{"thread":{"id":"4905","subject":"[PATCH 1/2] add git-quote: shell and C quoting tool","startedAt":"2006-07-16T22:27:57Z","lastAt":"2006-07-24T23:12:59Z","messageCount":3,"participants":["Alex Riesen","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"23883","messageId":"20060716222757.GA10135@steel.home","threadId":"4905","inReplyTo":null,"subject":"[PATCH 1/2] add git-quote: shell and C quoting tool","fromName":"Alex Riesen","fromEmail":"fork0@t-online.de","sentAt":"2006-07-16T22:27:57Z","receivedAt":"2006-07-16T22:27:57Z","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\nAs git-stripspace, it may ne useful for something. As an example, the\nnext patch converts git-bisect.sh to use of this tool.\n\nIn case anyone asks why isn't it a standalone tool nor is it put into\ngit-stripspace: I don't know. Maybe it should be.\n\n Makefile        |    3 +-\n builtin-quote.c |  102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h       |    2 +\n git.c           |    1 +\n 4 files changed, 107 insertions(+), 1 deletions(-)\n\ndiff --git a/Makefile b/Makefile\nindex 01fb9cf..9fcbf3b 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -234,7 +234,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-apply.o builtin-show-branch.o builtin-diff-files.o \\\n \tbuiltin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \\\n \tbuiltin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \\\n-\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o\n+\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \\\n+\tbuiltin-quote.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-quote.c b/builtin-quote.c\nnew file mode 100644\nindex 0000000..cbd822d\n--- /dev/null\n+++ b/builtin-quote.c\n@@ -0,0 +1,102 @@\n+/*\n+ * \"git quote\" builtin command\n+ *\n+ * DOES NOT QUOTE \\0 (truncates lines at it)\n+ */\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"quote.h\"\n+\n+enum {SHELL_QUOTE, C_QUOTE};\n+static int style = SHELL_QUOTE,\n+\t   use_stdin = 0;\n+static const char *separator = NULL; /* default is space */\n+static unsigned sep_len = 0;\n+\n+static const char builtin_quote_usage[] =\n+\"git-quote [--c] [--sep=<c-quoted> | -z] ( [--stdin] | [--] ... )\";\n+\n+static void print_quoted(const char *text)\n+{\n+\tswitch (style)\n+\t{\n+\tcase SHELL_QUOTE:\n+\t\tsq_quote_print(stdout, text);\n+\t\tbreak;\n+\tcase C_QUOTE:\n+\t\tquote_c_style(text, NULL, stdout, 0);\n+\t\tbreak;\n+\t}\n+\tfwrite(separator, 1, sep_len, stdout);\n+}\n+\n+int cmd_quote(int argc, const char **argv, char **envp)\n+{\n+\tint i;\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (arg[0] != '-')\n+\t\t\tbreak;\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--stdin\")) {\n+\t\t\tuse_stdin = 1;\n+\t\t\tif ( !separator ) {\n+\t\t\t\tseparator = \"\\n\";\n+\t\t\t\tsep_len = 1;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--c\")) {\n+\t\t\tstyle = C_QUOTE;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-z\")) {\n+\t\t\tseparator = \"\";\n+\t\t\tsep_len = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--sep=\", 6)) {\n+\t\t\tconst char *end;\n+\t\t\tchar *tmp;\n+\t\t\targ += 6;\n+\t\t\tif ('\"' == *arg)\n+\t\t\t\ttmp = strdup(arg);\n+\t\t\telse {\n+\t\t\t\tsize_t l = strlen(arg);\n+\t\t\t\ttmp = malloc(l + 3);\n+\t\t\t\tsprintf(tmp, \"\\\"%s\\\"\", arg);\n+\t\t\t}\n+\t\t\tseparator = unquote_c_style(tmp, &end);\n+\t\t\tsep_len = strlen(separator);\n+\t\t\t/* this will leak if multiple --sep= given */\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(builtin_quote_usage);\n+\t}\n+\tif (!separator) {\n+\t\tsep_len = 1;\n+\t\tseparator = \"\\x20\";\n+\t}\n+\tif (use_stdin) {\n+\t\tsize_t size = BUFSIZ;\n+\t\tchar *buf = xmalloc(size);\n+\t\tint ch, pos = 0;\n+\t\twhile (EOF != (ch = fgetc(stdin))) {\n+\t\t\tif (pos == size)\n+\t\t\t\tbuf = xrealloc(buf, size <<= 1);\n+\t\t\tbuf[pos++] = ch;\n+\t\t\tif ('\\n' == ch) {\n+\t\t\t\tbuf[--pos] = '\\0';\n+\t\t\t\tpos = 0;\n+\t\t\t\tprint_quoted(buf);\n+\t\t\t}\n+\t\t}\n+\t} else\n+\t\tfor (; argv[i]; ++i)\n+\t\t\tprint_quoted(argv[i]);\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 5339d86..9bd522e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -64,4 +64,6 @@ extern int mailinfo(FILE *in, FILE *out,\n \n extern int cmd_stripspace(int argc, const char **argv, char **envp);\n extern void stripspace(FILE *in, FILE *out);\n+\n+extern int cmd_quote(int argc, const char **argv, char **envp);\n #endif\ndiff --git a/git.c b/git.c\nindex ee5a0e8..f94d25a 100644\n--- a/git.c\n+++ b/git.c\n@@ -202,6 +202,7 @@ static void handle_internal_command(int \n \t\t{ \"update-ref\", cmd_update_ref },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg },\n \t\t{ \"prune\", cmd_prune },\n+\t\t{ \"quote\", cmd_quote },\n \t};\n \tint i;\n \n-- \n1.4.1.gb944\n"},{"id":"24035","messageId":"7vwta3h7r2.fsf@assigned-by-dhcp.cox.net","threadId":"4905","inReplyTo":"20060716222757.GA10135@steel.home","subject":"Re: [PATCH 1/2] add git-quote: shell and C quoting tool","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-07-24T05:49:53Z","receivedAt":"2006-07-24T05:49:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"fork0@t-online.de (Alex Riesen) writes:\n\n> In case anyone asks why isn't it a standalone tool nor is it put into\n> git-stripspace: I don't know. Maybe it should be.\n\nThis organization is fine by me.\n\n * unquote?\n * doc?\n\n> +enum {SHELL_QUOTE, C_QUOTE};\n> +static int style = SHELL_QUOTE,\n> +\t   use_stdin = 0;\n\nShouldn't style be of type which is that unnamed enum?\n\n> +static const char *separator = NULL; /* default is space */\n> +static unsigned sep_len = 0;\n\nIt is not clear if this is separator of input side or output\nside (after reading the code it becomes somewhat obvious that\nyou are talking about output separator), and if the default is\nspace wouldn't it make more sense to set that here?\n\n> +static const char builtin_quote_usage[] =\n> +\"git-quote [--c] [--sep=<c-quoted> | -z] ( [--stdin] | [--] ... )\";\n> +\n> +static void print_quoted(const char *text)\n> +{\n> +\tswitch (style)\n> +\t{\n> +\tcase SHELL_QUOTE:\n> +\t\tsq_quote_print(stdout, text);\n> +\t\tbreak;\n> +\tcase C_QUOTE:\n> +\t\tquote_c_style(text, NULL, stdout, 0);\n> +\t\tbreak;\n> +\t}\n\nNot a big deal but aren't these going to write out things quoted\nwhen they do not need to?  To help scripts, it might be worth\nadding \"check if this needs quoting\" interface perhaps?\n\n> +int cmd_quote(int argc, const char **argv, char **envp)\n> +{\n> +\tint i;\n> +\tfor (i = 1; i < argc; i++) {\n> +\t\tconst char *arg = argv[i];\n> +\n> +\t\tif (arg[0] != '-')\n> +\t\t\tbreak;\n> +\t\tif (!strcmp(arg, \"--\")) {\n> +\t\t\ti++;\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\tif (!strcmp(arg, \"--stdin\")) {\n> +\t\t\tuse_stdin = 1;\n> +\t\t\tif ( !separator ) {\n> +\t\t\t\tseparator = \"\\n\";\n> +\t\t\t\tsep_len = 1;\n> +\t\t\t}\n> +\t\t\tbreak;\n> +\t\t}\n> +\t\tif (!strcmp(arg, \"--c\")) {\n\nPerhaps \"--c-style\" with \"-c\" as a shorthand, (and\n\"--shell-style\" with \"-s\" to complement)?\n\n> +\t\t\tstyle = C_QUOTE;\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tif (!strcmp(arg, \"-z\")) {\n> +\t\t\tseparator = \"\";\n> +\t\t\tsep_len = 1;\n> +\t\t\tcontinue;\n> +\t\t}\n\nIs it plausible that somebody might want to feed you NUL\nterminated sequence as input (iow you might want to give them\nchoice of separator on the input side)?\n\n> +\t\tif (!strncmp(arg, \"--sep=\", 6)) {\n\nPerhaps \"--separator\" (or \"--output-separator\") with whatever\nshorthand is handy?\n\n> +\t\t\tconst char *end;\n> +\t\t\tchar *tmp;\n> +\t\t\targ += 6;\n> +\t\t\tif ('\"' == *arg)\n> +\t\t\t\ttmp = strdup(arg);\n> +\t\t\telse {\n> +\t\t\t\tsize_t l = strlen(arg);\n> +\t\t\t\ttmp = malloc(l + 3);\n> +\t\t\t\tsprintf(tmp, \"\\\"%s\\\"\", arg);\n> +\t\t\t}\n> +\t\t\tseparator = unquote_c_style(tmp, &end);\n> +\t\t\tsep_len = strlen(separator);\n\nIf the parameter is a malformed c-quoted string, you would dump\ncore here.\n\n> +\t\t\t/* this will leak if multiple --sep= given */\n> +\t\t\tcontinue;\n> +\t\t}\n> +\t\tdie(builtin_quote_usage);\n> +\t}\n> +\tif (!separator) {\n> +\t\tsep_len = 1;\n> +\t\tseparator = \"\\x20\";\n\nAny reason you needed to spell it in hex?  Is this trying to\nbe portable to EBCDIC (or trying to prevent the code to be\nported there)?  I dunno.\n\n> +\t}\n> +\tif (use_stdin) {\n> +\t\tsize_t size = BUFSIZ;\n> +\t\tchar *buf = xmalloc(size);\n> +\t\tint ch, pos = 0;\n> +\t\twhile (EOF != (ch = fgetc(stdin))) {\n> +\t\t\tif (pos == size)\n> +\t\t\t\tbuf = xrealloc(buf, size <<= 1);\n> +\t\t\tbuf[pos++] = ch;\n> +\t\t\tif ('\\n' == ch) {\n> +\t\t\t\tbuf[--pos] = '\\0';\n> +\t\t\t\tpos = 0;\n> +\t\t\t\tprint_quoted(buf);\n> +\t\t\t}\n> +\t\t}\n\nLooks similar to strbuf.c::read_line() loop...\n"},{"id":"24074","messageId":"20060724231259.GA14792@steel.home","threadId":"4905","inReplyTo":"7vwta3h7r2.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] add git-quote: shell and C quoting tool","fromName":"Alex Riesen","fromEmail":"fork0@t-online.de","sentAt":"2006-07-24T23:12:59Z","receivedAt":"2006-07-24T23:12:59Z","isPatch":true,"sender":{"key":"raa.lkml@gmail.com","avatar":"https://avatars.githubusercontent.com/u/324101?v=4"},"body":"Junio C Hamano, Mon, Jul 24, 2006 07:49:53 +0200:\n> > In case anyone asks why isn't it a standalone tool nor is it put into\n> > git-stripspace: I don't know. Maybe it should be.\n> \n> This organization is fine by me.\n> \n>  * unquote?\n\nNot done yet. I actually never expected it to be used for unquoting,\nbeing naturally unquoted by bash. C-quoting was done just because it\nwas so simple to do. I have no idea where could that be used.\n\n>  * doc?\n\nDone. Sorry for missing that.\n\n> > +enum {SHELL_QUOTE, C_QUOTE};\n> > +static int style = SHELL_QUOTE,\n> > +\t   use_stdin = 0;\n> \n> Shouldn't style be of type which is that unnamed enum?\n\nIt should. Done.\n\n> > +static const char *separator = NULL; /* default is space */\n> > +static unsigned sep_len = 0;\n> \n> It is not clear if this is separator of input side or output\n> side (after reading the code it becomes somewhat obvious that\n> you are talking about output separator), and if the default is\n> space wouldn't it make more sense to set that here?\n\nRight. It is out_separator now.\n\n> > +static const char builtin_quote_usage[] =\n> > +\"git-quote [--c] [--sep=<c-quoted> | -z] ( [--stdin] | [--] ... )\";\n> > +\n> > +static void print_quoted(const char *text)\n> > +{\n> > +\tswitch (style)\n> > +\t{\n> > +\tcase SHELL_QUOTE:\n> > +\t\tsq_quote_print(stdout, text);\n> > +\t\tbreak;\n> > +\tcase C_QUOTE:\n> > +\t\tquote_c_style(text, NULL, stdout, 0);\n> > +\t\tbreak;\n> > +\t}\n> \n> Not a big deal but aren't these going to write out things quoted\n> when they do not need to?  To help scripts, it might be worth\n> adding \"check if this needs quoting\" interface perhaps?\n\nNot done yet. \"--no-overquote\"?\n\n> > +\t\tif (!strcmp(arg, \"--c\")) {\n> \n> Perhaps \"--c-style\" with \"-c\" as a shorthand, (and\n> \"--shell-style\" with \"-s\" to complement)?\n\nDone. --sh-style.\n\n> > +\t\t\tstyle = C_QUOTE;\n> > +\t\t\tcontinue;\n> > +\t\t}\n> > +\t\tif (!strcmp(arg, \"-z\")) {\n> > +\t\t\tseparator = \"\";\n> > +\t\t\tsep_len = 1;\n> > +\t\t\tcontinue;\n> > +\t\t}\n> \n> Is it plausible that somebody might want to feed you NUL\n> terminated sequence as input (iow you might want to give them\n> choice of separator on the input side)?\n\nDone. -z sets both separators to NUL, --in-separator=<c-style> defines\nby its first character the input separator. I.e.:\n\n$ find | git quote -z --in-separator='\\n'\n'a'\\0'b'\\0''\n\nwhich is not all that useful...\n\n> > +\t\tif (!strncmp(arg, \"--sep=\", 6)) {\n> \n> Perhaps \"--separator\" (or \"--output-separator\") with whatever\n> shorthand is handy?\n\n--out-separator. No idea of a shortcut, though\n\n> > +\t\t\tseparator = unquote_c_style(tmp, &end);\n> > +\t\t\tsep_len = strlen(separator);\n> \n> If the parameter is a malformed c-quoted string, you would dump\n> core here.\n\nOops. Fixed.\n\n> > +\tif (!separator) {\n> > +\t\tsep_len = 1;\n> > +\t\tseparator = \"\\x20\";\n> \n> Any reason you needed to spell it in hex?  Is this trying to\n> be portable to EBCDIC (or trying to prevent the code to be\n> ported there)?  I dunno.\n\nVisibility. I have dificulty seeing spaces and tabs.\nI changed it anyway, maybe that's just me.\n\n> > +\t\twhile (EOF != (ch = fgetc(stdin))) {\n> \n> Looks similar to strbuf.c::read_line() loop...\n> \n\nIndeed. Replaced.\n\nI attach the updated patch here, because it is obviously is not quite\nfinished yet and I probably wont have enough time over the week. So\nmaybe someone takes over. Or it'll be taken as is, that happens too.\n\nSigned-off-by: Alex Riesen <raa.lkml@gmail.com>\n\n---\n\ndiff --git a/Documentation/git-quote.txt b/Documentation/git-quote.txt\nnew file mode 100644\nindex 0000000..98b2859\n--- /dev/null\n+++ b/Documentation/git-quote.txt\n@@ -0,0 +1,70 @@\n+git-quote(1)\n+============\n+\n+NAME\n+----\n+git-quote - Quote given text according to Shell or C rules\n+\n+\n+SYNOPSIS\n+--------\n+'git-quote' [-c] [-s] [-s] [--out-separator=<c-quoted>] \n+\t[--in-separator=<c-quoted>] [-z] [--] ...\n+'git-quote' ... --stdin < <stream>\n+\n+DESCRIPTION\n+-----------\n+Quote the lines according to the given rules, which can be Shell backslash\n+quoting (every line is enclosed in single ticks, backslash and single tick\n+quoted specially) or a C quoting (use double ticks and backslash-prefixed\n+symbols).\n+\n+The separator options are handled in the order they are given, so one\n+can save some typing by giving \"-z\" first, and overriding input or\n+output separator as needed.\n+\n+OPTIONS\n+-------\n+-c|--c-style::\n+\tSelect C-style quoting\n+\n+-s|--sh-style::\n+\tSelect Shell-style quoting. This is default.\n+\n+--out-separator=<c-quoted>::\n+\tSpecify the separator between the output elements. If the separator\n+\thave to contain special characters, they must be quoted according\n+\tto C quoting rules (double ticks are not essential).\n+\tThe ASCII space is default output separator.\n+\n+--in-separator=<c-quoted>::\n+\tSpecify the separator between the input elements. Only the\n+\tfirst character of <c-quoted> is used.\n+\tUsed only if --stdin is also given. The default is LF.\n+\n+-z::\n+\tSeparate the elements on output and expect elements separated\n+\ton input with NUL character.\n+\tUsed only if --stdin is also given.\n+\n+--stdin::\n+\tSpecify that the elements have to be read from stdin.\n+\tThis option ends command line parsing.\n+\tAlso, if there were no output separator set by previous\n+\t--out-separator or -z, this option will set it to LF.\n+\n+<stream>::\n+\tByte stream to act on.\n+\n+Author\n+------\n+Written by Alex Riesen <raa.lkml@gmail.com>\n+\n+Documentation\n+--------------\n+Documentation by Alex Riesen.\n+\n+GIT\n+---\n+Part of the gitlink:git[7] suite\n+\ndiff --git a/Makefile b/Makefile\nindex 3e085df..4df2951 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -246,7 +246,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-apply.o builtin-show-branch.o builtin-diff-files.o \\\n \tbuiltin-diff-index.o builtin-diff-stages.o builtin-diff-tree.o \\\n \tbuiltin-cat-file.o builtin-mailsplit.o builtin-stripspace.o \\\n-\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o\n+\tbuiltin-update-ref.o builtin-fmt-merge-msg.o builtin-prune.o \\\n+\tbuiltin-quote.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-quote.c b/builtin-quote.c\nnew file mode 100644\nindex 0000000..d9d4b00\n--- /dev/null\n+++ b/builtin-quote.c\n@@ -0,0 +1,123 @@\n+/*\n+ * \"git quote\" builtin command\n+ *\n+ * DOES NOT QUOTE \\0 (truncates lines at it)\n+ */\n+#include \"cache.h\"\n+#include \"builtin.h\"\n+#include \"strbuf.h\"\n+#include \"quote.h\"\n+\n+static enum {SHELL_QUOTE, C_QUOTE} style = SHELL_QUOTE;\n+static int use_stdin = 0;\n+static const char *out_separator = NULL; /* default is space */\n+static unsigned out_separator_len = 0;\n+\n+static const char builtin_quote_usage[] =\n+\"git-quote [-c | -s] [--out-separator=<c-quoted> | -z] ( [--stdin] | [--] ... )\";\n+\n+static void print_quoted(const char *text)\n+{\n+\tswitch (style)\n+\t{\n+\tcase SHELL_QUOTE:\n+\t\tsq_quote_print(stdout, text);\n+\t\tbreak;\n+\tcase C_QUOTE:\n+\t\tquote_c_style(text, NULL, stdout, 0);\n+\t\tbreak;\n+\t}\n+\tfwrite(out_separator, 1, out_separator_len, stdout);\n+}\n+\n+static char *unquote_c_arg(const char *arg)\n+{\n+\tconst char *end;\n+\tchar *tmp;\n+\tchar *result;\n+\tif ('\"' == *arg)\n+\t\ttmp = strdup(arg);\n+\telse {\n+\t\tsize_t l = strlen(arg);\n+\t\ttmp = malloc(l + 3);\n+\t\tsprintf(tmp, \"\\\"%s\\\"\", arg);\n+\t}\n+\tresult = unquote_c_style(tmp, &end);\n+\tfree(tmp);\n+\treturn result;\n+}\n+\n+int cmd_quote(int argc, const char **argv, char **envp)\n+{\n+\tint in_separator = '\\n';\n+\tint i;\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\n+\t\tif (arg[0] != '-')\n+\t\t\tbreak;\n+\t\tif (!strcmp(arg, \"--\")) {\n+\t\t\ti++;\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--stdin\")) {\n+\t\t\tuse_stdin = 1;\n+\t\t\tif ( !out_separator ) {\n+\t\t\t\tout_separator = \"\\n\";\n+\t\t\t\tout_separator_len = 1;\n+\t\t\t}\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--c-style\") || !strcmp(arg, \"-c\")) {\n+\t\t\tstyle = C_QUOTE;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"--sh-style\") || !strcmp(arg, \"-s\")) {\n+\t\t\tstyle = SHELL_QUOTE;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(arg, \"-z\")) {\n+\t\t\tin_separator = 0;\n+\t\t\tout_separator = \"\";\n+\t\t\tout_separator_len = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--out-separator=\", 16)) {\n+\t\t\targ += 16;\n+\t\t\tout_separator = unquote_c_arg(arg);\n+\t\t\tif (!out_separator)\n+\t\t\t\tdie(\"malformed separator '%s'\", arg);\n+\t\t\tout_separator_len = strlen(out_separator);\n+\t\t\t/* this will leak if multiple --sep= given */\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strncmp(arg, \"--in-separator=\", 15)) {\n+\t\t\tchar *sep;\n+\t\t\targ += 15;\n+\t\t\tsep = unquote_c_arg(arg);\n+\t\t\tif (!sep || !*sep)\n+\t\t\t\tdie(\"malformed separator '%s'\", arg);\n+\t\t\tin_separator = *sep;\n+\t\t\tfree(sep);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(builtin_quote_usage);\n+\t}\n+\tif (!out_separator) {\n+\t\tout_separator_len = 1;\n+\t\tout_separator = \" \";\n+\t}\n+\tif (use_stdin) {\n+\t\tstruct strbuf line;\n+\t\tstrbuf_init(&line);\n+\n+\t\twhile (!line.eof) {\n+\t\t\tread_line(&line, stdin, in_separator);\n+\t\t\tif (line.len)\n+\t\t\t\tprint_quoted(line.buf);\n+\t\t}\n+\t} else\n+\t\tfor (; argv[i]; ++i)\n+\t\t\tprint_quoted(argv[i]);\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 5339d86..9bd522e 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -64,4 +64,6 @@ extern int mailinfo(FILE *in, FILE *out,\n \n extern int cmd_stripspace(int argc, const char **argv, char **envp);\n extern void stripspace(FILE *in, FILE *out);\n+\n+extern int cmd_quote(int argc, const char **argv, char **envp);\n #endif\ndiff --git a/git.c b/git.c\nindex ee5a0e8..f94d25a 100644\n--- a/git.c\n+++ b/git.c\n@@ -202,6 +202,7 @@ static void handle_internal_command(int \n \t\t{ \"update-ref\", cmd_update_ref },\n \t\t{ \"fmt-merge-msg\", cmd_fmt_merge_msg },\n \t\t{ \"prune\", cmd_prune },\n+\t\t{ \"quote\", cmd_quote },\n \t};\n \tint i;\n \n"}]}