diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -322,11 +322,13 @@ 'PhutilSyntaxHighlighterException' => 'markup/syntax/highlighter/PhutilSyntaxHighlighterException.php', 'PhutilSystem' => 'utils/PhutilSystem.php', 'PhutilSystemTestCase' => 'utils/__tests__/PhutilSystemTestCase.php', + 'PhutilTerminalString' => 'xsprintf/PhutilTerminalString.php', 'PhutilTestPhobject' => 'object/__tests__/PhutilTestPhobject.php', 'PhutilTortureTestDaemon' => 'daemon/torture/PhutilTortureTestDaemon.php', 'PhutilTranslation' => 'internationalization/PhutilTranslation.php', 'PhutilTranslator' => 'internationalization/PhutilTranslator.php', 'PhutilTranslatorTestCase' => 'internationalization/__tests__/PhutilTranslatorTestCase.php', + 'PhutilTsprintfTestCase' => 'xsprintf/__tests__/PhutilTsprintfTestCase.php', 'PhutilTwitchAuthAdapter' => 'auth/PhutilTwitchAuthAdapter.php', 'PhutilTwitchFuture' => 'future/twitch/PhutilTwitchFuture.php', 'PhutilTwitterAuthAdapter' => 'auth/PhutilTwitterAuthAdapter.php', @@ -464,6 +466,7 @@ 'queryfx' => 'xsprintf/queryfx.php', 'queryfx_all' => 'xsprintf/queryfx.php', 'queryfx_one' => 'xsprintf/queryfx.php', + 'tsprintf' => 'xsprintf/tsprintf.php', 'urisprintf' => 'xsprintf/urisprintf.php', 'vcsprintf' => 'xsprintf/csprintf.php', 'vjsprintf' => 'xsprintf/jsprintf.php', @@ -478,6 +481,7 @@ 'xsprintf_ldap' => 'xsprintf/ldapsprintf.php', 'xsprintf_mercurial' => 'xsprintf/hgsprintf.php', 'xsprintf_query' => 'xsprintf/qsprintf.php', + 'xsprintf_terminal' => 'xsprintf/tsprintf.php', 'xsprintf_uri' => 'xsprintf/urisprintf.php', ), 'xmap' => array( @@ -720,10 +724,12 @@ 'PhutilSyntaxHighlighterException' => 'Exception', 'PhutilSystem' => 'Phobject', 'PhutilSystemTestCase' => 'PhutilTestCase', + 'PhutilTerminalString' => 'Phobject', 'PhutilTestPhobject' => 'Phobject', 'PhutilTortureTestDaemon' => 'PhutilDaemon', 'PhutilTranslation' => 'Phobject', 'PhutilTranslatorTestCase' => 'PhutilTestCase', + 'PhutilTsprintfTestCase' => 'PhutilTestCase', 'PhutilTwitchAuthAdapter' => 'PhutilOAuthAuthAdapter', 'PhutilTwitchFuture' => 'FutureProxy', 'PhutilTwitterAuthAdapter' => 'PhutilOAuth1AuthAdapter', diff --git a/src/xsprintf/PhutilTerminalString.php b/src/xsprintf/PhutilTerminalString.php new file mode 100644 --- /dev/null +++ b/src/xsprintf/PhutilTerminalString.php @@ -0,0 +1,63 @@ +string = $string; + } + + public function __toString() { + return $this->string; + } + + public static function escapeStringValue($value, $allow_whitespace) { + if ($value instanceof PhutilTerminalString) { + return (string)$value; + } + + $value = (string)$value; + + static $escape_map; + if ($escape_map === null) { + $escape_map = array( + chr(0x00) => '', + chr(0x07) => '', + chr(0x08) => '', + chr(0x09) => '', + chr(0x0A) => '', + chr(0x0D) => '', + chr(0x1B) => '', + chr(0x7F) => '', + ); + + for ($ii = 0; $ii < 32; $ii++) { + $c = chr($ii); + if (empty($escape_map[$c])) { + $escape_map[$c] = sprintf('<0x%02X>', $ii); + } + } + } + + $map = $escape_map; + if ($allow_whitespace) { + unset($map["\r"]); + unset($map["\n"]); + unset($map["\t"]); + } + + $value = str_replace(array_keys($map), array_values($map), $value); + + // In this mode, we additionally escape any which is not immediately + // followed by . + if ($allow_whitespace) { + $value = preg_replace('/\r(?!\n)/', '', $value); + } + + return $value; + } +} diff --git a/src/xsprintf/__tests__/PhutilTsprintfTestCase.php b/src/xsprintf/__tests__/PhutilTsprintfTestCase.php new file mode 100644 --- /dev/null +++ b/src/xsprintf/__tests__/PhutilTsprintfTestCase.php @@ -0,0 +1,24 @@ +assertEqual( + '', + (string)tsprintf('%s', "\0")); + + $this->assertEqual( + '[31mred[39m', + (string)tsprintf('%s', "\x1B[31mred\x1B[39m")); + + $block = "1\r\n2\r3\n4"; + + $this->assertEqual( + '1234', + (string)tsprintf('%s', $block)); + $this->assertEqual( + "1\r\n23\n4", + (string)tsprintf('%B', $block)); + } + +} diff --git a/src/xsprintf/tsprintf.php b/src/xsprintf/tsprintf.php new file mode 100644 --- /dev/null +++ b/src/xsprintf/tsprintf.php @@ -0,0 +1,43 @@ +