/**
  * Parse and consume a list of arguments, removing them from the argument
  * vector but leaving unparsed arguments for later consumption. You can
  * retrieve unconsumed arguments directly with
  * @{method:getUnconsumedArgumentVector}. Doing a partial parse can make it
  * easier to share common flags across scripts or workflows.
  *
  * @param   list  List of argument specs, see
  *                @{class:PhutilArgumentSpecification}.
  * @return  this
  * @task parse
  */
 public function parsePartial(array $specs)
 {
     $specs = PhutilArgumentSpecification::newSpecsFromList($specs);
     $this->mergeSpecs($specs);
     $specs_by_name = mpull($specs, null, 'getName');
     $specs_by_short = mpull($specs, null, 'getShortAlias');
     unset($specs_by_short[null]);
     $argv = $this->argv;
     $len = count($argv);
     for ($ii = 0; $ii < $len; $ii++) {
         $arg = $argv[$ii];
         $map = null;
         if (!is_string($arg)) {
             // Non-string argument; pass it through as-is.
         } else {
             if ($arg == '--') {
                 // This indicates "end of flags".
                 break;
             } else {
                 if ($arg == '-') {
                     // This is a normal argument (e.g., stdin).
                     continue;
                 } else {
                     if (!strncmp('--', $arg, 2)) {
                         $pre = '--';
                         $arg = substr($arg, 2);
                         $map = $specs_by_name;
                     } else {
                         if (!strncmp('-', $arg, 1) && strlen($arg) > 1) {
                             $pre = '-';
                             $arg = substr($arg, 1);
                             $map = $specs_by_short;
                         }
                     }
                 }
             }
         }
         if ($map) {
             $val = null;
             $parts = explode('=', $arg, 2);
             if (count($parts) == 2) {
                 list($arg, $val) = $parts;
             }
             if (isset($map[$arg])) {
                 $spec = $map[$arg];
                 unset($argv[$ii]);
                 $param_name = $spec->getParamName();
                 if ($val !== null) {
                     if ($param_name === null) {
                         throw new PhutilArgumentUsageException(pht("Argument '%s' does not take a parameter.", "{$pre}{$arg}"));
                     }
                 } else {
                     if ($param_name !== null) {
                         if ($ii + 1 < $len) {
                             $val = $argv[$ii + 1];
                             unset($argv[$ii + 1]);
                             $ii++;
                         } else {
                             throw new PhutilArgumentUsageException(pht("Argument '%s' requires a parameter.", "{$pre}{$arg}"));
                         }
                     } else {
                         $val = true;
                     }
                 }
                 if (!$spec->getRepeatable()) {
                     if (array_key_exists($spec->getName(), $this->results)) {
                         throw new PhutilArgumentUsageException(pht("Argument '%s' was provided twice.", "{$pre}{$arg}"));
                     }
                 }
                 $conflicts = $spec->getConflicts();
                 foreach ($conflicts as $conflict => $reason) {
                     if (array_key_exists($conflict, $this->results)) {
                         if (!is_string($reason) || !strlen($reason)) {
                             $reason = '.';
                         } else {
                             $reason = ': ' . $reason . '.';
                         }
                         throw new PhutilArgumentUsageException(pht("Argument '%s' conflicts with argument '%s'%s", "{$pre}{$arg}", "--{$conflict}", $reason));
                     }
                 }
                 if ($spec->getRepeatable()) {
                     if ($spec->getParamName() === null) {
                         if (empty($this->results[$spec->getName()])) {
                             $this->results[$spec->getName()] = 0;
                         }
                         $this->results[$spec->getName()]++;
                     } else {
                         $this->results[$spec->getName()][] = $val;
                     }
                 } else {
                     $this->results[$spec->getName()] = $val;
                 }
             }
         }
     }
     foreach ($specs as $spec) {
         if ($spec->getWildcard()) {
             $this->results[$spec->getName()] = $this->filterWildcardArgv($argv);
             $argv = array();
             break;
         }
     }
     $this->argv = array_values($argv);
     return $this;
 }
 public final function setArguments(array $specs)
 {
     $specs = PhutilArgumentSpecification::newSpecsFromList($specs);
     $this->specs = $specs;
     return $this;
 }
 private function parseInternal(array $specs, $correct_spelling)
 {
     $specs = PhutilArgumentSpecification::newSpecsFromList($specs);
     $this->mergeSpecs($specs);
     $specs_by_name = mpull($specs, null, 'getName');
     $specs_by_short = mpull($specs, null, 'getShortAlias');
     unset($specs_by_short[null]);
     $argv = $this->argv;
     $len = count($argv);
     for ($ii = 0; $ii < $len; $ii++) {
         $arg = $argv[$ii];
         $map = null;
         $options = null;
         if (!is_string($arg)) {
             // Non-string argument; pass it through as-is.
         } else {
             if ($arg == '--') {
                 // This indicates "end of flags".
                 break;
             } else {
                 if ($arg == '-') {
                     // This is a normal argument (e.g., stdin).
                     continue;
                 } else {
                     if (!strncmp('--', $arg, 2)) {
                         $pre = '--';
                         $arg = substr($arg, 2);
                         $map = $specs_by_name;
                         $options = array_keys($specs_by_name);
                     } else {
                         if (!strncmp('-', $arg, 1) && strlen($arg) > 1) {
                             $pre = '-';
                             $arg = substr($arg, 1);
                             $map = $specs_by_short;
                         }
                     }
                 }
             }
         }
         if ($map) {
             $val = null;
             $parts = explode('=', $arg, 2);
             if (count($parts) == 2) {
                 list($arg, $val) = $parts;
             }
             // Try to correct flag spelling for full flags, to allow users to make
             // minor mistakes.
             if ($correct_spelling && $options && !isset($map[$arg])) {
                 $corrections = PhutilArgumentSpellingCorrector::newFlagCorrector()->correctSpelling($arg, $options);
                 if (count($corrections) == 1) {
                     $corrected = head($corrections);
                     $this->logMessage(tsprintf("%s\n", pht('(Assuming "%s" is the British spelling of "%s".)', $pre . $arg, $pre . $corrected)));
                     $arg = $corrected;
                 }
             }
             if (isset($map[$arg])) {
                 $spec = $map[$arg];
                 unset($argv[$ii]);
                 $param_name = $spec->getParamName();
                 if ($val !== null) {
                     if ($param_name === null) {
                         throw new PhutilArgumentUsageException(pht("Argument '%s' does not take a parameter.", "{$pre}{$arg}"));
                     }
                 } else {
                     if ($param_name !== null) {
                         if ($ii + 1 < $len) {
                             $val = $argv[$ii + 1];
                             unset($argv[$ii + 1]);
                             $ii++;
                         } else {
                             throw new PhutilArgumentUsageException(pht("Argument '%s' requires a parameter.", "{$pre}{$arg}"));
                         }
                     } else {
                         $val = true;
                     }
                 }
                 if (!$spec->getRepeatable()) {
                     if (array_key_exists($spec->getName(), $this->results)) {
                         throw new PhutilArgumentUsageException(pht("Argument '%s' was provided twice.", "{$pre}{$arg}"));
                     }
                 }
                 $conflicts = $spec->getConflicts();
                 foreach ($conflicts as $conflict => $reason) {
                     if (array_key_exists($conflict, $this->results)) {
                         if (!is_string($reason) || !strlen($reason)) {
                             $reason = '.';
                         } else {
                             $reason = ': ' . $reason . '.';
                         }
                         throw new PhutilArgumentUsageException(pht("Argument '%s' conflicts with argument '%s'%s", "{$pre}{$arg}", "--{$conflict}", $reason));
                     }
                 }
                 if ($spec->getRepeatable()) {
                     if ($spec->getParamName() === null) {
                         if (empty($this->results[$spec->getName()])) {
                             $this->results[$spec->getName()] = 0;
                         }
                         $this->results[$spec->getName()]++;
                     } else {
                         $this->results[$spec->getName()][] = $val;
                     }
                 } else {
                     $this->results[$spec->getName()] = $val;
                 }
             }
         }
     }
     foreach ($specs as $spec) {
         if ($spec->getWildcard()) {
             $this->results[$spec->getName()] = $this->filterWildcardArgv($argv);
             $argv = array();
             break;
         }
     }
     $this->argv = array_values($argv);
     return $this;
 }