Creating repository for dokuwiki modifications for sudaraka.org
[sudaraka-org:dokuwiki-mods.git] / inc / cliopts.php
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 license@php.net so we can mail you a copy immediately.
14  *
15  * @category Console
16  * @package  Console_Getopt
17  * @author   Andrei Zmievski <andrei@php.net>
18  * @modified Harry Fuecks hfuecks  gmail.com
19  * @modified Tanguy Ortolo <tanguy+dokuwiki@ortolo.eu>
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 <andrei@php.net>
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 }