annotate vendor/pear/console_getopt/Console/Getopt.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@18 1 <?php
Chris@18 2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
Chris@18 3 /**
Chris@18 4 * PHP Version 5
Chris@18 5 *
Chris@18 6 * Copyright (c) 2001-2015, The PEAR developers
Chris@18 7 *
Chris@18 8 * This source file is subject to the BSD-2-Clause license,
Chris@18 9 * that is bundled with this package in the file LICENSE, and is
Chris@18 10 * available through the world-wide-web at the following url:
Chris@18 11 * http://opensource.org/licenses/bsd-license.php.
Chris@18 12 *
Chris@18 13 * @category Console
Chris@18 14 * @package Console_Getopt
Chris@18 15 * @author Andrei Zmievski <andrei@php.net>
Chris@18 16 * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
Chris@18 17 * @version CVS: $Id$
Chris@18 18 * @link http://pear.php.net/package/Console_Getopt
Chris@18 19 */
Chris@18 20
Chris@18 21 require_once 'PEAR.php';
Chris@18 22
Chris@18 23 /**
Chris@18 24 * Command-line options parsing class.
Chris@18 25 *
Chris@18 26 * @category Console
Chris@18 27 * @package Console_Getopt
Chris@18 28 * @author Andrei Zmievski <andrei@php.net>
Chris@18 29 * @license http://opensource.org/licenses/bsd-license.php BSD-2-Clause
Chris@18 30 * @link http://pear.php.net/package/Console_Getopt
Chris@18 31 */
Chris@18 32 class Console_Getopt
Chris@18 33 {
Chris@18 34
Chris@18 35 /**
Chris@18 36 * Parses the command-line options.
Chris@18 37 *
Chris@18 38 * The first parameter to this function should be the list of command-line
Chris@18 39 * arguments without the leading reference to the running program.
Chris@18 40 *
Chris@18 41 * The second parameter is a string of allowed short options. Each of the
Chris@18 42 * option letters can be followed by a colon ':' to specify that the option
Chris@18 43 * requires an argument, or a double colon '::' to specify that the option
Chris@18 44 * takes an optional argument.
Chris@18 45 *
Chris@18 46 * The third argument is an optional array of allowed long options. The
Chris@18 47 * leading '--' should not be included in the option name. Options that
Chris@18 48 * require an argument should be followed by '=', and options that take an
Chris@18 49 * option argument should be followed by '=='.
Chris@18 50 *
Chris@18 51 * The return value is an array of two elements: the list of parsed
Chris@18 52 * options and the list of non-option command-line arguments. Each entry in
Chris@18 53 * the list of parsed options is a pair of elements - the first one
Chris@18 54 * specifies the option, and the second one specifies the option argument,
Chris@18 55 * if there was one.
Chris@18 56 *
Chris@18 57 * Long and short options can be mixed.
Chris@18 58 *
Chris@18 59 * Most of the semantics of this function are based on GNU getopt_long().
Chris@18 60 *
Chris@18 61 * @param array $args an array of command-line arguments
Chris@18 62 * @param string $short_options specifies the list of allowed short options
Chris@18 63 * @param array $long_options specifies the list of allowed long options
Chris@18 64 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
Chris@18 65 *
Chris@18 66 * @return array two-element array containing the list of parsed options and
Chris@18 67 * the non-option arguments
Chris@18 68 */
Chris@18 69 public static function getopt2($args, $short_options, $long_options = null, $skip_unknown = false)
Chris@18 70 {
Chris@18 71 return Console_Getopt::doGetopt(2, $args, $short_options, $long_options, $skip_unknown);
Chris@18 72 }
Chris@18 73
Chris@18 74 /**
Chris@18 75 * This function expects $args to start with the script name (POSIX-style).
Chris@18 76 * Preserved for backwards compatibility.
Chris@18 77 *
Chris@18 78 * @param array $args an array of command-line arguments
Chris@18 79 * @param string $short_options specifies the list of allowed short options
Chris@18 80 * @param array $long_options specifies the list of allowed long options
Chris@18 81 *
Chris@18 82 * @see getopt2()
Chris@18 83 * @return array two-element array containing the list of parsed options and
Chris@18 84 * the non-option arguments
Chris@18 85 */
Chris@18 86 public static function getopt($args, $short_options, $long_options = null, $skip_unknown = false)
Chris@18 87 {
Chris@18 88 return Console_Getopt::doGetopt(1, $args, $short_options, $long_options, $skip_unknown);
Chris@18 89 }
Chris@18 90
Chris@18 91 /**
Chris@18 92 * The actual implementation of the argument parsing code.
Chris@18 93 *
Chris@18 94 * @param int $version Version to use
Chris@18 95 * @param array $args an array of command-line arguments
Chris@18 96 * @param string $short_options specifies the list of allowed short options
Chris@18 97 * @param array $long_options specifies the list of allowed long options
Chris@18 98 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
Chris@18 99 *
Chris@18 100 * @return array
Chris@18 101 */
Chris@18 102 public static function doGetopt($version, $args, $short_options, $long_options = null, $skip_unknown = false)
Chris@18 103 {
Chris@18 104 // in case you pass directly readPHPArgv() as the first arg
Chris@18 105 if (PEAR::isError($args)) {
Chris@18 106 return $args;
Chris@18 107 }
Chris@18 108
Chris@18 109 if (empty($args)) {
Chris@18 110 return array(array(), array());
Chris@18 111 }
Chris@18 112
Chris@18 113 $non_opts = $opts = array();
Chris@18 114
Chris@18 115 settype($args, 'array');
Chris@18 116
Chris@18 117 if ($long_options) {
Chris@18 118 sort($long_options);
Chris@18 119 }
Chris@18 120
Chris@18 121 /*
Chris@18 122 * Preserve backwards compatibility with callers that relied on
Chris@18 123 * erroneous POSIX fix.
Chris@18 124 */
Chris@18 125 if ($version < 2) {
Chris@18 126 if (isset($args[0]{0}) && $args[0]{0} != '-') {
Chris@18 127 array_shift($args);
Chris@18 128 }
Chris@18 129 }
Chris@18 130
Chris@18 131 for ($i = 0; $i < count($args); $i++) {
Chris@18 132 $arg = $args[$i];
Chris@18 133 /* The special element '--' means explicit end of
Chris@18 134 options. Treat the rest of the arguments as non-options
Chris@18 135 and end the loop. */
Chris@18 136 if ($arg == '--') {
Chris@18 137 $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
Chris@18 138 break;
Chris@18 139 }
Chris@18 140
Chris@18 141 if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
Chris@18 142 $non_opts = array_merge($non_opts, array_slice($args, $i));
Chris@18 143 break;
Chris@18 144 } elseif (strlen($arg) > 1 && $arg{1} == '-') {
Chris@18 145 $error = Console_Getopt::_parseLongOption(substr($arg, 2),
Chris@18 146 $long_options,
Chris@18 147 $opts,
Chris@18 148 $i,
Chris@18 149 $args,
Chris@18 150 $skip_unknown);
Chris@18 151 if (PEAR::isError($error)) {
Chris@18 152 return $error;
Chris@18 153 }
Chris@18 154 } elseif ($arg == '-') {
Chris@18 155 // - is stdin
Chris@18 156 $non_opts = array_merge($non_opts, array_slice($args, $i));
Chris@18 157 break;
Chris@18 158 } else {
Chris@18 159 $error = Console_Getopt::_parseShortOption(substr($arg, 1),
Chris@18 160 $short_options,
Chris@18 161 $opts,
Chris@18 162 $i,
Chris@18 163 $args,
Chris@18 164 $skip_unknown);
Chris@18 165 if (PEAR::isError($error)) {
Chris@18 166 return $error;
Chris@18 167 }
Chris@18 168 }
Chris@18 169 }
Chris@18 170
Chris@18 171 return array($opts, $non_opts);
Chris@18 172 }
Chris@18 173
Chris@18 174 /**
Chris@18 175 * Parse short option
Chris@18 176 *
Chris@18 177 * @param string $arg Argument
Chris@18 178 * @param string[] $short_options Available short options
Chris@18 179 * @param string[][] &$opts
Chris@18 180 * @param int &$argIdx
Chris@18 181 * @param string[] $args
Chris@18 182 * @param boolean $skip_unknown suppresses Console_Getopt: unrecognized option
Chris@18 183 *
Chris@18 184 * @return void
Chris@18 185 */
Chris@18 186 protected static function _parseShortOption($arg, $short_options, &$opts, &$argIdx, $args, $skip_unknown)
Chris@18 187 {
Chris@18 188 for ($i = 0; $i < strlen($arg); $i++) {
Chris@18 189 $opt = $arg{$i};
Chris@18 190 $opt_arg = null;
Chris@18 191
Chris@18 192 /* Try to find the short option in the specifier string. */
Chris@18 193 if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') {
Chris@18 194 if ($skip_unknown === true) {
Chris@18 195 break;
Chris@18 196 }
Chris@18 197
Chris@18 198 $msg = "Console_Getopt: unrecognized option -- $opt";
Chris@18 199 return PEAR::raiseError($msg);
Chris@18 200 }
Chris@18 201
Chris@18 202 if (strlen($spec) > 1 && $spec{1} == ':') {
Chris@18 203 if (strlen($spec) > 2 && $spec{2} == ':') {
Chris@18 204 if ($i + 1 < strlen($arg)) {
Chris@18 205 /* Option takes an optional argument. Use the remainder of
Chris@18 206 the arg string if there is anything left. */
Chris@18 207 $opts[] = array($opt, substr($arg, $i + 1));
Chris@18 208 break;
Chris@18 209 }
Chris@18 210 } else {
Chris@18 211 /* Option requires an argument. Use the remainder of the arg
Chris@18 212 string if there is anything left. */
Chris@18 213 if ($i + 1 < strlen($arg)) {
Chris@18 214 $opts[] = array($opt, substr($arg, $i + 1));
Chris@18 215 break;
Chris@18 216 } else if (isset($args[++$argIdx])) {
Chris@18 217 $opt_arg = $args[$argIdx];
Chris@18 218 /* Else use the next argument. */;
Chris@18 219 if (Console_Getopt::_isShortOpt($opt_arg)
Chris@18 220 || Console_Getopt::_isLongOpt($opt_arg)) {
Chris@18 221 $msg = "option requires an argument --$opt";
Chris@18 222 return PEAR::raiseError("Console_Getopt: " . $msg);
Chris@18 223 }
Chris@18 224 } else {
Chris@18 225 $msg = "option requires an argument --$opt";
Chris@18 226 return PEAR::raiseError("Console_Getopt: " . $msg);
Chris@18 227 }
Chris@18 228 }
Chris@18 229 }
Chris@18 230
Chris@18 231 $opts[] = array($opt, $opt_arg);
Chris@18 232 }
Chris@18 233 }
Chris@18 234
Chris@18 235 /**
Chris@18 236 * Checks if an argument is a short option
Chris@18 237 *
Chris@18 238 * @param string $arg Argument to check
Chris@18 239 *
Chris@18 240 * @return bool
Chris@18 241 */
Chris@18 242 protected static function _isShortOpt($arg)
Chris@18 243 {
Chris@18 244 return strlen($arg) == 2 && $arg[0] == '-'
Chris@18 245 && preg_match('/[a-zA-Z]/', $arg[1]);
Chris@18 246 }
Chris@18 247
Chris@18 248 /**
Chris@18 249 * Checks if an argument is a long option
Chris@18 250 *
Chris@18 251 * @param string $arg Argument to check
Chris@18 252 *
Chris@18 253 * @return bool
Chris@18 254 */
Chris@18 255 protected static function _isLongOpt($arg)
Chris@18 256 {
Chris@18 257 return strlen($arg) > 2 && $arg[0] == '-' && $arg[1] == '-' &&
Chris@18 258 preg_match('/[a-zA-Z]+$/', substr($arg, 2));
Chris@18 259 }
Chris@18 260
Chris@18 261 /**
Chris@18 262 * Parse long option
Chris@18 263 *
Chris@18 264 * @param string $arg Argument
Chris@18 265 * @param string[] $long_options Available long options
Chris@18 266 * @param string[][] &$opts
Chris@18 267 * @param int &$argIdx
Chris@18 268 * @param string[] $args
Chris@18 269 *
Chris@18 270 * @return void|PEAR_Error
Chris@18 271 */
Chris@18 272 protected static function _parseLongOption($arg, $long_options, &$opts, &$argIdx, $args, $skip_unknown)
Chris@18 273 {
Chris@18 274 @list($opt, $opt_arg) = explode('=', $arg, 2);
Chris@18 275
Chris@18 276 $opt_len = strlen($opt);
Chris@18 277
Chris@18 278 for ($i = 0; $i < count($long_options); $i++) {
Chris@18 279 $long_opt = $long_options[$i];
Chris@18 280 $opt_start = substr($long_opt, 0, $opt_len);
Chris@18 281
Chris@18 282 $long_opt_name = str_replace('=', '', $long_opt);
Chris@18 283
Chris@18 284 /* Option doesn't match. Go on to the next one. */
Chris@18 285 if ($long_opt_name != $opt) {
Chris@18 286 continue;
Chris@18 287 }
Chris@18 288
Chris@18 289 $opt_rest = substr($long_opt, $opt_len);
Chris@18 290
Chris@18 291 /* Check that the options uniquely matches one of the allowed
Chris@18 292 options. */
Chris@18 293 if ($i + 1 < count($long_options)) {
Chris@18 294 $next_option_rest = substr($long_options[$i + 1], $opt_len);
Chris@18 295 } else {
Chris@18 296 $next_option_rest = '';
Chris@18 297 }
Chris@18 298
Chris@18 299 if ($opt_rest != '' && $opt{0} != '=' &&
Chris@18 300 $i + 1 < count($long_options) &&
Chris@18 301 $opt == substr($long_options[$i+1], 0, $opt_len) &&
Chris@18 302 $next_option_rest != '' &&
Chris@18 303 $next_option_rest{0} != '=') {
Chris@18 304
Chris@18 305 $msg = "Console_Getopt: option --$opt is ambiguous";
Chris@18 306 return PEAR::raiseError($msg);
Chris@18 307 }
Chris@18 308
Chris@18 309 if (substr($long_opt, -1) == '=') {
Chris@18 310 if (substr($long_opt, -2) != '==') {
Chris@18 311 /* Long option requires an argument.
Chris@18 312 Take the next argument if one wasn't specified. */;
Chris@18 313 if (!strlen($opt_arg)) {
Chris@18 314 if (!isset($args[++$argIdx])) {
Chris@18 315 $msg = "Console_Getopt: option requires an argument --$opt";
Chris@18 316 return PEAR::raiseError($msg);
Chris@18 317 }
Chris@18 318 $opt_arg = $args[$argIdx];
Chris@18 319 }
Chris@18 320
Chris@18 321 if (Console_Getopt::_isShortOpt($opt_arg)
Chris@18 322 || Console_Getopt::_isLongOpt($opt_arg)) {
Chris@18 323 $msg = "Console_Getopt: option requires an argument --$opt";
Chris@18 324 return PEAR::raiseError($msg);
Chris@18 325 }
Chris@18 326 }
Chris@18 327 } else if ($opt_arg) {
Chris@18 328 $msg = "Console_Getopt: option --$opt doesn't allow an argument";
Chris@18 329 return PEAR::raiseError($msg);
Chris@18 330 }
Chris@18 331
Chris@18 332 $opts[] = array('--' . $opt, $opt_arg);
Chris@18 333 return;
Chris@18 334 }
Chris@18 335
Chris@18 336 if ($skip_unknown === true) {
Chris@18 337 return;
Chris@18 338 }
Chris@18 339
Chris@18 340 return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
Chris@18 341 }
Chris@18 342
Chris@18 343 /**
Chris@18 344 * Safely read the $argv PHP array across different PHP configurations.
Chris@18 345 * Will take care on register_globals and register_argc_argv ini directives
Chris@18 346 *
Chris@18 347 * @return mixed the $argv PHP array or PEAR error if not registered
Chris@18 348 */
Chris@18 349 public static function readPHPArgv()
Chris@18 350 {
Chris@18 351 global $argv;
Chris@18 352 if (!is_array($argv)) {
Chris@18 353 if (!@is_array($_SERVER['argv'])) {
Chris@18 354 if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
Chris@18 355 $msg = "Could not read cmd args (register_argc_argv=Off?)";
Chris@18 356 return PEAR::raiseError("Console_Getopt: " . $msg);
Chris@18 357 }
Chris@18 358 return $GLOBALS['HTTP_SERVER_VARS']['argv'];
Chris@18 359 }
Chris@18 360 return $_SERVER['argv'];
Chris@18 361 }
Chris@18 362 return $argv;
Chris@18 363 }
Chris@18 364
Chris@18 365 }