{"thread":{"id":"13899","subject":"[PATCH] Add test-tr: poor-man tr","startedAt":"2008-06-11T18:25:01Z","lastAt":"2008-06-13T18:00:41Z","messageCount":14,"participants":["Alex Riesen","Jeff King","Alf Clement","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"79472","messageId":"20080611182501.GA3344@steel.home","threadId":"13899","inReplyTo":null,"subject":"[PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-11T18:25:01Z","receivedAt":"2008-06-11T18:25:01Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"It offers a limited set of POSIX tr, in particular: no character class\nsupport and no [n*m] operators. Only 8bit. C-escapes supported, and\ncharacter ranges. Deletion and squeezing should work, but -s does not\nmatch the GNU tr from coreutils (which, in turn, does not match POSIX).\n\nSigned-off-by: Alex Riesen <raa.lkml@gmail.com>\n---\n\nRebased on top of current master. I still think it makes the test\nsuite more portable.\n\n Makefile  |    2 +-\n test-tr.c |  206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 207 insertions(+), 1 deletions(-)\n create mode 100644 test-tr.c\n\ndiff --git a/Makefile b/Makefile\nindex 1937507..5f208d6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1229,7 +1229,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-tr$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/test-tr.c b/test-tr.c\nnew file mode 100644\nindex 0000000..82af602\n--- /dev/null\n+++ b/test-tr.c\n@@ -0,0 +1,206 @@\n+/*\n+vim: sw=8\n+*/\n+#include \"cache.h\"\n+\n+static int squeeze, delete;\n+\n+static unsigned char *unquote(const char *s, unsigned *len)\n+{\n+\tunsigned char *result = malloc(strlen(s)), *r = result;\n+\n+\twhile (*s) {\n+\t\tswitch (*s) {\n+\t\tcase '\\\\':\n+\t\t\t++s;\n+#define ISOCT(c) (((c) >= '0' && (c) <= '7'))\n+\t\t\tif (ISOCT(*s)) {\n+\t\t\t\tunsigned int c;\n+\t\t\t\tchar oct[4] = {0,0,0,0};\n+\t\t\t\toct[0] = *s++;\n+\t\t\t\tc = (oct[0] - '0');\n+\t\t\t\tif (ISOCT(*s)) {\n+\t\t\t\t\toct[1] = *s++;\n+\t\t\t\t\tc = (c << 3) |(oct[1] - '0');\n+\t\t\t\t\tif (ISOCT(*s)) {\n+\t\t\t\t\t\toct[2] = *s++;\n+\t\t\t\t\t\tc = (c << 3) |(oct[2] - '0');\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tif (c > 255) {\n+\t\t\t\t\tfprintf(stderr, \"invalid octal character specification: \\\\%s\\n\", oct);\n+\t\t\t\t\texit(1);\n+\t\t\t\t}\n+\t\t\t\t*r++ = c & 0xff;\n+\t\t\t} else {\n+\t\t\t\tswitch (*s) {\n+\t\t\t\tcase '\\0':\n+\t\t\t\t\t*r++ = '\\\\';\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase '\\\\':\n+\t\t\t\t\t*r++ = *s++;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'a':\n+\t\t\t\t\t*r++ = '\\a';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'b':\n+\t\t\t\t\t*r++ = '\\b';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'f':\n+\t\t\t\t\t*r++ = '\\f';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'n':\n+\t\t\t\t\t*r++ = '\\n';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'r':\n+\t\t\t\t\t*r++ = '\\r';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 't':\n+\t\t\t\t\t*r++ = '\\t';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'v':\n+\t\t\t\t\t*r++ = '\\v';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\t*r++ = '\\\\';\n+\t\t\t\t\t*r++ = *s++;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t*r++ = *s++;\n+\t\t}\n+\t}\n+\n+\t*len = r - result;\n+\t*r = '\\0';\n+\treturn result;\n+}\n+\n+#define MAX_PATTERN 256\n+static void put_op(unsigned char *conv, unsigned char ch, unsigned *len)\n+{\n+\tunsigned i = (*len)++;\n+\tif (*len > MAX_PATTERN) {\n+\t\tfprintf(stderr, \"pattern too long\\n\");\n+\t\texit(1);\n+\t}\n+\tconv[i] = ch;\n+}\n+\n+static void parse(const unsigned char *rule, unsigned rule_len,\n+\t\t  unsigned char *set, unsigned *set_len)\n+{\n+\tconst unsigned char *p = rule;\n+\twhile (p < rule + rule_len) {\n+\t\tif ('-' == *p && p > rule && p[1]) {\n+\t\t\tunsigned c;\n+\t\t\tif (p[-1] > p[1]) {\n+\t\t\t\tfprintf(stderr, \"%c%c%c: range is reversed\\n\",\n+\t\t\t\t\tp[-1], *p, p[1]);\n+\t\t\t\texit(1);\n+\t\t\t}\n+\t\t\tc = p[-1] + 1u;\n+\t\t\tfor (; c <= p[1]; ++c)\n+\t\t\t\tput_op(set, c, set_len);\n+\t\t\t++p;\n+\t\t\t++p;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tput_op(set, *p, set_len);\n+\t\t++p;\n+\t}\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tunsigned set1_len = 0, set2_len = 0;\n+\tunsigned char set1[MAX_PATTERN];\n+\tunsigned char set2[MAX_PATTERN];\n+\n+\tssize_t n;\n+\tunsigned char last = 0, have_last = 0;\n+\tunsigned char buf[BUFSIZ];\n+\n+\tchar *rule1 = NULL, *rule2 = NULL;\n+\tunsigned char *urule1, *urule2;\n+\tunsigned urule1_len, urule2_len;\n+\tint opt;\n+\n+\tfor (opt = 1; opt < argc; ++opt) {\n+\t\tif (!strcmp(\"-s\", argv[opt]))\n+\t\t\tsqueeze = 1;\n+\t\telse if (!strcmp(\"-d\", argv[opt]))\n+\t\t\tdelete = 1;\n+\t\telse if (!rule1) {\n+\t\t\trule1 = argv[opt];\n+\t\t} else if (!rule2)\n+\t\t\trule2 = argv[opt];\n+\t}\n+\tif (!rule1) {\n+\t    fprintf(stderr, \"no source set given\\n\"\n+\t\t    \"test-tr [-s] [-d] set1 [set2]\\n\"\n+\t\t    \"\\\"set\\\" supports only \\\\NNN, \\\\a-\\\\v and CHAR1-CHAR2 rules\\n\");\n+\t    exit(1);\n+\t}\n+\tif (delete && rule2) {\n+\t\tfprintf(stderr, \"extra operand %s when deleting\\n\", rule2);\n+\t\texit(1);\n+\t}\n+\turule1 = unquote(rule1, &urule1_len);\n+\turule2 = NULL;\n+\turule2_len = 0;\n+\tif ((!rule2 || !*rule2) && !delete && !squeeze) {\n+\t\tfprintf(stderr, \"set2 must be non-empty\\n\");\n+\t\texit(1);\n+\t}\n+\n+\tparse(urule1, urule1_len, set1, &set1_len);\n+\n+\tif (rule2) {\n+\t\tunsigned i;\n+\t\turule2 = unquote(rule2, &urule2_len);\n+\t\tparse(urule2, urule2_len, set2, &set2_len);\n+\t\ti = set2[set2_len - 1];\n+\t\twhile (set2_len < set1_len)\n+\t\t\tput_op(set2, i, &set2_len);\n+\t}\n+\n+\twhile ((n = read(STDIN_FILENO, buf, sizeof(buf)))) {\n+\t\tif (n < 0) {\n+\t\t\tint err = errno;\n+\t\t\tif (EINTR == err || EAGAIN == err)\n+\t\t\t\tcontinue;\n+\t\t\tfprintf(stderr, \"%s: %s\\n\", argv[0], strerror(err));\n+\t\t\texit(1);\n+\t\t}\n+\t\tif (set1_len) {\n+\t\t\tunsigned i, o = 0;\n+\t\t\tfor (i = 0; i < (unsigned)n; ++i) {\n+\t\t\t\tunsigned char *p, ch = buf[i];\n+\t\t\t\tp = memchr(set1, ch, set1_len);\n+\t\t\t\tif (p) {\n+\t\t\t\t\tif (delete)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tif (set2_len)\n+\t\t\t\t\t\tch = set2[p - set1];\n+\t\t\t\t}\n+\t\t\t\tif (!(squeeze && have_last && ch == last))\n+\t\t\t\t\tbuf[o++] = ch;\n+\t\t\t\thave_last = 1;\n+\t\t\t\tlast = ch;\n+\t\t\t}\n+\t\t\tn = o;\n+\t\t}\n+\t\twrite(STDOUT_FILENO, buf, n);\n+\t}\n+\treturn 0;\n+}\n-- \n1.5.6.rc2.57.gc9624\n"},{"id":"79473","messageId":"20080611182608.GB3344@steel.home","threadId":"13899","inReplyTo":"20080611182501.GA3344@steel.home","subject":"[PATCH] Use test-tr in the tests","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-11T18:26:08Z","receivedAt":"2008-06-11T18:26:08Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Signed-off-by: Alex Riesen <raa.lkml@gmail.com>\n---\n t/annotate-tests.sh            |    4 ++--\n t/diff-lib.sh                  |    4 ++--\n t/t0000-basic.sh               |    2 +-\n t/t0020-crlf.sh                |   10 +++++-----\n t/t0021-conversion.sh          |    2 +-\n t/t1300-repo-config.sh         |    4 ++--\n t/t3300-funny-names.sh         |    6 +++---\n t/t3402-rebase-merge.sh        |    4 ++--\n t/t4004-diff-rename-symlink.sh |    2 +-\n t/t4015-diff-whitespace.sh     |    6 +++---\n t/t4019-diff-wserror.sh        |    2 +-\n t/t4020-diff-external.sh       |    2 +-\n t/t4022-diff-rewrite.sh        |    2 +-\n t/t4101-apply-nonl.sh          |    4 ++--\n t/t4103-apply-binary.sh        |    4 ++--\n t/t4116-apply-reverse.sh       |    4 ++--\n t/t4118-apply-empty-context.sh |    4 ++--\n t/t4200-rerere.sh              |    2 +-\n t/t4201-shortlog.sh            |    4 ++--\n t/t5300-pack-object.sh         |    2 +-\n t/t5500-fetch-pack.sh          |    2 +-\n t/t5505-remote.sh              |    4 ++--\n t/t6003-rev-list-topo-order.sh |    2 +-\n t/t6033-merge-crlf.sh          |    4 ++--\n t/t7003-filter-branch.sh       |    2 +-\n t/t9200-git-cvsexportcommit.sh |    2 +-\n t/t9401-git-cvsserver-crlf.sh  |    4 ++--\n t/test-lib.sh                  |   12 +++++++-----\n 28 files changed, 54 insertions(+), 52 deletions(-)\n\ndiff --git a/t/annotate-tests.sh b/t/annotate-tests.sh\nindex cacb273..8e10323 100644\n--- a/t/annotate-tests.sh\n+++ b/t/annotate-tests.sh\n@@ -104,7 +104,7 @@ test_expect_success \\\n \n test_expect_success \\\n     'an incomplete line added' \\\n-    'echo \"incomplete\" | tr -d \"\\\\012\" >>file &&\n+    'echo \"incomplete\" | test-tr -d \"\\\\012\" >>file &&\n     GIT_AUTHOR_NAME=\"C\" git commit -a -m \"Incomplete\"'\n \n test_expect_success \\\n@@ -115,7 +115,7 @@ test_expect_success \\\n     'some edit' \\\n     'mv file file.orig &&\n     sed -e \"s/^3A/99/\" -e \"/^1A/d\" -e \"/^incomplete/d\" < file.orig > file &&\n-    echo \"incomplete\" | tr -d \"\\\\012\" >>file &&\n+    echo \"incomplete\" | test-tr -d \"\\\\012\" >>file &&\n     GIT_AUTHOR_NAME=\"D\" git commit -a -m \"edit\"'\n \n test_expect_success \\\ndiff --git a/t/diff-lib.sh b/t/diff-lib.sh\nindex 4bddeb5..f840340 100644\n--- a/t/diff-lib.sh\n+++ b/t/diff-lib.sh\n@@ -21,8 +21,8 @@ compare_diff_raw_z () {\n     # Also we do not check SHA1 hash generation in this test, which\n     # is a job for t0000-basic.sh\n \n-    perl -pe 'y/\\000/\\012/' <\"$1\" | sed -e \"$sanitize_diff_raw_z\" >.tmp-1\n-    perl -pe 'y/\\000/\\012/' <\"$2\" | sed -e \"$sanitize_diff_raw_z\" >.tmp-2\n+    test-tr '\\000' '\\012' <\"$1\" | sed -e \"$sanitize_diff_raw_z\" >.tmp-1\n+    test-tr '\\000' '\\012' <\"$2\" | sed -e \"$sanitize_diff_raw_z\" >.tmp-2\n     test_cmp .tmp-1 .tmp-2 && rm -f .tmp-1 .tmp-2\n }\n \ndiff --git a/t/t0000-basic.sh b/t/t0000-basic.sh\nindex 690f80a..ad81546 100755\n--- a/t/t0000-basic.sh\n+++ b/t/t0000-basic.sh\n@@ -324,7 +324,7 @@ test_expect_success 'very long name in the index handled sanely' '\n \t(\n \t\tgit ls-files -s path4 |\n \t\tsed -e \"s/\t.*/\t/\" |\n-\t\ttr -d \"\\012\"\n+\t\ttest-tr -d \"\\012\"\n \t\techo \"$a\"\n \t) | git update-index --index-info &&\n \tlen=$(git ls-files \"a*\" | wc -c) &&\ndiff --git a/t/t0020-crlf.sh b/t/t0020-crlf.sh\nindex 2bfeac9..7427d59 100755\n--- a/t/t0020-crlf.sh\n+++ b/t/t0020-crlf.sh\n@@ -5,20 +5,20 @@ test_description='CRLF conversion'\n . ./test-lib.sh\n \n q_to_nul () {\n-\tperl -pe 'y/Q/\\000/'\n+\ttest-tr Q '\\000'\n }\n \n q_to_cr () {\n-\ttr Q '\\015'\n+\ttest-tr Q '\\015'\n }\n \n append_cr () {\n-\tsed -e 's/$/Q/' | tr Q '\\015'\n+\tsed -e 's/$/Q/' | test-tr Q '\\015'\n }\n \n remove_cr () {\n-\ttr '\\015' Q <\"$1\" | grep Q >/dev/null &&\n-\ttr '\\015' Q <\"$1\" | sed -ne 's/Q$//p'\n+\ttest-tr '\\015' Q <\"$1\" | grep Q >/dev/null &&\n+\ttest-tr '\\015' Q <\"$1\" | sed -ne 's/Q$//p'\n }\n \n test_expect_success setup '\ndiff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh\nindex 8fc39d7..d146db4 100755\n--- a/t/t0021-conversion.sh\n+++ b/t/t0021-conversion.sh\n@@ -5,7 +5,7 @@ test_description='blob conversion via gitattributes'\n . ./test-lib.sh\n \n cat <<\\EOF >rot13.sh\n-tr \\\n+test-tr \\\n   'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \\\n   'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'\n EOF\ndiff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh\nindex afe7e66..57ba088 100755\n--- a/t/t1300-repo-config.sh\n+++ b/t/t1300-repo-config.sh\n@@ -715,12 +715,12 @@ Qsection.sub=section.val4\n Qsection.sub=section.val5Q\n EOF\n \n-git config --null --list | perl -pe 'y/\\000/Q/' > result\n+git config --null --list | test-tr '\\000' Q > result\n echo >>result\n \n test_expect_success '--null --list' 'cmp result expect'\n \n-git config --null --get-regexp 'val[0-9]' | perl -pe 'y/\\000/Q/' > result\n+git config --null --get-regexp 'val[0-9]' | test-tr '\\000' Q > result\n echo >>result\n \n test_expect_success '--null --get-regexp' 'cmp result expect'\ndiff --git a/t/t3300-funny-names.sh b/t/t3300-funny-names.sh\nindex 0574ef1..6ec6c24 100755\n--- a/t/t3300-funny-names.sh\n+++ b/t/t3300-funny-names.sh\n@@ -54,7 +54,7 @@ echo 'just space\n no-funny\n tabs\t,\" (dq) and spaces' >expected\n test_expect_success 'git ls-files -z with-funny' \\\n-\t'git ls-files -z | perl -pe y/\\\\000/\\\\012/ >current &&\n+\t'git ls-files -z | test-tr \"\\000\" \"\\012\" >current &&\n \ttest_cmp expected current'\n \n t1=`git write-tree`\n@@ -83,11 +83,11 @@ test_expect_success 'git diff-tree with-funny' \\\n echo 'A\n tabs\t,\" (dq) and spaces' >expected\n test_expect_success 'git diff-index -z with-funny' \\\n-\t'git diff-index -z --name-status $t0 | perl -pe y/\\\\000/\\\\012/ >current &&\n+\t'git diff-index -z --name-status $t0 | test-tr \"\\000\" \"\\012\" >current &&\n \ttest_cmp expected current'\n \n test_expect_success 'git diff-tree -z with-funny' \\\n-\t'git diff-tree -z --name-status $t0 $t1 | perl -pe y/\\\\000/\\\\012/ >current &&\n+\t'git diff-tree -z --name-status $t0 $t1 | test-tr \"\\000\" \"\\012\" >current &&\n \ttest_cmp expected current'\n \n cat > expected <<\\EOF\ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nindex 7b7d072..704f9a3 100755\n--- a/t/t3402-rebase-merge.sh\n+++ b/t/t3402-rebase-merge.sh\n@@ -30,11 +30,11 @@ test_expect_success setup '\n \tgit update-index --force-remove original &&\n \tgit commit -a -m\"side renames and edits.\" &&\n \n-\ttr \"[a-z]\" \"[A-Z]\" <original >newfile &&\n+\ttest-tr a-z A-Z <original >newfile &&\n \tgit add newfile &&\n \tgit commit -a -m\"side edits further.\" &&\n \n-\ttr \"[a-m]\" \"[A-M]\" <original >newfile &&\n+\ttest-tr a-m A-M <original >newfile &&\n \trm -f original &&\n \tgit commit -a -m\"side edits once again.\" &&\n \ndiff --git a/t/t4004-diff-rename-symlink.sh b/t/t4004-diff-rename-symlink.sh\nindex 3d25be7..b49efa4 100755\n--- a/t/t4004-diff-rename-symlink.sh\n+++ b/t/t4004-diff-rename-symlink.sh\n@@ -14,7 +14,7 @@ by an edit for them.\n \n test_expect_success \\\n     'prepare reference tree' \\\n-    'echo xyzzy | tr -d '\\\\\\\\'012 >yomin &&\n+    'echo xyzzy | test-tr -d \"\\012\" >yomin &&\n      ln -s xyzzy frotz &&\n     git update-index --add frotz yomin &&\n     tree=$(git write-tree) &&\ndiff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh\nindex ca0302f..8841626 100755\n--- a/t/t4015-diff-whitespace.sh\n+++ b/t/t4015-diff-whitespace.sh\n@@ -51,7 +51,7 @@ test_expect_success \"Ray's example with -w\" 'test_cmp expect out'\n git diff -b > out\n test_expect_success \"Ray's example with -b\" 'test_cmp expect out'\n \n-tr 'Q' '\\015' << EOF > x\n+test-tr 'Q' '\\015' << EOF > x\n whitespace at beginning\n whitespace change\n whitespace in the middle\n@@ -71,7 +71,7 @@ unchanged line\n CR at end\n EOF\n \n-tr 'Q' '\\015' << EOF > expect\n+test-tr 'Q' '\\015' << EOF > expect\n diff --git a/x b/x\n index d99af23..8b32fb5 100644\n --- a/x\n@@ -99,7 +99,7 @@ EOF\n git diff -w > out\n test_expect_success 'another test, with -w' 'test_cmp expect out'\n \n-tr 'Q' '\\015' << EOF > expect\n+test-tr 'Q' '\\015' << EOF > expect\n diff --git a/x b/x\n index d99af23..8b32fb5 100644\n --- a/x\ndiff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh\nindex 0d9cbb6..4967026 100755\n--- a/t/t4019-diff-wserror.sh\n+++ b/t/t4019-diff-wserror.sh\n@@ -12,7 +12,7 @@ test_expect_success setup '\n \techo \"         Eight SP indent\" >>F &&\n \techo \" \tHT and SP indent\" >>F &&\n \techo \"With trailing SP \" >>F &&\n-\techo \"Carriage ReturnQ\" | tr Q \"\\015\" >>F &&\n+\techo \"Carriage ReturnQ\" | test-tr Q \"\\015\" >>F &&\n \techo \"No problem\" >>F\n \n '\ndiff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh\nindex 637b4e1..3ae3165 100755\n--- a/t/t4020-diff-external.sh\n+++ b/t/t4020-diff-external.sh\n@@ -99,7 +99,7 @@ test_expect_success 'no diff with -diff' '\n \tgit diff | grep Binary\n '\n \n-echo NULZbetweenZwords | perl -pe 'y/Z/\\000/' > file\n+echo NULZbetweenZwords | test-tr 'Z' '\\000' > file\n \n test_expect_success 'force diff with \"diff\"' '\n \techo >.gitattributes \"file diff\" &&\ndiff --git a/t/t4022-diff-rewrite.sh b/t/t4022-diff-rewrite.sh\nindex bf996fc..01d221b 100755\n--- a/t/t4022-diff-rewrite.sh\n+++ b/t/t4022-diff-rewrite.sh\n@@ -8,7 +8,7 @@ test_expect_success setup '\n \n \tcat ../../COPYING >test &&\n \tgit add test &&\n-\ttr \\\n+\ttest-tr \\\n \t  \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\" \\\n \t  \"nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM\" \\\n \t  <../../COPYING >test\ndiff --git a/t/t4101-apply-nonl.sh b/t/t4101-apply-nonl.sh\nindex da8abcf..53277e6 100755\n--- a/t/t4101-apply-nonl.sh\n+++ b/t/t4101-apply-nonl.sh\n@@ -12,8 +12,8 @@ test_description='git apply should handle files with incomplete lines.\n \n (echo a; echo b) >frotz.0\n (echo a; echo b; echo c) >frotz.1\n-(echo a; echo b | tr -d '\\012') >frotz.2\n-(echo a; echo c; echo b | tr -d '\\012') >frotz.3\n+(echo a; echo b | test-tr -d '\\012') >frotz.2\n+(echo a; echo c; echo b | test-tr -d '\\012') >frotz.3\n \n for i in 0 1 2 3\n do\ndiff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh\nindex 1b58233..f7542a0 100755\n--- a/t/t4103-apply-binary.sh\n+++ b/t/t4103-apply-binary.sh\n@@ -24,10 +24,10 @@ git update-index --add --remove file1 file2 file4\n git-commit -m 'Initial Version' 2>/dev/null\n \n git-checkout -b binary\n-perl -pe 'y/x/\\000/' <file1 >file3\n+test-tr x '\\000' <file1 >file3\n cat file3 >file4\n git add file2\n-perl -pe 'y/\\000/v/' <file3 >file1\n+test-tr '\\000' v <file3 >file1\n rm -f file2\n git update-index --add --remove file1 file2 file3 file4\n git-commit -m 'Second Version'\ndiff --git a/t/t4116-apply-reverse.sh b/t/t4116-apply-reverse.sh\nindex 1459a90..31608df 100755\n--- a/t/t4116-apply-reverse.sh\n+++ b/t/t4116-apply-reverse.sh\n@@ -12,14 +12,14 @@ test_description='git apply in reverse\n test_expect_success setup '\n \n \tfor i in a b c d e f g h i j k l m n; do echo $i; done >file1 &&\n-\tperl -pe \"y/ijk/\\\\000\\\\001\\\\002/\" <file1 >file2 &&\n+\ttest-tr \"ijk\" \"\\000\\001\\002\" <file1 >file2 &&\n \n \tgit add file1 file2 &&\n \tgit commit -m initial &&\n \tgit tag initial &&\n \n \tfor i in a b c g h i J K L m o n p q; do echo $i; done >file1 &&\n-\tperl -pe \"y/mon/\\\\000\\\\001\\\\002/\" <file1 >file2 &&\n+\ttest-tr \"mon\" \"\\000\\001\\002\" <file1 >file2 &&\n \n \tgit commit -a -m second &&\n \tgit tag second &&\ndiff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh\nindex f92e259..89e5d14 100755\n--- a/t/t4118-apply-empty-context.sh\n+++ b/t/t4118-apply-empty-context.sh\n@@ -18,13 +18,13 @@ test_expect_success setup '\n \tcat file1 >file1.orig &&\n \t{\n \t\tcat file1 &&\n-\t\techo Q | tr -d \"\\\\012\"\n+\t\techo Q | test-tr -d \"\\012\"\n \t} >file2 &&\n \tcat file2 >file2.orig\n \tgit add file1 file2 &&\n \tsed -e \"/^B/d\" <file1.orig >file1 &&\n \tsed -e \"/^[BQ]/d\" <file2.orig >file2 &&\n-\techo Q | tr -d \"\\\\012\" >>file2 &&\n+\techo Q | test-tr -d \"\\012\" >>file2 &&\n \tcat file1 >file1.mods &&\n \tcat file2 >file2.mods &&\n \tgit diff |\ndiff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh\nindex 85d7e3e..23b10f2 100755\n--- a/t/t4200-rerere.sh\n+++ b/t/t4200-rerere.sh\n@@ -129,7 +129,7 @@ test_expect_success 'rerere kicked in' \"! grep ======= a1\"\n test_expect_success 'rerere prefers first change' 'test_cmp a1 expect'\n \n rm $rr/postimage\n-echo \"$sha1\ta1\" | perl -pe 'y/\\012/\\000/' > .git/rr-cache/MERGE_RR\n+echo \"$sha1\ta1\" | test-tr '\\012' '\\000' > .git/rr-cache/MERGE_RR\n \n test_expect_success 'rerere clear' 'git rerere clear'\n \ndiff --git a/t/t4201-shortlog.sh b/t/t4201-shortlog.sh\nindex 405b971..1a4b88d 100755\n--- a/t/t4201-shortlog.sh\n+++ b/t/t4201-shortlog.sh\n@@ -19,12 +19,12 @@ git commit --quiet -m \"This is a very, very long first line for the commit messa\n \n # test if the wrapping is still valid when replacing all i's by treble clefs.\n echo 3 > a1\n-git commit --quiet -m \"$(echo \"This is a very, very long first line for the commit message to see if it is wrapped correctly\" | sed \"s/i/1234/g\" | tr 1234 '\\360\\235\\204\\236')\" a1\n+git commit --quiet -m \"$(echo \"This is a very, very long first line for the commit message to see if it is wrapped correctly\" | sed \"s/i/1234/g\" | test-tr 1234 '\\360\\235\\204\\236')\" a1\n \n # now fsck up the utf8\n git config i18n.commitencoding non-utf-8\n echo 4 > a1\n-git commit --quiet -m \"$(echo \"This is a very, very long first line for the commit message to see if it is wrapped correctly\" | sed \"s/i/1234/g\" | tr 1234 '\\370\\235\\204\\236')\" a1\n+git commit --quiet -m \"$(echo \"This is a very, very long first line for the commit message to see if it is wrapped correctly\" | sed \"s/i/1234/g\" | test-tr 1234 '\\370\\235\\204\\236')\" a1\n \n echo 5 > a1\n git commit --quiet -m \"a\t\t\t\t\t\t\t\t12\t34\t56\t78\" a1\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex 983a393..ebb85a5 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -15,7 +15,7 @@ test_expect_success \\\n     'rm -f .git/index*\n      for i in a b c\n      do\n-\t     dd if=/dev/zero bs=4k count=1 | perl -pe \"y/\\\\000/$i/\" >$i &&\n+\t     dd if=/dev/zero bs=4k count=1 | test-tr \"\\000\" \"$i\" >$i &&\n \t     git update-index --add $i || return 1\n      done &&\n      cat c >d && echo foo >>d && git update-index --add d &&\ndiff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh\nindex 140e874..784600e 100755\n--- a/t/t5500-fetch-pack.sh\n+++ b/t/t5500-fetch-pack.sh\n@@ -37,7 +37,7 @@ add () {\n }\n \n count_objects () {\n-\tls .git/objects/??/* 2>>log2.txt | wc -l | tr -d \" \"\n+\tls .git/objects/??/* 2>>log2.txt | wc -l | test-tr -d \" \"\n }\n \n test_expect_object_count () {\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 0d7ed1f..4c08c81 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -22,8 +22,8 @@ setup_repository () {\n }\n \n tokens_match () {\n-\techo \"$1\" | tr ' ' '\\012' | sort | sed -e '/^$/d' >expect &&\n-\techo \"$2\" | tr ' ' '\\012' | sort | sed -e '/^$/d' >actual &&\n+\techo \"$1\" | test-tr ' ' '\\012' | sort | sed -e '/^$/d' >expect &&\n+\techo \"$2\" | test-tr ' ' '\\012' | sort | sed -e '/^$/d' >actual &&\n \ttest_cmp expect actual\n }\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 5daa0be..5dbc6a7 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -79,7 +79,7 @@ save_tag g4 unique_commit g6 tree -p g3 -p h2\n \n git update-ref HEAD $(tag l5)\n \n-test_output_expect_success 'rev-list has correct number of entries' 'git rev-list HEAD | wc -l | tr -d \\\" \\\"' <<EOF\n+test_output_expect_success 'rev-list has correct number of entries' 'git rev-list HEAD | wc -l | test-tr -d \\\" \\\"' <<EOF\n 19\n EOF\n \ndiff --git a/t/t6033-merge-crlf.sh b/t/t6033-merge-crlf.sh\nindex 75d9602..fed088a 100755\n--- a/t/t6033-merge-crlf.sh\n+++ b/t/t6033-merge-crlf.sh\n@@ -1,11 +1,11 @@\n #!/bin/sh\n \n append_cr () {\n-\tsed -e 's/$/Q/' | tr Q '\\015'\n+\tsed -e 's/$/Q/' | test-tr Q '\\015'\n }\n \n remove_cr () {\n-\ttr '\\015' Q | sed -e 's/Q$//'\n+\ttest-tr '\\015' Q | sed -e 's/Q$//'\n }\n \n test_description='merge conflict in crlf repo\ndiff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh\nindex e26f726..9928863 100755\n--- a/t/t7003-filter-branch.sh\n+++ b/t/t7003-filter-branch.sh\n@@ -4,7 +4,7 @@ test_description='git-filter-branch'\n . ./test-lib.sh\n \n make_commit () {\n-\tlower=$(echo $1 | tr '[A-Z]' '[a-z]')\n+\tlower=$(echo $1 | test-tr 'A-Z' 'a-z')\n \techo $lower > $lower\n \tgit add $lower\n \ttest_tick\ndiff --git a/t/t9200-git-cvsexportcommit.sh b/t/t9200-git-cvsexportcommit.sh\nindex b1dc32d..f84a99c 100755\n--- a/t/t9200-git-cvsexportcommit.sh\n+++ b/t/t9200-git-cvsexportcommit.sh\n@@ -35,7 +35,7 @@ check_entries () {\n \tthen\n \t\t>expected\n \telse\n-\t\tprintf '%s\\n' \"$2\" | tr '|' '\\012' >expected\n+\t\tprintf '%s\\n' \"$2\" | test-tr '|' '\\012' >expected\n \tfi\n \ttest_cmp expected actual\n }\ndiff --git a/t/t9401-git-cvsserver-crlf.sh b/t/t9401-git-cvsserver-crlf.sh\nindex e27a1c5..f5b4d94 100755\n--- a/t/t9401-git-cvsserver-crlf.sh\n+++ b/t/t9401-git-cvsserver-crlf.sh\n@@ -12,11 +12,11 @@ repository using cvs CLI client via git-cvsserver server'\n . ./test-lib.sh\n \n q_to_nul () {\n-    perl -pe 'y/Q/\\000/'\n+    test-tr Q '\\000'\n }\n \n q_to_cr () {\n-    tr Q '\\015'\n+    test-tr Q '\\015'\n }\n \n marked_as () {\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 7a8bd27..c3eb7df 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -44,11 +44,16 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n export EDITOR VISUAL\n GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}\n \n+# Test the binaries we have just built.  The tests are kept in\n+# t/ subdirectory and are run in trash subdirectory.\n+PATH=$(pwd)/..:$PATH\n+export PATH\n+\n # Protect ourselves from common misconfiguration to export\n # CDPATH into the environment\n unset CDPATH\n \n-case $(echo $GIT_TRACE |tr \"[A-Z]\" \"[a-z]\") in\n+case $(echo $GIT_TRACE |test-tr A-Z a-z) in\n \t1|2|true)\n \t\techo \"* warning: Some tests will not work if GIT_TRACE\" \\\n \t\t\t\"is set as to trace on STDERR ! *\"\n@@ -385,16 +390,13 @@ test_done () {\n \tesac\n }\n \n-# Test the binaries we have just built.  The tests are kept in\n-# t/ subdirectory and are run in trash subdirectory.\n-PATH=$(pwd)/..:$PATH\n GIT_EXEC_PATH=$(pwd)/..\n GIT_TEMPLATE_DIR=$(pwd)/../templates/blt\n unset GIT_CONFIG\n unset GIT_CONFIG_LOCAL\n GIT_CONFIG_NOSYSTEM=1\n GIT_CONFIG_NOGLOBAL=1\n-export PATH GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL\n+export GIT_EXEC_PATH GIT_TEMPLATE_DIR GIT_CONFIG_NOSYSTEM GIT_CONFIG_NOGLOBAL\n \n GITPERLLIB=$(pwd)/../perl/blib/lib:$(pwd)/../perl/blib/arch/auto/Git\n export GITPERLLIB\n-- \n1.5.6.rc2.57.gc9624\n"},{"id":"79508","messageId":"20080611213444.GA14753@steel.home","threadId":"13899","inReplyTo":"20080611182501.GA3344@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-11T21:34:44Z","receivedAt":"2008-06-11T21:34:44Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Alex Riesen, Wed, Jun 11, 2008 20:25:01 +0200:\n> --- /dev/null\n> +++ b/test-tr.c\n> @@ -0,0 +1,206 @@\n> +/*\n> +vim: sw=8\n> +*/\n\nSorry about that...\n"},{"id":"79518","messageId":"20080611225448.GC19474@sigill.intra.peff.net","threadId":"13899","inReplyTo":"20080611182501.GA3344@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-06-11T22:54:48Z","receivedAt":"2008-06-11T22:54:48Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Jun 11, 2008 at 08:25:01PM +0200, Alex Riesen wrote:\n\n> It offers a limited set of POSIX tr, in particular: no character class\n> support and no [n*m] operators. Only 8bit. C-escapes supported, and\n> character ranges. Deletion and squeezing should work, but -s does not\n> match the GNU tr from coreutils (which, in turn, does not match POSIX).\n> \n> Signed-off-by: Alex Riesen <raa.lkml@gmail.com>\n> ---\n> \n> Rebased on top of current master. I still think it makes the test\n> suite more portable.\n\nHaving wrestled with Solaris tr, I can understand where you are coming\nfrom. However, does this _actually_ increase the portability of the test\nsuite? That is, are there failing tests that this fixes, and if so, for\nwhich platforms (getting a successful run of the test suite on Solaris\nis still on my long-term todo, but I thought I had fixed all of the tr\nissues)?\n\nOr is your rationale \"this will prevent people from screwing up the test\nscripts accidentally in the future\"?\n\nI am not opposed to the latter, but I think it makes sense to state it\nclearly in the commit message.\n\n-Peff\n"},{"id":"79559","messageId":"20080612060152.GA3798@steel.home","threadId":"13899","inReplyTo":"20080611225448.GC19474@sigill.intra.peff.net","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-12T06:01:52Z","receivedAt":"2008-06-12T06:01:52Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Jeff King, Thu, Jun 12, 2008 00:54:48 +0200:\n> On Wed, Jun 11, 2008 at 08:25:01PM +0200, Alex Riesen wrote:\n> \n> > It offers a limited set of POSIX tr, in particular: no character class\n> > support and no [n*m] operators. Only 8bit. C-escapes supported, and\n> > character ranges. Deletion and squeezing should work, but -s does not\n> > match the GNU tr from coreutils (which, in turn, does not match POSIX).\n> > \n> > Signed-off-by: Alex Riesen <raa.lkml@gmail.com>\n> > ---\n> > \n> > Rebased on top of current master. I still think it makes the test\n> > suite more portable.\n> \n> Having wrestled with Solaris tr, I can understand where you are coming\n> from. However, does this _actually_ increase the portability of the test\n> suite? That is, are there failing tests that this fixes, and if so, for\n> which platforms (getting a successful run of the test suite on Solaris\n> is still on my long-term todo, but I thought I had fixed all of the tr\n> issues)?\n\nFrankly, it started because I wanted to minimize use of Perl on\nWindows (because I can't get around ActiveState Perl at work, and it\nbreaks almost everything it touches). Accidentally, it is also faster\nthere (maybe just because it's smaller).\n\nBut, as was already noted, tr does not behave the same for all\nplatforms (there were even differences in output, BSD or Solaris put\nout a stray LF?).\n\n> Or is your rationale \"this will prevent people from screwing up the test\n> scripts accidentally in the future\"?\n\nWe just can't have that. Nothing can prevent people from screwing up\nanything in any given point of time :)\n\nIn any case, I wont push this change too hard. I must admit, that\nthere is no real good reason besides one \"screwed\" company using\nobsoleted tools in a weird way. And it is a maintenance effort (and\npeople will forget to use test-tr instead of perl and tr).\nAnd maybe someday my employment situation improves and I wont push it\nat all :)\n"},{"id":"79561","messageId":"20080612062309.GA31816@sigill.intra.peff.net","threadId":"13899","inReplyTo":"20080612060152.GA3798@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-06-12T06:23:09Z","receivedAt":"2008-06-12T06:23:09Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 12, 2008 at 08:01:52AM +0200, Alex Riesen wrote:\n\n> Frankly, it started because I wanted to minimize use of Perl on\n> Windows (because I can't get around ActiveState Perl at work, and it\n> breaks almost everything it touches). Accidentally, it is also faster\n> there (maybe just because it's smaller).\n\nAh, right. Well, I am not opposed to getting rid of perl in the test\nscripts (there is core functionality provided by perl, so one can easily\nrun git on a system with no perl at all).\n\nHowever, even with your patch, there is still some perl left, so I am\nnot sure that it has really bought us very much.\n\n> But, as was already noted, tr does not behave the same for all\n> platforms (there were even differences in output, BSD or Solaris put\n> out a stray LF?).\n\nI think those were all resolved by using perl, and your patch replaces\nthem with test-tr.\n\n> > Or is your rationale \"this will prevent people from screwing up the test\n> > scripts accidentally in the future\"?\n> We just can't have that. Nothing can prevent people from screwing up\n> anything in any given point of time :)\n\nHeh. At least we can yell at them after the fact for not using the\navailable tool. :)\n\n> In any case, I wont push this change too hard. I must admit, that\n> there is no real good reason besides one \"screwed\" company using\n> obsoleted tools in a weird way. And it is a maintenance effort (and\n> people will forget to use test-tr instead of perl and tr).\n\nThere is maintenance effort either way; people need to know not to do\nunportable things with tr (and other tools), and the solution to that is\nto run the test scripts on each platform (something we are starting to\ndo).\n\nSo I am not opposed to test-tr, I just wanted you to explain it better\nin the commit log. ;)\n\n-Peff\n"},{"id":"79562","messageId":"20080612062806.GB28696@sigill.intra.peff.net","threadId":"13899","inReplyTo":"20080612062309.GA31816@sigill.intra.peff.net","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-06-12T06:28:06Z","receivedAt":"2008-06-12T06:28:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 12, 2008 at 02:23:09AM -0400, Jeff King wrote:\n\n> Ah, right. Well, I am not opposed to getting rid of perl in the test\n> scripts (there is core functionality provided by perl, so one can easily\n> run git on a system with no perl at all).\n\nEr, sorry. This should read: \"there is _no_ core functionality provide\nby perl\".\n\n-Peff\n"},{"id":"79563","messageId":"556d90580806112332s25f76caajaaafa8e16e895922@mail.gmail.com","threadId":"13899","inReplyTo":"20080611182501.GA3344@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Alf Clement","fromEmail":"alf.clement@gmail.com","sentAt":"2008-06-12T06:32:26Z","receivedAt":"2008-06-12T06:32:26Z","isPatch":true,"sender":{"key":"alf.clement@gmail.com","avatar":null},"body":"Hi Alex,\n\n> +static unsigned char *unquote(const char *s, unsigned *len)\n> +{\n> +       unsigned char *result = malloc(strlen(s)), *r = result;\n\nare you sure that the buffer is big enough?? Nomally you do a\nmalloc(strlen(s)+1).\n\nCU,\nAlf\n"},{"id":"79572","messageId":"alpine.DEB.1.00.0806120822140.1783@racer","threadId":"13899","inReplyTo":"556d90580806112332s25f76caajaaafa8e16e895922@mail.gmail.com","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-06-12T07:25:09Z","receivedAt":"2008-06-12T07:25:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Thu, 12 Jun 2008, Alf Clement wrote:\n\n> > +static unsigned char *unquote(const char *s, unsigned *len) +{\n> > +       unsigned char *result = malloc(strlen(s)), *r = result;\n> \n> are you sure that the buffer is big enough?? Nomally you do a\n> malloc(strlen(s)+1).\n\nAFAIU a \"tr\" really only ever replaces single characters by single \ncharacters.  So, not even looking at the code -- just like you -- I would \nexpect it to get the exact number of bytes read, and to write the same \nnumber of bytes.\n\nI certainly would not expect it to do something string based, introducing \na silly expectation of NUL-terminations (which tr(1) does not expect \neither).\n\nCiao,\nDscho\n"},{"id":"79624","messageId":"20080612202007.GA8057@steel.home","threadId":"13899","inReplyTo":"alpine.DEB.1.00.0806120822140.1783@racer","subject":"[PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-12T20:20:07Z","receivedAt":"2008-06-12T20:20:07Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"It offers a limited set of POSIX tr, in particular: no character class\nsupport and no [n*m] operators. Only 8bit. C-escapes supported, and\ncharacter ranges. Deletion and squeezing should work, but -s does not\nmatch the GNU tr from coreutils (which, in turn, does not match POSIX).\n\nEven if the program does not offers the full tr features, what's left\nis guaranteed to work the same on every platform where you manage to\ncompile it (unless the platform does something really nasty at low\nlevel). Originally written to overcome brokenness of ActiveState Perl\non Windows, it should also improve (assuming it will be used instead\nof local \"tr\" and \"perl -pe y///\") portability of the test suite on\nother platforms.\n\nSigned-off-by: Alex Riesen <raa.lkml@gmail.com>\n---\nJohannes Schindelin, Thu, Jun 12, 2008 09:25:09 +0200:\n> On Thu, 12 Jun 2008, Alf Clement wrote:\n> \n> > > +static unsigned char *unquote(const char *s, unsigned *len) +{\n> > > +       unsigned char *result = malloc(strlen(s)), *r = result;\n> > \n> > are you sure that the buffer is big enough?? Nomally you do a\n> > malloc(strlen(s)+1).\n> \n> AFAIU a \"tr\" really only ever replaces single characters by single \n> characters.  So, not even looking at the code -- just like you -- I would \n> expect it to get the exact number of bytes read, and to write the same \n> number of bytes.\n> \n> I certainly would not expect it to do something string based, introducing \n> a silly expectation of NUL-terminations (which tr(1) does not expect \n> either).\n\nIn any case, Alf is right. The unquote is called unconditionally with\nits arguments, even if there is nothing to unquote. In which case the\nsize of result is off by one. Corrected test-tr.c (also with a\nsensible header) below.\n\n Makefile  |    2 +-\n test-tr.c |  212 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 213 insertions(+), 1 deletions(-)\n create mode 100644 test-tr.c\n\ndiff --git a/Makefile b/Makefile\nindex 1937507..5f208d6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1229,7 +1229,7 @@ endif\n \n ### Testing rules\n \n-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X\n+TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X test-tr$X\n \n all:: $(TEST_PROGRAMS)\n \ndiff --git a/test-tr.c b/test-tr.c\nnew file mode 100644\nindex 0000000..2248535\n--- /dev/null\n+++ b/test-tr.c\n@@ -0,0 +1,212 @@\n+/*\n+ * test-tr  A simplified tr(1) implementation for testing purposes\n+ *\n+ * It offers a limited set of POSIX tr, in particular: no character\n+ * class support and no [n*m] operators. Only 8bit. C-escapes\n+ * supported, and character ranges. Deletion and squeezing should\n+ * work, but -s does not match the GNU tr from coreutils (which, in\n+ * turn, does not match POSIX).\n+ */\n+#include \"cache.h\"\n+\n+static int squeeze, delete;\n+\n+static unsigned char *unquote(const char *s, unsigned *len)\n+{\n+\tunsigned char *result = malloc(strlen(s) + 1), *r = result;\n+\n+\twhile (*s) {\n+\t\tswitch (*s) {\n+\t\tcase '\\\\':\n+\t\t\t++s;\n+#define ISOCT(c) (((c) >= '0' && (c) <= '7'))\n+\t\t\tif (ISOCT(*s)) {\n+\t\t\t\tunsigned int c;\n+\t\t\t\tchar oct[4] = {0,0,0,0};\n+\t\t\t\toct[0] = *s++;\n+\t\t\t\tc = (oct[0] - '0');\n+\t\t\t\tif (ISOCT(*s)) {\n+\t\t\t\t\toct[1] = *s++;\n+\t\t\t\t\tc = (c << 3) |(oct[1] - '0');\n+\t\t\t\t\tif (ISOCT(*s)) {\n+\t\t\t\t\t\toct[2] = *s++;\n+\t\t\t\t\t\tc = (c << 3) |(oct[2] - '0');\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tif (c > 255) {\n+\t\t\t\t\tfprintf(stderr, \"invalid octal character specification: \\\\%s\\n\", oct);\n+\t\t\t\t\texit(1);\n+\t\t\t\t}\n+\t\t\t\t*r++ = c & 0xff;\n+\t\t\t} else {\n+\t\t\t\tswitch (*s) {\n+\t\t\t\tcase '\\0':\n+\t\t\t\t\t*r++ = '\\\\';\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase '\\\\':\n+\t\t\t\t\t*r++ = *s++;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'a':\n+\t\t\t\t\t*r++ = '\\a';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'b':\n+\t\t\t\t\t*r++ = '\\b';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'f':\n+\t\t\t\t\t*r++ = '\\f';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'n':\n+\t\t\t\t\t*r++ = '\\n';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'r':\n+\t\t\t\t\t*r++ = '\\r';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 't':\n+\t\t\t\t\t*r++ = '\\t';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tcase 'v':\n+\t\t\t\t\t*r++ = '\\v';\n+\t\t\t\t\t++s;\n+\t\t\t\t\tbreak;\n+\t\t\t\tdefault:\n+\t\t\t\t\t*r++ = '\\\\';\n+\t\t\t\t\t*r++ = *s++;\n+\t\t\t\t\tbreak;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t*r++ = *s++;\n+\t\t}\n+\t}\n+\n+\t*len = r - result;\n+\t*r = '\\0';\n+\treturn result;\n+}\n+\n+#define MAX_PATTERN 256\n+static void put_op(unsigned char *conv, unsigned char ch, unsigned *len)\n+{\n+\tunsigned i = (*len)++;\n+\tif (*len > MAX_PATTERN) {\n+\t\tfprintf(stderr, \"pattern too long\\n\");\n+\t\texit(1);\n+\t}\n+\tconv[i] = ch;\n+}\n+\n+static void parse(const unsigned char *rule, unsigned rule_len,\n+\t\t  unsigned char *set, unsigned *set_len)\n+{\n+\tconst unsigned char *p = rule;\n+\twhile (p < rule + rule_len) {\n+\t\tif ('-' == *p && p > rule && p[1]) {\n+\t\t\tunsigned c;\n+\t\t\tif (p[-1] > p[1]) {\n+\t\t\t\tfprintf(stderr, \"%c%c%c: range is reversed\\n\",\n+\t\t\t\t\tp[-1], *p, p[1]);\n+\t\t\t\texit(1);\n+\t\t\t}\n+\t\t\tc = p[-1] + 1u;\n+\t\t\tfor (; c <= p[1]; ++c)\n+\t\t\t\tput_op(set, c, set_len);\n+\t\t\t++p;\n+\t\t\t++p;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tput_op(set, *p, set_len);\n+\t\t++p;\n+\t}\n+}\n+\n+int main(int argc, char *argv[])\n+{\n+\tunsigned set1_len = 0, set2_len = 0;\n+\tunsigned char set1[MAX_PATTERN];\n+\tunsigned char set2[MAX_PATTERN];\n+\n+\tssize_t n;\n+\tunsigned char last = 0, have_last = 0;\n+\tunsigned char buf[BUFSIZ];\n+\n+\tchar *rule1 = NULL, *rule2 = NULL;\n+\tunsigned char *urule1, *urule2;\n+\tunsigned urule1_len, urule2_len;\n+\tint opt;\n+\n+\tfor (opt = 1; opt < argc; ++opt) {\n+\t\tif (!strcmp(\"-s\", argv[opt]))\n+\t\t\tsqueeze = 1;\n+\t\telse if (!strcmp(\"-d\", argv[opt]))\n+\t\t\tdelete = 1;\n+\t\telse if (!rule1) {\n+\t\t\trule1 = argv[opt];\n+\t\t} else if (!rule2)\n+\t\t\trule2 = argv[opt];\n+\t}\n+\tif (!rule1) {\n+\t    fprintf(stderr, \"no source set given\\n\"\n+\t\t    \"test-tr [-s] [-d] set1 [set2]\\n\"\n+\t\t    \"\\\"set\\\" supports only \\\\NNN, \\\\a-\\\\v and CHAR1-CHAR2 rules\\n\");\n+\t    exit(1);\n+\t}\n+\tif (delete && rule2) {\n+\t\tfprintf(stderr, \"extra operand %s when deleting\\n\", rule2);\n+\t\texit(1);\n+\t}\n+\turule1 = unquote(rule1, &urule1_len);\n+\turule2 = NULL;\n+\turule2_len = 0;\n+\tif ((!rule2 || !*rule2) && !delete && !squeeze) {\n+\t\tfprintf(stderr, \"set2 must be non-empty\\n\");\n+\t\texit(1);\n+\t}\n+\n+\tparse(urule1, urule1_len, set1, &set1_len);\n+\n+\tif (rule2) {\n+\t\tunsigned i;\n+\t\turule2 = unquote(rule2, &urule2_len);\n+\t\tparse(urule2, urule2_len, set2, &set2_len);\n+\t\ti = set2[set2_len - 1];\n+\t\twhile (set2_len < set1_len)\n+\t\t\tput_op(set2, i, &set2_len);\n+\t}\n+\n+\twhile ((n = read(STDIN_FILENO, buf, sizeof(buf)))) {\n+\t\tif (n < 0) {\n+\t\t\tint err = errno;\n+\t\t\tif (EINTR == err || EAGAIN == err)\n+\t\t\t\tcontinue;\n+\t\t\tfprintf(stderr, \"%s: %s\\n\", argv[0], strerror(err));\n+\t\t\texit(1);\n+\t\t}\n+\t\tif (set1_len) {\n+\t\t\tunsigned i, o = 0;\n+\t\t\tfor (i = 0; i < (unsigned)n; ++i) {\n+\t\t\t\tunsigned char *p, ch = buf[i];\n+\t\t\t\tp = memchr(set1, ch, set1_len);\n+\t\t\t\tif (p) {\n+\t\t\t\t\tif (delete)\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\tif (set2_len)\n+\t\t\t\t\t\tch = set2[p - set1];\n+\t\t\t\t}\n+\t\t\t\tif (!(squeeze && have_last && ch == last))\n+\t\t\t\t\tbuf[o++] = ch;\n+\t\t\t\thave_last = 1;\n+\t\t\t\tlast = ch;\n+\t\t\t}\n+\t\t\tn = o;\n+\t\t}\n+\t\twrite(STDOUT_FILENO, buf, n);\n+\t}\n+\treturn 0;\n+}\n-- \n1.5.6.rc2.43.g77bfa\n"},{"id":"79628","messageId":"20080612203245.GB8057@steel.home","threadId":"13899","inReplyTo":"20080612062309.GA31816@sigill.intra.peff.net","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-12T20:32:45Z","receivedAt":"2008-06-12T20:32:45Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Jeff King, Thu, Jun 12, 2008 08:23:09 +0200:\n> On Thu, Jun 12, 2008 at 08:01:52AM +0200, Alex Riesen wrote:\n> \n> > Frankly, it started because I wanted to minimize use of Perl on\n> > Windows (because I can't get around ActiveState Perl at work, and it\n> > breaks almost everything it touches). Accidentally, it is also faster\n> > there (maybe just because it's smaller).\n> \n> Ah, right. Well, I am not opposed to getting rid of perl in the test\n> scripts (there is core functionality provided by perl, so one can easily\n> run git on a system with no perl at all).\n\n\"git add --interactive\" and \"git send-mail\". The first has a very good\nreplacement (git gui). The other, even if not that \"core\", is a very\npopular tool. (And I can't use it at work at all: noone to understand\nword \"patch\" there. And even then, noone would care about change\nreview anyway).\n\n> However, even with your patch, there is still some perl left, so I am\n> not sure that it has really bought us very much.\n\nI was forced to carefully check every instance and am positive that\nthe others are ok with regard to ActiveState Perl and Windows quirks :)\n(IOW, I run the testsuite every day and it aint broke yet)\n\n> > But, as was already noted, tr does not behave the same for all\n> > platforms (there were even differences in output, BSD or Solaris put\n> > out a stray LF?).\n> \n> I think those were all resolved by using perl, and your patch replaces\n> them with test-tr.\n\nThat was the problem. ActiveState Perl always replaces LF in the\noutput with CRLF, which caused mismatches with template files in some\ntests (even the generated templates had LF line endings, cygwins tools\nfollow that convention). At first, I tried to get by putting\n\"binmode(STDOUT)\" into every test, but this became boring with a time.\nBesides, the lines get very long and ugly (and make conflict resolving\nharder).\n\n> > In any case, I wont push this change too hard. I must admit, that\n> > there is no real good reason besides one \"screwed\" company using\n> > obsoleted tools in a weird way. And it is a maintenance effort (and\n> > people will forget to use test-tr instead of perl and tr).\n> \n> There is maintenance effort either way; people need to know not to do\n> unportable things with tr (and other tools), and the solution to that is\n> to run the test scripts on each platform (something we are starting to\n> do).\n\nWith test-tr they still have to do all that, but also support its\nexistence. So I'm not sure.\n\n> So I am not opposed to test-tr, I just wanted you to explain it better\n> in the commit log. ;)\n\nI already tried.\n"},{"id":"79679","messageId":"20080613060215.GB26768@sigill.intra.peff.net","threadId":"13899","inReplyTo":"20080612203245.GB8057@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-06-13T06:02:15Z","receivedAt":"2008-06-13T06:02:15Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Jun 12, 2008 at 10:32:45PM +0200, Alex Riesen wrote:\n\n> That was the problem. ActiveState Perl always replaces LF in the\n> output with CRLF, which caused mismatches with template files in some\n> tests (even the generated templates had LF line endings, cygwins tools\n> follow that convention). At first, I tried to get by putting\n> \"binmode(STDOUT)\" into every test, but this became boring with a time.\n> Besides, the lines get very long and ugly (and make conflict resolving\n> harder).\n\nDoes ActiveState respect the PERLIO environment variable? I haven't\nplayed with it much, but my understanding is that setting\nPERLIO=:unix:perlio should give you sane behavior (the default on\nWindows should be PERLIO=:unix:crlf).\n\n> > So I am not opposed to test-tr, I just wanted you to explain it better\n> > in the commit log. ;)\n> \n> I already tried.\n\nI think this thread and the improved commit message have helped.\n\n-Peff\n"},{"id":"79744","messageId":"20080613172634.GB5671@steel.home","threadId":"13899","inReplyTo":"20080613060215.GB26768@sigill.intra.peff.net","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Alex Riesen","fromEmail":"raa.lkml@gmail.com","sentAt":"2008-06-13T17:26:34Z","receivedAt":"2008-06-13T17:26:34Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Jeff King, Fri, Jun 13, 2008 08:02:15 +0200:\n> On Thu, Jun 12, 2008 at 10:32:45PM +0200, Alex Riesen wrote:\n> \n> > That was the problem. ActiveState Perl always replaces LF in the\n> > output with CRLF, which caused mismatches with template files in some\n> > tests (even the generated templates had LF line endings, cygwins tools\n> > follow that convention). At first, I tried to get by putting\n> > \"binmode(STDOUT)\" into every test, but this became boring with a time.\n> > Besides, the lines get very long and ugly (and make conflict resolving\n> > harder).\n> \n> Does ActiveState respect the PERLIO environment variable? I haven't\n> played with it much, but my understanding is that setting\n> PERLIO=:unix:perlio should give you sane behavior (the default on\n> Windows should be PERLIO=:unix:crlf).\n\nI didn't know about it (and managed to miss it when I looked to\nworkaround the problem. I even looked into the manpage documenting\nenvironment variables!)\n\nYes, it works. It has side effects (the scripts of some build\nprocesses will be affected), but that's already something...\n"},{"id":"79747","messageId":"20080613180041.GA8921@sigill.intra.peff.net","threadId":"13899","inReplyTo":"20080613172634.GB5671@steel.home","subject":"Re: [PATCH] Add test-tr: poor-man tr","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2008-06-13T18:00:41Z","receivedAt":"2008-06-13T18:00:41Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Jun 13, 2008 at 07:26:34PM +0200, Alex Riesen wrote:\n\n> > Does ActiveState respect the PERLIO environment variable? I haven't\n> > played with it much, but my understanding is that setting\n> > PERLIO=:unix:perlio should give you sane behavior (the default on\n> > Windows should be PERLIO=:unix:crlf).\n> \n> I didn't know about it (and managed to miss it when I looked to\n> workaround the problem. I even looked into the manpage documenting\n> environment variables!)\n> \n> Yes, it works. It has side effects (the scripts of some build\n> processes will be affected), but that's already something...\n\nI was thinking that we could set it inside test-lib.sh to get consistent\nbehavior. It should just be a no-op on sane systems.\n\n-Peff\n"}]}