mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 06:42:41 +01:00
Require "--" as an argument terminator when running noninteractively
Summary: Depends on D21001. Ref T13490. - Require "--" to terminate arguments when running noninteractively. - Don't correct spelling when running noninteractively (we still suggest corrections). - Remove old "phage" wrapper script. - Fix an issue where "argv" could implicitly generate a parseable "--argv" flag. - Accept "--" as an argument terminator even when there is no argument list. - Update some strings to use more modern quoting style. - Make workflows decline to handle signals by default. - Allow "arc weld" to weld non-UTF8 files together. Test Plan: Ran various commands. Welded non-UTF8 files. Maniphest Tasks: T13490 Differential Revision: https://secure.phabricator.com/D21002
This commit is contained in:
parent
db8419f19b
commit
d3b77af8a5
9 changed files with 130 additions and 51 deletions
|
@ -1,29 +0,0 @@
|
||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once dirname(__FILE__).'/__init_script__.php';
|
|
||||||
ini_set('memory_limit', -1);
|
|
||||||
|
|
||||||
$args = new PhutilArgumentParser($argv);
|
|
||||||
$args->parseStandardArguments();
|
|
||||||
|
|
||||||
$args->parsePartial(array());
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: This is pretty minimal and should be shared with "arc".
|
|
||||||
$working_directory = getcwd();
|
|
||||||
$working_copy = ArcanistWorkingCopyIdentity::newFromPath($working_directory);
|
|
||||||
$config = id(new ArcanistConfigurationManager())
|
|
||||||
->setWorkingCopyIdentity($working_copy);
|
|
||||||
|
|
||||||
foreach ($config->getProjectConfig('load') as $load) {
|
|
||||||
$load = Filesystem::resolvePath($working_copy->getProjectRoot().'/'.$load);
|
|
||||||
phutil_load_library($load);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$workflows = id(new PhutilClassMapQuery())
|
|
||||||
->setAncestorClass('PhageWorkflow')
|
|
||||||
->execute();
|
|
||||||
$workflows[] = new PhutilHelpArgumentWorkflow();
|
|
||||||
$args->parseWorkflows($workflows);
|
|
|
@ -301,6 +301,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php',
|
'ArcanistMergeConflictLinter' => 'lint/linter/ArcanistMergeConflictLinter.php',
|
||||||
'ArcanistMergeConflictLinterTestCase' => 'lint/linter/__tests__/ArcanistMergeConflictLinterTestCase.php',
|
'ArcanistMergeConflictLinterTestCase' => 'lint/linter/__tests__/ArcanistMergeConflictLinterTestCase.php',
|
||||||
'ArcanistMessageRevisionHardpointLoader' => 'loader/ArcanistMessageRevisionHardpointLoader.php',
|
'ArcanistMessageRevisionHardpointLoader' => 'loader/ArcanistMessageRevisionHardpointLoader.php',
|
||||||
|
'ArcanistMissingArgumentTerminatorException' => 'exception/ArcanistMissingArgumentTerminatorException.php',
|
||||||
'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php',
|
'ArcanistMissingLinterException' => 'lint/linter/exception/ArcanistMissingLinterException.php',
|
||||||
'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php',
|
'ArcanistModifierOrderingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistModifierOrderingXHPASTLinterRule.php',
|
||||||
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistModifierOrderingXHPASTLinterRuleTestCase.php',
|
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistModifierOrderingXHPASTLinterRuleTestCase.php',
|
||||||
|
@ -885,6 +886,7 @@ phutil_register_library_map(array(
|
||||||
'phutil_ini_decode' => 'utils/utils.php',
|
'phutil_ini_decode' => 'utils/utils.php',
|
||||||
'phutil_is_hiphop_runtime' => 'utils/utils.php',
|
'phutil_is_hiphop_runtime' => 'utils/utils.php',
|
||||||
'phutil_is_natural_list' => 'utils/utils.php',
|
'phutil_is_natural_list' => 'utils/utils.php',
|
||||||
|
'phutil_is_noninteractive' => 'utils/utils.php',
|
||||||
'phutil_is_system_locale_available' => 'utils/utf8.php',
|
'phutil_is_system_locale_available' => 'utils/utf8.php',
|
||||||
'phutil_is_utf8' => 'utils/utf8.php',
|
'phutil_is_utf8' => 'utils/utf8.php',
|
||||||
'phutil_is_utf8_slowly' => 'utils/utf8.php',
|
'phutil_is_utf8_slowly' => 'utils/utf8.php',
|
||||||
|
@ -1240,6 +1242,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistMergeConflictLinter' => 'ArcanistLinter',
|
'ArcanistMergeConflictLinter' => 'ArcanistLinter',
|
||||||
'ArcanistMergeConflictLinterTestCase' => 'ArcanistLinterTestCase',
|
'ArcanistMergeConflictLinterTestCase' => 'ArcanistLinterTestCase',
|
||||||
'ArcanistMessageRevisionHardpointLoader' => 'ArcanistHardpointLoader',
|
'ArcanistMessageRevisionHardpointLoader' => 'ArcanistHardpointLoader',
|
||||||
|
'ArcanistMissingArgumentTerminatorException' => 'Exception',
|
||||||
'ArcanistMissingLinterException' => 'Exception',
|
'ArcanistMissingLinterException' => 'Exception',
|
||||||
'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistModifierOrderingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistModifierOrderingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistMissingArgumentTerminatorException
|
||||||
|
extends Exception {}
|
|
@ -78,6 +78,8 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
private $synopsis;
|
private $synopsis;
|
||||||
private $workflows;
|
private $workflows;
|
||||||
private $showHelp;
|
private $showHelp;
|
||||||
|
private $requireArgumentTerminator = false;
|
||||||
|
private $sawTerminator = false;
|
||||||
|
|
||||||
const PARSE_ERROR_CODE = 77;
|
const PARSE_ERROR_CODE = 77;
|
||||||
|
|
||||||
|
@ -129,8 +131,20 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
$specs = PhutilArgumentSpecification::newSpecsFromList($specs);
|
$specs = PhutilArgumentSpecification::newSpecsFromList($specs);
|
||||||
$this->mergeSpecs($specs);
|
$this->mergeSpecs($specs);
|
||||||
|
|
||||||
$specs_by_name = mpull($specs, null, 'getName');
|
// Wildcard arguments have a name like "argv", but we don't want to
|
||||||
$specs_by_short = mpull($specs, null, 'getShortAlias');
|
// parse a corresponding flag like "--argv". Filter them out before
|
||||||
|
// building a list of available flags.
|
||||||
|
$non_wildcard = array();
|
||||||
|
foreach ($specs as $spec_key => $spec) {
|
||||||
|
if ($spec->getWildcard()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$non_wildcard[$spec_key] = $spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
$specs_by_name = mpull($non_wildcard, null, 'getName');
|
||||||
|
$specs_by_short = mpull($non_wildcard, null, 'getShortAlias');
|
||||||
unset($specs_by_short[null]);
|
unset($specs_by_short[null]);
|
||||||
|
|
||||||
$argv = $this->argv;
|
$argv = $this->argv;
|
||||||
|
@ -144,6 +158,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
// Non-string argument; pass it through as-is.
|
// Non-string argument; pass it through as-is.
|
||||||
} else if ($arg == '--') {
|
} else if ($arg == '--') {
|
||||||
// This indicates "end of flags".
|
// This indicates "end of flags".
|
||||||
|
$this->sawTerminator = true;
|
||||||
break;
|
break;
|
||||||
} else if ($arg == '-') {
|
} else if ($arg == '-') {
|
||||||
// This is a normal argument (e.g., stdin).
|
// This is a normal argument (e.g., stdin).
|
||||||
|
@ -174,7 +189,8 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
$corrections = PhutilArgumentSpellingCorrector::newFlagCorrector()
|
$corrections = PhutilArgumentSpellingCorrector::newFlagCorrector()
|
||||||
->correctSpelling($arg, $options);
|
->correctSpelling($arg, $options);
|
||||||
|
|
||||||
if (count($corrections) == 1) {
|
$should_autocorrect = $this->shouldAutocorrect();
|
||||||
|
if (count($corrections) == 1 && $should_autocorrect) {
|
||||||
$corrected = head($corrections);
|
$corrected = head($corrections);
|
||||||
|
|
||||||
$this->logMessage(
|
$this->logMessage(
|
||||||
|
@ -206,7 +222,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
if ($param_name === null) {
|
if ($param_name === null) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' does not take a parameter.",
|
'Argument "%s" does not take a parameter.',
|
||||||
"{$pre}{$arg}"));
|
"{$pre}{$arg}"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,7 +234,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
} else {
|
} else {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' requires a parameter.",
|
'Argument "%s" requires a parameter.',
|
||||||
"{$pre}{$arg}"));
|
"{$pre}{$arg}"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -230,7 +246,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
if (array_key_exists($spec->getName(), $this->results)) {
|
if (array_key_exists($spec->getName(), $this->results)) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' was provided twice.",
|
'Argument "%s" was provided twice.',
|
||||||
"{$pre}{$arg}"));
|
"{$pre}{$arg}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -247,7 +263,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
|
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' conflicts with argument '%s'%s",
|
'Argument "%s" conflicts with argument "%s"%s',
|
||||||
"{$pre}{$arg}",
|
"{$pre}{$arg}",
|
||||||
"--{$conflict}",
|
"--{$conflict}",
|
||||||
$reason));
|
$reason));
|
||||||
|
@ -279,6 +295,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->argv = array_values($argv);
|
$this->argv = array_values($argv);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,10 +317,20 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
public function parseFull(array $specs) {
|
public function parseFull(array $specs) {
|
||||||
$this->parseInternal($specs, true, false);
|
$this->parseInternal($specs, true, false);
|
||||||
|
|
||||||
if (count($this->argv)) {
|
// If we have remaining unconsumed arguments other than a single "--",
|
||||||
$arg = head($this->argv);
|
// fail.
|
||||||
|
$argv = $this->filterWildcardArgv($this->argv);
|
||||||
|
if ($argv) {
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht("Unrecognized argument '%s'.", $arg));
|
pht(
|
||||||
|
'Unrecognized argument "%s".',
|
||||||
|
head($argv)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->getRequireArgumentTerminator()) {
|
||||||
|
if (!$this->sawTerminator) {
|
||||||
|
throw new ArcanistMissingArgumentTerminatorException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->showHelp) {
|
if ($this->showHelp) {
|
||||||
|
@ -408,7 +435,8 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
$corrected = PhutilArgumentSpellingCorrector::newCommandCorrector()
|
$corrected = PhutilArgumentSpellingCorrector::newCommandCorrector()
|
||||||
->correctSpelling($flow, array_keys($this->workflows));
|
->correctSpelling($flow, array_keys($this->workflows));
|
||||||
|
|
||||||
if (count($corrected) == 1) {
|
$should_autocorrect = $this->shouldAutocorrect();
|
||||||
|
if (count($corrected) == 1 && $should_autocorrect) {
|
||||||
$corrected = head($corrected);
|
$corrected = head($corrected);
|
||||||
|
|
||||||
$this->logMessage(
|
$this->logMessage(
|
||||||
|
@ -551,7 +579,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
if ($xprofile) {
|
if ($xprofile) {
|
||||||
if (!function_exists('xhprof_enable')) {
|
if (!function_exists('xhprof_enable')) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht("To use '%s', you must install XHProf.", '--xprofile'));
|
pht('To use "--xprofile", you must install XHProf.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
xhprof_enable(0);
|
xhprof_enable(0);
|
||||||
|
@ -580,7 +608,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
public function getArg($name) {
|
public function getArg($name) {
|
||||||
if (empty($this->specs[$name])) {
|
if (empty($this->specs[$name])) {
|
||||||
throw new PhutilArgumentSpecificationException(
|
throw new PhutilArgumentSpecificationException(
|
||||||
pht("No specification exists for argument '%s'!", $name));
|
pht('No specification exists for argument "%s"!', $name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (idx($this->results, $name) !== null) {
|
if (idx($this->results, $name) !== null) {
|
||||||
|
@ -602,6 +630,14 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
|
|
||||||
/* -( Command Help )------------------------------------------------------- */
|
/* -( Command Help )------------------------------------------------------- */
|
||||||
|
|
||||||
|
public function setRequireArgumentTerminator($require) {
|
||||||
|
$this->requireArgumentTerminator = $require;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequireArgumentTerminator() {
|
||||||
|
return $this->requireArgumentTerminator;
|
||||||
|
}
|
||||||
|
|
||||||
public function setSynopsis($synopsis) {
|
public function setSynopsis($synopsis) {
|
||||||
$this->synopsis = $synopsis;
|
$this->synopsis = $synopsis;
|
||||||
|
@ -750,8 +786,8 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
|
|
||||||
throw new PhutilArgumentUsageException(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' is unrecognized. Use '%s' to indicate ".
|
'Argument "%s" is unrecognized. Use "%s" to indicate '.
|
||||||
"the end of flags.",
|
'the end of flags.',
|
||||||
$value,
|
$value,
|
||||||
'--'));
|
'--'));
|
||||||
}
|
}
|
||||||
|
@ -778,7 +814,9 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
|
|
||||||
if (isset($this->specs[$name])) {
|
if (isset($this->specs[$name])) {
|
||||||
throw new PhutilArgumentSpecificationException(
|
throw new PhutilArgumentSpecificationException(
|
||||||
pht("Two argument specifications have the same name ('%s').", $name));
|
pht(
|
||||||
|
'Two argument specifications have the same name ("%s").',
|
||||||
|
$name));
|
||||||
}
|
}
|
||||||
|
|
||||||
$short = $spec->getShortAlias();
|
$short = $spec->getShortAlias();
|
||||||
|
@ -786,7 +824,7 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
if (isset($short_map[$short])) {
|
if (isset($short_map[$short])) {
|
||||||
throw new PhutilArgumentSpecificationException(
|
throw new PhutilArgumentSpecificationException(
|
||||||
pht(
|
pht(
|
||||||
"Two argument specifications have the same short alias ('%s').",
|
'Two argument specifications have the same short alias ("%s").',
|
||||||
$short));
|
$short));
|
||||||
}
|
}
|
||||||
$short_map[$short] = $spec;
|
$short_map[$short] = $spec;
|
||||||
|
@ -811,13 +849,15 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
if (empty($this->specs[$conflict])) {
|
if (empty($this->specs[$conflict])) {
|
||||||
throw new PhutilArgumentSpecificationException(
|
throw new PhutilArgumentSpecificationException(
|
||||||
pht(
|
pht(
|
||||||
"Argument '%s' conflicts with unspecified argument '%s'.",
|
'Argument "%s" conflicts with unspecified argument "%s".',
|
||||||
$name,
|
$name,
|
||||||
$conflict));
|
$conflict));
|
||||||
}
|
}
|
||||||
if ($conflict == $name) {
|
if ($conflict == $name) {
|
||||||
throw new PhutilArgumentSpecificationException(
|
throw new PhutilArgumentSpecificationException(
|
||||||
pht("Argument '%s' conflicts with itself!", $name));
|
pht(
|
||||||
|
'Argument "%s" conflicts with itself!',
|
||||||
|
$name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -925,11 +965,15 @@ final class PhutilArgumentParser extends Phobject {
|
||||||
"%B\n%s",
|
"%B\n%s",
|
||||||
$message,
|
$message,
|
||||||
pht(
|
pht(
|
||||||
'For details on available commands, run `%s`.',
|
'For details on available commands, run "%s".',
|
||||||
"{$binary} help"));
|
"{$binary} help"));
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new PhutilArgumentUsageException($message);
|
throw new PhutilArgumentUsageException($message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function shouldAutocorrect() {
|
||||||
|
return !phutil_is_noninteractive();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,14 @@ final class ArcanistRuntime {
|
||||||
$args = id(new PhutilArgumentParser($argv))
|
$args = id(new PhutilArgumentParser($argv))
|
||||||
->parseStandardArguments();
|
->parseStandardArguments();
|
||||||
|
|
||||||
|
// If we can test whether STDIN is a TTY, and it isn't, require that "--"
|
||||||
|
// appear in the argument list. This is intended to make it very hard to
|
||||||
|
// write unsafe scripts on top of Arcanist.
|
||||||
|
|
||||||
|
if (phutil_is_noninteractive()) {
|
||||||
|
$args->setRequireArgumentTerminator(true);
|
||||||
|
}
|
||||||
|
|
||||||
$is_trace = $args->getArg('trace');
|
$is_trace = $args->getArg('trace');
|
||||||
$log->setShowTraceMessages($is_trace);
|
$log->setShowTraceMessages($is_trace);
|
||||||
|
|
||||||
|
@ -138,6 +146,29 @@ final class ArcanistRuntime {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return $args->parseWorkflowsFull($phutil_workflows);
|
return $args->parseWorkflowsFull($phutil_workflows);
|
||||||
|
} catch (ArcanistMissingArgumentTerminatorException $terminator_exception) {
|
||||||
|
$log->writeHint(
|
||||||
|
pht('USAGE'),
|
||||||
|
pht(
|
||||||
|
'"%s" is being run noninteractively, but the argument list is '.
|
||||||
|
'missing "--" to indicate end of flags.',
|
||||||
|
$toolset->getToolsetKey()));
|
||||||
|
|
||||||
|
$log->writeHint(
|
||||||
|
pht('USAGE'),
|
||||||
|
pht(
|
||||||
|
'When running noninteractively, you MUST provide "--" to all '.
|
||||||
|
'commands (even if they take no arguments).'));
|
||||||
|
|
||||||
|
$log->writeHint(
|
||||||
|
pht('USAGE'),
|
||||||
|
tsprintf(
|
||||||
|
'%s <__%s__>',
|
||||||
|
pht('Learn More:'),
|
||||||
|
'https://phurl.io/u/noninteractive'));
|
||||||
|
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht('Missing required "--" in argument list.'));
|
||||||
} catch (PhutilArgumentUsageException $usage_exception) {
|
} catch (PhutilArgumentUsageException $usage_exception) {
|
||||||
|
|
||||||
// TODO: This is very, very hacky; we're trying to let errors like
|
// TODO: This is very, very hacky; we're trying to let errors like
|
||||||
|
|
|
@ -145,6 +145,8 @@ EOTEXT
|
||||||
// TOOLSETS: Check if the user already has an alias for this trigger, and
|
// TOOLSETS: Check if the user already has an alias for this trigger, and
|
||||||
// prompt them to overwrite it. Needs prompting to work.
|
// prompt them to overwrite it. Needs prompting to work.
|
||||||
|
|
||||||
|
// TOOLSETS: Don't let users set aliases which don't resolve to anything.
|
||||||
|
|
||||||
$aliases[] = $alias;
|
$aliases[] = $alias;
|
||||||
|
|
||||||
$this->writeAliases($aliases);
|
$this->writeAliases($aliases);
|
||||||
|
|
|
@ -1918,3 +1918,11 @@ function phutil_escape_uri_path_component($string) {
|
||||||
function phutil_unescape_uri_path_component($string) {
|
function phutil_unescape_uri_path_component($string) {
|
||||||
return rawurldecode($string);
|
return rawurldecode($string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function phutil_is_noninteractive() {
|
||||||
|
if (function_exists('posix_isatty') && !posix_isatty(STDIN)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -87,8 +87,20 @@ EOTEXT
|
||||||
$u = rtrim($u, "\r\n");
|
$u = rtrim($u, "\r\n");
|
||||||
$v = rtrim($v, "\r\n");
|
$v = rtrim($v, "\r\n");
|
||||||
|
|
||||||
$u = phutil_utf8v_combined($u);
|
// If the inputs are UTF8, split glyphs (so two valid UTF8 inputs always
|
||||||
$v = phutil_utf8v_combined($v);
|
// produce a sensible, valid UTF8 output). If they aren't, split bytes.
|
||||||
|
|
||||||
|
if (phutil_is_utf8($u)) {
|
||||||
|
$u = phutil_utf8v_combined($u);
|
||||||
|
} else {
|
||||||
|
$u = str_split($u);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phutil_is_utf8($v)) {
|
||||||
|
$v = phutil_utf8v_combined($v);
|
||||||
|
} else {
|
||||||
|
$v = str_split($v);
|
||||||
|
}
|
||||||
|
|
||||||
$len = max(count($u), count($v));
|
$len = max(count($u), count($v));
|
||||||
|
|
||||||
|
|
|
@ -2282,4 +2282,8 @@ abstract class ArcanistWorkflow extends Phobject {
|
||||||
return $this->getConfigurationSourceList()->getConfig($key);
|
return $this->getConfigurationSourceList()->getConfig($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public function canHandleSignal($signo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue