1 | <?php
|
---|
2 | /**
|
---|
3 | * Brutally chopped and modified from http://pear.php.net/package/Console_Getopts
|
---|
4 | *
|
---|
5 | * PHP Version 5
|
---|
6 | *
|
---|
7 | * Copyright (c) 1997-2004 The PHP Group
|
---|
8 | *
|
---|
9 | * LICENSE: This source file is subject to the New BSD license that is
|
---|
10 | * available through the world-wide-web at the following URI:
|
---|
11 | * http://www.opensource.org/licenses/bsd-license.php. If you did not receive
|
---|
12 | * a copy of the New BSD License and are unable to obtain it through the web,
|
---|
13 | * please send a note to [email protected] so we can mail you a copy immediately.
|
---|
14 | *
|
---|
15 | * @category Console
|
---|
16 | * @package Console_Getopt
|
---|
17 | * @author Andrei Zmievski <[email protected]>
|
---|
18 | * @modified Harry Fuecks hfuecks gmail.com
|
---|
19 | * @modified Tanguy Ortolo <[email protected]>
|
---|
20 | * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
|
---|
21 | * @version CVS: $Id$
|
---|
22 | * @link http://pear.php.net/package/Console_Getopt
|
---|
23 | *
|
---|
24 | */
|
---|
25 |
|
---|
26 | //------------------------------------------------------------------------------
|
---|
27 | /**
|
---|
28 | * Sets up CLI environment based on SAPI and PHP version
|
---|
29 | * Helps resolve some issues between the CGI and CLI SAPIs
|
---|
30 | * as well is inconsistencies between PHP 4.3+ and older versions
|
---|
31 | */
|
---|
32 | if (version_compare(phpversion(), '4.3.0', '<') || php_sapi_name() == 'cgi') {
|
---|
33 | // Handle output buffering
|
---|
34 | @ob_end_flush();
|
---|
35 | ob_implicit_flush(true);
|
---|
36 |
|
---|
37 | // PHP ini settings
|
---|
38 | set_time_limit(0);
|
---|
39 | ini_set('track_errors', true);
|
---|
40 | ini_set('html_errors', false);
|
---|
41 | ini_set('magic_quotes_runtime', false);
|
---|
42 |
|
---|
43 | // Define stream constants
|
---|
44 | define('STDIN', fopen('php://stdin', 'r'));
|
---|
45 | define('STDOUT', fopen('php://stdout', 'w'));
|
---|
46 | define('STDERR', fopen('php://stderr', 'w'));
|
---|
47 |
|
---|
48 | // Close the streams on script termination
|
---|
49 | register_shutdown_function(
|
---|
50 | create_function('',
|
---|
51 | 'fclose(STDIN); fclose(STDOUT); fclose(STDERR); return true;')
|
---|
52 | );
|
---|
53 | }
|
---|
54 |
|
---|
55 | //------------------------------------------------------------------------------
|
---|
56 | /**
|
---|
57 | * Error codes
|
---|
58 | */
|
---|
59 | define('DOKU_CLI_OPTS_UNKNOWN_OPT',1); //Unrecognized option
|
---|
60 | define('DOKU_CLI_OPTS_OPT_ARG_REQUIRED',2); //Option requires argument
|
---|
61 | define('DOKU_CLI_OPTS_OPT_ARG_DENIED',3); //Option not allowed argument
|
---|
62 | define('DOKU_CLI_OPTS_OPT_ABIGUOUS',4);//Option abiguous
|
---|
63 | define('DOKU_CLI_OPTS_ARG_READ',5);//Could not read argv
|
---|
64 |
|
---|
65 | //------------------------------------------------------------------------------
|
---|
66 | /**
|
---|
67 | * Command-line options parsing class.
|
---|
68 | *
|
---|
69 | * @author Andrei Zmievski <[email protected]>
|
---|
70 | *
|
---|
71 | */
|
---|
72 | class Doku_Cli_Opts {
|
---|
73 |
|
---|
74 | /**
|
---|
75 | * <?php ?>
|
---|
76 | * @see http://www.sitepoint.com/article/php-command-line-1/3
|
---|
77 | * @param string executing file name - this MUST be passed the __FILE__ constant
|
---|
78 | * @param string short options
|
---|
79 | * @param array (optional) long options
|
---|
80 | * @return Doku_Cli_Opts_Container or Doku_Cli_Opts_Error
|
---|
81 | */
|
---|
82 | function & getOptions($bin_file, $short_options, $long_options = null) {
|
---|
83 | $args = Doku_Cli_Opts::readPHPArgv();
|
---|
84 |
|
---|
85 | if ( Doku_Cli_Opts::isError($args) ) {
|
---|
86 | return $args;
|
---|
87 | }
|
---|
88 |
|
---|
89 | // Compatibility between "php extensions.php" and "./extensions.php"
|
---|
90 | if ( realpath($_SERVER['argv'][0]) == $bin_file ) {
|
---|
91 | $options = Doku_Cli_Opts::getOpt($args,$short_options,$long_options);
|
---|
92 | } else {
|
---|
93 | $options = Doku_Cli_Opts::getOpt2($args,$short_options,$long_options);
|
---|
94 | }
|
---|
95 |
|
---|
96 | if ( Doku_Cli_Opts::isError($options) ) {
|
---|
97 | return $options;
|
---|
98 | }
|
---|
99 |
|
---|
100 | $container = new Doku_Cli_Opts_Container($options);
|
---|
101 | return $container;
|
---|
102 | }
|
---|
103 |
|
---|
104 | /**
|
---|
105 | * Parses the command-line options.
|
---|
106 | *
|
---|
107 | * The first parameter to this function should be the list of command-line
|
---|
108 | * arguments without the leading reference to the running program.
|
---|
109 | *
|
---|
110 | * The second parameter is a string of allowed short options. Each of the
|
---|
111 | * option letters can be followed by a colon ':' to specify that the option
|
---|
112 | * requires an argument, or a double colon '::' to specify that the option
|
---|
113 | * takes an optional argument.
|
---|
114 | *
|
---|
115 | * The third argument is an optional array of allowed long options. The
|
---|
116 | * leading '--' should not be included in the option name. Options that
|
---|
117 | * require an argument should be followed by '=', and options that take an
|
---|
118 | * option argument should be followed by '=='.
|
---|
119 | *
|
---|
120 | * The return value is an array of two elements: the list of parsed
|
---|
121 | * options and the list of non-option command-line arguments. Each entry in
|
---|
122 | * the list of parsed options is a pair of elements - the first one
|
---|
123 | * specifies the option, and the second one specifies the option argument,
|
---|
124 | * if there was one.
|
---|
125 | *
|
---|
126 | * Long and short options can be mixed.
|
---|
127 | *
|
---|
128 | * Most of the semantics of this function are based on GNU getopt_long().
|
---|
129 | *
|
---|
130 | * @param array $args an array of command-line arguments
|
---|
131 | * @param string $short_options specifies the list of allowed short options
|
---|
132 | * @param array $long_options specifies the list of allowed long options
|
---|
133 | *
|
---|
134 | * @return array two-element array containing the list of parsed options and
|
---|
135 | * the non-option arguments
|
---|
136 | * @access public
|
---|
137 | */
|
---|
138 | function getopt2($args, $short_options, $long_options = null) {
|
---|
139 | return Doku_Cli_Opts::doGetopt(
|
---|
140 | 2, $args, $short_options, $long_options
|
---|
141 | );
|
---|
142 | }
|
---|
143 |
|
---|
144 | /**
|
---|
145 | * This function expects $args to start with the script name (POSIX-style).
|
---|
146 | * Preserved for backwards compatibility.
|
---|
147 | *
|
---|
148 | * @param array $args an array of command-line arguments
|
---|
149 | * @param string $short_options specifies the list of allowed short options
|
---|
150 | * @param array $long_options specifies the list of allowed long options
|
---|
151 | *
|
---|
152 | * @see getopt2()
|
---|
153 | * @return array two-element array containing the list of parsed options and
|
---|
154 | * the non-option arguments
|
---|
155 | */
|
---|
156 | function getopt($args, $short_options, $long_options = null) {
|
---|
157 | return Doku_Cli_Opts::doGetopt(
|
---|
158 | 1, $args, $short_options, $long_options
|
---|
159 | );
|
---|
160 | }
|
---|
161 |
|
---|
162 | /**
|
---|
163 | * The actual implementation of the argument parsing code.
|
---|
164 | *
|
---|
165 | * @param int $version Version to use
|
---|
166 | * @param array $args an array of command-line arguments
|
---|
167 | * @param string $short_options specifies the list of allowed short options
|
---|
168 | * @param array $long_options specifies the list of allowed long options
|
---|
169 | *
|
---|
170 | * @return array
|
---|
171 | */
|
---|
172 | function doGetopt($version, $args, $short_options, $long_options = null) {
|
---|
173 |
|
---|
174 | // in case you pass directly readPHPArgv() as the first arg
|
---|
175 | if (Doku_Cli_Opts::isError($args)) {
|
---|
176 | return $args;
|
---|
177 | }
|
---|
178 | if (empty($args)) {
|
---|
179 | return array(array(), array());
|
---|
180 | }
|
---|
181 | $opts = array();
|
---|
182 | $non_opts = array();
|
---|
183 |
|
---|
184 | settype($args, 'array');
|
---|
185 |
|
---|
186 | if ($long_options && is_array($long_options)) {
|
---|
187 | sort($long_options);
|
---|
188 | }
|
---|
189 |
|
---|
190 | /*
|
---|
191 | * Preserve backwards compatibility with callers that relied on
|
---|
192 | * erroneous POSIX fix.
|
---|
193 | */
|
---|
194 | if ($version < 2) {
|
---|
195 | if (isset($args[0]{0}) && $args[0]{0} != '-') {
|
---|
196 | array_shift($args);
|
---|
197 | }
|
---|
198 | }
|
---|
199 |
|
---|
200 | reset($args);
|
---|
201 | while (list($i, $arg) = each($args)) {
|
---|
202 |
|
---|
203 | /* The special element '--' means explicit end of
|
---|
204 | options. Treat the rest of the arguments as non-options
|
---|
205 | and end the loop. */
|
---|
206 | if ($arg == '--') {
|
---|
207 | $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
|
---|
208 | break;
|
---|
209 | }
|
---|
210 |
|
---|
211 | if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
|
---|
212 | $non_opts = array_merge($non_opts, array_slice($args, $i));
|
---|
213 | break;
|
---|
214 | } elseif (strlen($arg) > 1 && $arg{1} == '-') {
|
---|
215 | $error = Doku_Cli_Opts::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
|
---|
216 | if (Doku_Cli_Opts::isError($error))
|
---|
217 | return $error;
|
---|
218 | } elseif ($arg == '-') {
|
---|
219 | // - is stdin
|
---|
220 | $non_opts = array_merge($non_opts, array_slice($args, $i));
|
---|
221 | break;
|
---|
222 | } else {
|
---|
223 | $error = Doku_Cli_Opts::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
|
---|
224 | if (Doku_Cli_Opts::isError($error))
|
---|
225 | return $error;
|
---|
226 | }
|
---|
227 | }
|
---|
228 |
|
---|
229 | return array($opts, $non_opts);
|
---|
230 | }
|
---|
231 |
|
---|
232 | /**
|
---|
233 | * Parse short option
|
---|
234 | *
|
---|
235 | * @param string $arg Argument
|
---|
236 | * @param string[] $short_options Available short options
|
---|
237 | * @param string[][] &$opts
|
---|
238 | * @param string[] &$args
|
---|
239 | *
|
---|
240 | * @access private
|
---|
241 | * @return void
|
---|
242 | */
|
---|
243 | function _parseShortOption($arg, $short_options, &$opts, &$args) {
|
---|
244 | $len = strlen($arg);
|
---|
245 | for ($i = 0; $i < $len; $i++) {
|
---|
246 | $opt = $arg{$i};
|
---|
247 | $opt_arg = null;
|
---|
248 |
|
---|
249 | /* Try to find the short option in the specifier string. */
|
---|
250 | if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
|
---|
251 | {
|
---|
252 | return Doku_Cli_Opts::raiseError(
|
---|
253 | DOKU_CLI_OPTS_UNKNOWN_OPT,
|
---|
254 | "Unrecognized option -- $opt"
|
---|
255 | );
|
---|
256 | }
|
---|
257 |
|
---|
258 | if (strlen($spec) > 1 && $spec{1} == ':') {
|
---|
259 | if (strlen($spec) > 2 && $spec{2} == ':') {
|
---|
260 | if ($i + 1 < strlen($arg)) {
|
---|
261 | /* Option takes an optional argument. Use the remainder of
|
---|
262 | the arg string if there is anything left. */
|
---|
263 | $opts[] = array($opt, substr($arg, $i + 1));
|
---|
264 | break;
|
---|
265 | }
|
---|
266 | } else {
|
---|
267 | /* Option requires an argument. Use the remainder of the arg
|
---|
268 | string if there is anything left. */
|
---|
269 | if ($i + 1 < strlen($arg)) {
|
---|
270 | $opts[] = array($opt, substr($arg, $i + 1));
|
---|
271 | break;
|
---|
272 | } else if (list(, $opt_arg) = each($args)) {
|
---|
273 | /* Else use the next argument. */;
|
---|
274 | if (Doku_Cli_Opts::_isShortOpt($opt_arg) || Doku_Cli_Opts::_isLongOpt($opt_arg))
|
---|
275 | return Doku_Cli_Opts::raiseError(
|
---|
276 | DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
|
---|
277 | "option requires an argument --$opt"
|
---|
278 | );
|
---|
279 | }
|
---|
280 | else
|
---|
281 | return Doku_Cli_Opts::raiseError(
|
---|
282 | DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
|
---|
283 | "Option requires an argument -- $opt"
|
---|
284 | );
|
---|
285 | }
|
---|
286 | }
|
---|
287 |
|
---|
288 | $opts[] = array($opt, $opt_arg);
|
---|
289 | }
|
---|
290 | }
|
---|
291 |
|
---|
292 | /**
|
---|
293 | * Checks if an argument is a short option
|
---|
294 | *
|
---|
295 | * @param string $arg Argument to check
|
---|
296 | *
|
---|
297 | * @access private
|
---|
298 | * @return bool
|
---|
299 | */
|
---|
300 | function _isShortOpt($arg)
|
---|
301 | {
|
---|
302 | return strlen($arg) == 2 && $arg[0] == '-'
|
---|
303 | && preg_match('/[a-zA-Z]/', $arg[1]);
|
---|
304 | }
|
---|
305 |
|
---|
306 | /**
|
---|
307 | * Checks if an argument is a long option
|
---|
308 | *
|
---|
309 | * @param string $arg Argument to check
|
---|
310 | *
|
---|
311 | * @access private
|
---|
312 | * @return bool
|
---|
313 | */
|
---|
314 | function _isLongOpt($arg)
|
---|
315 | {
|
---|
316 | return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
|
---|
317 | preg_match('/[a-zA-Z]+$/', substr($arg, 2));
|
---|
318 | }
|
---|
319 |
|
---|
320 | /**
|
---|
321 | * Parse long option
|
---|
322 | *
|
---|
323 | * @param string $arg Argument
|
---|
324 | * @param string[] $long_options Available long options
|
---|
325 | * @param string[][] &$opts
|
---|
326 | * @param string[] &$args
|
---|
327 | *
|
---|
328 | * @access private
|
---|
329 | * @return void|PEAR_Error
|
---|
330 | */
|
---|
331 | function _parseLongOption($arg, $long_options, &$opts, &$args) {
|
---|
332 | @list($opt, $opt_arg) = explode('=', $arg, 2);
|
---|
333 | $opt_len = strlen($opt);
|
---|
334 | $opt_cnt = count($long_options);
|
---|
335 |
|
---|
336 | for ($i = 0; $i < $opt_cnt; $i++) {
|
---|
337 | $long_opt = $long_options[$i];
|
---|
338 | $opt_start = substr($long_opt, 0, $opt_len);
|
---|
339 |
|
---|
340 | $long_opt_name = str_replace('=', '', $long_opt);
|
---|
341 |
|
---|
342 | /* Option doesn't match. Go on to the next one. */
|
---|
343 | if ($opt_start != $opt)
|
---|
344 | continue;
|
---|
345 |
|
---|
346 | $opt_rest = substr($long_opt, $opt_len);
|
---|
347 |
|
---|
348 | /* Check that the options uniquely matches one of the allowed
|
---|
349 | options. */
|
---|
350 | if ($i + 1 < count($long_options)) {
|
---|
351 | $next_option_rest = substr($long_options[$i + 1], $opt_len);
|
---|
352 | } else {
|
---|
353 | $next_option_rest = '';
|
---|
354 | }
|
---|
355 |
|
---|
356 | if ($opt_rest != '' && $opt{0} != '=' &&
|
---|
357 | $i + 1 < $opt_cnt &&
|
---|
358 | $opt == substr($long_options[$i+1], 0, $opt_len) &&
|
---|
359 | $next_option_rest != '' &&
|
---|
360 | $next_option_rest{0} != '=') {
|
---|
361 | return Doku_Cli_Opts::raiseError(
|
---|
362 | DOKU_CLI_OPTS_OPT_ABIGUOUS,
|
---|
363 | "Option --$opt is ambiguous"
|
---|
364 | );
|
---|
365 | }
|
---|
366 |
|
---|
367 | if (substr($long_opt, -1) == '=') {
|
---|
368 | if (substr($long_opt, -2) != '==') {
|
---|
369 | /* Long option requires an argument.
|
---|
370 | Take the next argument if one wasn't specified. */;
|
---|
371 | if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
|
---|
372 | return Doku_Cli_Opts::raiseError(
|
---|
373 | DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
|
---|
374 | "Option --$opt requires an argument"
|
---|
375 | );
|
---|
376 | }
|
---|
377 |
|
---|
378 | if (Doku_Cli_Opts::_isShortOpt($opt_arg)
|
---|
379 | || Doku_Cli_Opts::_isLongOpt($opt_arg))
|
---|
380 | return Doku_Cli_Opts::raiseError(
|
---|
381 | DOKU_CLI_OPTS_OPT_ARG_REQUIRED,
|
---|
382 | "Option --$opt requires an argument"
|
---|
383 | );
|
---|
384 | }
|
---|
385 | } else if ($opt_arg) {
|
---|
386 | return Doku_Cli_Opts::raiseError(
|
---|
387 | DOKU_CLI_OPTS_OPT_ARG_DENIED,
|
---|
388 | "Option --$opt doesn't allow an argument"
|
---|
389 | );
|
---|
390 | }
|
---|
391 |
|
---|
392 | $opts[] = array('--' . $opt, $opt_arg);
|
---|
393 | return;
|
---|
394 | }
|
---|
395 |
|
---|
396 | return Doku_Cli_Opts::raiseError(
|
---|
397 | DOKU_CLI_OPTS_UNKNOWN_OPT,
|
---|
398 | "Unrecognized option --$opt"
|
---|
399 | );
|
---|
400 | }
|
---|
401 |
|
---|
402 | /**
|
---|
403 | * Safely read the $argv PHP array across different PHP configurations.
|
---|
404 | * Will take care on register_globals and register_argc_argv ini directives
|
---|
405 | *
|
---|
406 | * @access public
|
---|
407 | * @return mixed the $argv PHP array or PEAR error if not registered
|
---|
408 | */
|
---|
409 | function readPHPArgv() {
|
---|
410 | global $argv;
|
---|
411 | if (!is_array($argv)) {
|
---|
412 | if (!@is_array($_SERVER['argv'])) {
|
---|
413 | if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
|
---|
414 | return Doku_Cli_Opts::raiseError(
|
---|
415 | DOKU_CLI_OPTS_ARG_READ,
|
---|
416 | "Could not read cmd args (register_argc_argv=Off?)"
|
---|
417 | );
|
---|
418 | }
|
---|
419 | return $GLOBALS['HTTP_SERVER_VARS']['argv'];
|
---|
420 | }
|
---|
421 | return $_SERVER['argv'];
|
---|
422 | }
|
---|
423 | return $argv;
|
---|
424 | }
|
---|
425 |
|
---|
426 | function raiseError($code, $msg) {
|
---|
427 | return new Doku_Cli_Opts_Error($code, $msg);
|
---|
428 | }
|
---|
429 |
|
---|
430 | function isError($obj) {
|
---|
431 | return is_a($obj, 'Doku_Cli_Opts_Error');
|
---|
432 | }
|
---|
433 |
|
---|
434 | }
|
---|
435 |
|
---|
436 | //------------------------------------------------------------------------------
|
---|
437 | class Doku_Cli_Opts_Error {
|
---|
438 |
|
---|
439 | var $code;
|
---|
440 | var $msg;
|
---|
441 |
|
---|
442 | function Doku_Cli_Opts_Error($code, $msg) {
|
---|
443 | $this->code = $code;
|
---|
444 | $this->msg = $msg;
|
---|
445 | }
|
---|
446 |
|
---|
447 | function getMessage() {
|
---|
448 | return $this->msg;
|
---|
449 | }
|
---|
450 |
|
---|
451 | function isError() {
|
---|
452 | return true;
|
---|
453 | }
|
---|
454 |
|
---|
455 | }
|
---|
456 |
|
---|
457 | //------------------------------------------------------------------------------
|
---|
458 | class Doku_Cli_Opts_Container {
|
---|
459 |
|
---|
460 | var $options = array();
|
---|
461 | var $args = array();
|
---|
462 |
|
---|
463 | function Doku_Cli_Opts_Container($options) {
|
---|
464 | foreach ( $options[0] as $option ) {
|
---|
465 | if ( false !== ( strpos($option[0], '--') ) ) {
|
---|
466 | $opt_name = substr($option[0], 2);
|
---|
467 | } else {
|
---|
468 | $opt_name = $option[0];
|
---|
469 | }
|
---|
470 | $this->options[$opt_name] = $option[1];
|
---|
471 | }
|
---|
472 |
|
---|
473 | $this->args = $options[1];
|
---|
474 | }
|
---|
475 |
|
---|
476 | function has($option) {
|
---|
477 | return array_key_exists($option, $this->options);
|
---|
478 | }
|
---|
479 |
|
---|
480 | function get($option) {
|
---|
481 | if ( isset($this->options[$option]) ) {
|
---|
482 | return ( $this->options[$option] ) ;
|
---|
483 | }
|
---|
484 | }
|
---|
485 |
|
---|
486 | function arg($index) {
|
---|
487 | if ( isset($this->args[$index]) ) {
|
---|
488 | return $this->args[$index];
|
---|
489 | }
|
---|
490 | }
|
---|
491 |
|
---|
492 | function numArgs() {
|
---|
493 | return count($this->args);
|
---|
494 | }
|
---|
495 |
|
---|
496 | function hasArgs() {
|
---|
497 | return count($this->args) !== 0;
|
---|
498 | }
|
---|
499 |
|
---|
500 | function isError() {
|
---|
501 | return false;
|
---|
502 | }
|
---|
503 |
|
---|
504 | }
|
---|