{"thread":{"id":"23423","subject":"[RFC/PATCH v2 2/4] ls-tree: complete conversion to using output library","startedAt":"2010-04-11T23:21:13Z","lastAt":"2010-04-19T19:40:20Z","messageCount":28,"participants":["Julian Phillips","Sverre Rabbelier","Eric Raymond","Ilari Liusvaara","Jakub Narebski","Junio C Hamano","Jeff King"],"isPatch":true,"patchVersion":2,"patchTotal":4},"messages":[{"id":"139296","messageId":"20100411231824.67460.24844.julian@quantumfyre.co.uk","threadId":"23423","inReplyTo":null,"subject":"[RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-11T23:21:13Z","receivedAt":"2010-04-11T23:21:13Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"Ok, round 2 of an attempt at making a format agnostic structured output library.\nThe idea being that the command doing the output doesn't have to care what the\nactual output format is, it uses one set of output functions and the user gets\nto choose their preferred output style.\n\nThe current backend/frontend interface probably needs expanding so that a less\nnoddy XML output can be used, but it's a start.\n\nRather than building an in-memory structure and then writing it out I've gone\nfor the approach of writing out immediately.  The thought behind this was that I\ndidn't really want to force commands like log to have to wait 'til the end to\nstart outputting information (though I still haven't got around to working on\nconverting log).\n\nProbably the biggest change from v1 is an expanded aim.  Now the output library\nis aimed at controlling _all_ plubming output.  This series includes a patch for\nls-tree that has all it's output going through the library, and a patch for\nstatus that has all the --porcelain output going through the library.\n\nThe XML patch still needs a lot of work, as I've been busy playing with the\nlibrary API and the NORMAL/ZERO backends ...\n\nJulian Phillips (4):\n  output: Add a new library for plumbing output\n  ls-tree: complete conversion to using output library\n  status: use output library for porcelain output\n  output: WIP: Add XML backend\n\n Documentation/technical/api-output.txt |  116 ++++++++++++++\n Makefile                               |    5 +\n builtin/commit.c                       |   21 +++-\n builtin/ls-tree.c                      |   51 ++++--\n output-json.c                          |  127 +++++++++++++++\n output-normal.c                        |   95 +++++++++++\n output-xml.c                           |   68 ++++++++\n output-zero.c                          |   74 +++++++++\n output.c                               |  270 ++++++++++++++++++++++++++++++++\n output.h                               |   93 +++++++++++\n wt-status.c                            |   88 ++++++++++-\n wt-status.h                            |    3 +-\n 12 files changed, 985 insertions(+), 26 deletions(-)\n create mode 100644 Documentation/technical/api-output.txt\n create mode 100644 output-json.c\n create mode 100644 output-normal.c\n create mode 100644 output-xml.c\n create mode 100644 output-zero.c\n create mode 100644 output.c\n create mode 100644 output.h\n"},{"id":"139299","messageId":"20100411232118.67460.52907.julian@quantumfyre.co.uk","threadId":"23423","inReplyTo":"20100411231824.67460.24844.julian@quantumfyre.co.uk","subject":"[RFC/PATCH v2 1/4] output: Add a new library for plumbing output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-11T23:21:14Z","receivedAt":"2010-04-11T23:21:14Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"Add a library that allows commands to produce output in any of a range of\nformats using a single API.  The idea being that by running all plumbing\ncommand output through this library the output can easily be switched to an\nalternative output style (e.g. JSON), while still supporting the\ncurrent output formats.\n\nThe API includes an OPT_OUTPUT and handle_output_arg so that the\noption handling for different commands will be as similar as possible.\n\nDocumentation for the API is included in\nDocumentation/technical/api-output.txt.\n\nAt the moment the only new output format is JSON.\n\nSigned-off-by: Julian Phillips <julian@quantumfyre.co.uk>\n---\n Documentation/technical/api-output.txt |  116 ++++++++++++++\n Makefile                               |    4 +\n output-json.c                          |  127 +++++++++++++++\n output-normal.c                        |   95 +++++++++++\n output-zero.c                          |   74 +++++++++\n output.c                               |  266 ++++++++++++++++++++++++++++++++\n output.h                               |   92 +++++++++++\n 7 files changed, 774 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/technical/api-output.txt\n create mode 100644 output-json.c\n create mode 100644 output-normal.c\n create mode 100644 output-zero.c\n create mode 100644 output.c\n create mode 100644 output.h\n\ndiff --git a/Documentation/technical/api-output.txt b/Documentation/technical/api-output.txt\nnew file mode 100644\nindex 0000000..a811cbe\n--- /dev/null\n+++ b/Documentation/technical/api-output.txt\n@@ -0,0 +1,116 @@\n+structured output API\n+=====================\n+\n+The structured output API is provided by output.h and consists of a set of\n+functions for outputting data in a structured manner in one of a number of\n+formats (referred to as output styles).\n+\n+The output consists of objects, arrays and the actual values, the term item is\n+used where any of these may be used, and container when either an object or\n+array may be used.  Objects are unordered collections of named items, and arrays\n+are ordered collections of unnamed items.  For simplicity a name is always\n+supplied when creating an item - though it may not always be used (e.g. if you\n+are adding the item to a list).\n+\n+To start using structured output `output_start` must be called, as this\n+allocates a context to track what is happening and records which output style\n+has been selected.  Once all the data has been output, then `output_end` must be\n+called.\n+\n+Values are output by calling the `output_<type>` functions, and grouped by\n+starting a container with either `output_start_object` or `output_start_array`.\n+A container can be closed by calling `output_end_current` after which items will\n+be added at the same level as the container just closed.  It is not necessary to\n+close all containers before calling `output_end`, as any open containers will be\n+automatically closed.\n+\n+There are also some functions in the API which exist to support unstructured\n+output formats - primarily the existing regular and -z output formats.\n+\n+Functions\n+---------\n+\n+* Option Parsing\n+\n+`OPT_OUTPUT`::\n+\n+\tThis is a convienience macro that allows easy integration of structured\n+\toutput into a command using the option parser.  The caller only has to\n+\tsupply the short and long option names, and the variable to store the\n+\toption string in.\n+\n+`handle_output_arg`::\n+\n+\tConvert a string into a `enum output_style` value.  This allows the\n+\tcalling command to pass the job of parsing the command line option value\n+\tto the output code.\n+\n+* Top Level\n+\n+`ouput_start`::\n+\n+\tThis function starts the structured output.  It takes an `enum\n+\toutput_style` argument the defines which backend will be used to do the\n+\toutput, and returns a context pointer that must be passed to all other\n+\toutput functions.\n+\n+`output_end`::\n+\n+\tThis function finishes structured output, including closing any open\n+\tcontainers and freeing the context structure allocated by output_start.\n+\n+* Container Management\n+\n+`output_start_object`::\n+\n+\tThis starts a new object as a member of the curent container, all items\n+\twill then be added to this object until `object_end_current` or\n+\t`output_end` is called.\n+\n+`output_start_array`::\n+\n+\tThis starts a new array as a member of the current container, all items\n+\twill then be added to this array until `object_end_curent` or\n+\t`output_end` is called.\n+\n+`output_end_current`::\n+\n+\tThis closes the current container.  Any items added after this function\n+\tis called will be added to the parent of the container just closed.\n+\n+* Value Output Functions\n+\n+`output_<type>`::\n+\n+\tOutput a value of the given type into the current with the given name\n+\t(which will be ignored if the continer is an array).  Strings are\n+\tassumed to be UTF-8, so strings known to be in other encodings will need\n+\tto be converted.  Quoting of spaces etc is done by the backend code, as\n+\tthe quoting rules are goverened by which style is chosen, so strings\n+\tshould be unqouted.\n+\n+* Unstructured Output Functions\n+\n+`output_token`::\n+\n+\tOutput the given token.  This token is not subject to any quoting rules\n+\tbut is just displayed as given.\n+\n+`output_nul`::\n+\n+\tThis is basically a dedicated version of `output_token` for outputing a\n+\tNUL character (which can't be passed through `output_token` since it\n+\tuses NUL terminatation).\n+\n+`output_newline`::\n+\n+\tOutput an approprite marker for the end of a line.  In the normal output\n+\tthis is a newline character - for the -z output it will be a NUL\n+\tcharacter.\n+\n+`output_next_directive`::\n+\n+\tThis function allows the modification of printf format directives if\n+\tsupported by the backend (only NORMAL and ZERO currently).  The given\n+\tstring is inserted between the \"%\" and the exisitng directive\n+\tcharacters.\ndiff --git a/Makefile b/Makefile\nindex 910f471..dc38730 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -576,6 +576,10 @@ LIB_OBJS += merge-recursive.o\n LIB_OBJS += name-hash.o\n LIB_OBJS += notes.o\n LIB_OBJS += object.o\n+LIB_OBJS += output.o\n+LIB_OBJS += output-json.o\n+LIB_OBJS += output-normal.o\n+LIB_OBJS += output-zero.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-refs.o\n LIB_OBJS += pack-revindex.o\ndiff --git a/output-json.c b/output-json.c\nnew file mode 100644\nindex 0000000..386f010\n--- /dev/null\n+++ b/output-json.c\n@@ -0,0 +1,127 @@\n+#include \"git-compat-util.h\"\n+#include \"output.h\"\n+\n+static void json_print_quoted(FILE *file, const char *unquoted)\n+{\n+\tunsigned char *s = (unsigned char *)unquoted;\n+\n+\twhile (*s) {\n+\t\tswitch (*s) {\n+\t\tcase '\"':\n+\t\t\tfprintf(file, \"\\\\\\\"\");\n+\t\t\tbreak;\n+\t\tcase '\\\\':\n+\t\t\tfprintf(file, \"\\\\\\\\\");\n+\t\t\tbreak;\n+\t\tcase '\\b':\n+\t\t\tfprintf(file, \"\\\\b\");\n+\t\t\tbreak;\n+\t\tcase '\\f':\n+\t\t\tfprintf(file, \"\\\\f\");\n+\t\t\tbreak;\n+\t\tcase '\\n':\n+\t\t\tfprintf(file, \"\\\\n\");\n+\t\t\tbreak;\n+\t\tcase '\\r':\n+\t\t\tfprintf(file, \"\\\\r\");\n+\t\t\tbreak;\n+\t\tcase '\\t':\n+\t\t\tfprintf(file, \"\\\\t\");\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\t/*\n+\t\t\t * All control characters must be encoded, even if they\n+\t\t\t * don't have a specific escape character of their own\n+\t\t\t */\n+\t\t\tif (*s < 0x20)\n+\t\t\t\tfprintf(file, \"\\\\u%04x\", *s);\n+\t\t\telse\n+\t\t\t\tfprintf(file, \"%c\", *s);\n+\t\t\tbreak;\n+\t\t}\n+\t\ts++;\n+\t}\n+}\n+\n+static void json_obj_start(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"{\\n\");\n+}\n+\n+static void json_obj_end(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"\\n}\");\n+}\n+\n+static void json_obj_item_start(FILE *file, const char *name, int first)\n+{\n+\tif (!first)\n+\t\tfprintf(file, \",\\n\");\n+\tfprintf(file, \"\\\"\");\n+\tjson_print_quoted(file, name);\n+\tfprintf(file, \"\\\" : \");\n+}\n+\n+static void json_array_start(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"[\\n\");\n+}\n+\n+static void json_array_end(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"\\n]\");\n+}\n+\n+static void json_array_item_start(FILE *file, const char *name, int first)\n+{\n+\tif (!first)\n+\t\tfprintf(file, \",\\n\");\n+}\n+\n+static void json_bool(FILE *file, int value)\n+{\n+\tif (value)\n+\t\tfprintf(file, \"true\");\n+\telse\n+\t\tfprintf(file, \"false\");\n+}\n+\n+static void json_str(FILE *file, const char *value)\n+{\n+\tfprintf(file, \"\\\"\");\n+\tjson_print_quoted(file, value);\n+\tfprintf(file, \"\\\"\");\n+}\n+\n+static void json_int(FILE *file, int64_t value)\n+{\n+\tfprintf(file, \"%lld\", value);\n+}\n+\n+static void json_uint(FILE *file, uint64_t value)\n+{\n+\tfprintf(file, \"%llu\", value);\n+}\n+\n+static void json_double(FILE *file, double value, int precision)\n+{\n+\tfprintf(file, \"%.*f\", precision, value);\n+}\n+\n+struct output_ops output_json_ops = {\n+\tjson_obj_start,\n+\tjson_obj_end,\n+\tjson_obj_item_start,\n+\tNULL,\n+\n+\tjson_array_start,\n+\tjson_array_end,\n+\tjson_array_item_start,\n+\tNULL,\n+\n+\tjson_bool,\n+\tjson_str,\n+\tjson_int,\n+\tjson_uint,\n+\tjson_double,\n+};\ndiff --git a/output-normal.c b/output-normal.c\nnew file mode 100644\nindex 0000000..d4c570a\n--- /dev/null\n+++ b/output-normal.c\n@@ -0,0 +1,95 @@\n+#include \"git-compat-util.h\"\n+#include \"output.h\"\n+#include \"strbuf.h\"\n+#include \"quote.h\"\n+\n+static const char *next_directive = \"\";\n+static char format_string[100];\n+\n+static char *normal_quote(const char *s)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tsize_t len = quote_c_style(s, &buf, NULL, 0);\n+\n+\tif (len == 0) {\n+\t\tstrbuf_release(&buf);\n+\t\treturn xstrdup(s);\n+\t} else\n+\t\treturn strbuf_detach(&buf, NULL);\n+}\n+\n+static void normal_bool(FILE *file, int value)\n+{\n+\tif (value)\n+\t\tfprintf(file, \"true\");\n+\telse\n+\t\tfprintf(file, \"false\");\n+}\n+\n+static void normal_str(FILE *file, const char *value)\n+{\n+\tchar *quoted = normal_quote(value);\n+\tsnprintf(format_string, sizeof(format_string), \"%%%ss\", next_directive);\n+\tfprintf(file, format_string, quoted);\n+\tnext_directive = \"\";\n+\tfree(quoted);\n+}\n+\n+static void normal_int(FILE *file, int64_t value)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%slld\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void normal_uint(FILE *file, uint64_t value)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%sllu\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void normal_double(FILE *file, double value, int precision)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%s.*f\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void normal_token(FILE *file, const char *token)\n+{\n+\tfprintf(file, \"%s\", token);\n+}\n+\n+static void normal_newline(FILE *file)\n+{\n+\tfprintf(file, \"\\n\");\n+}\n+\n+static void normal_next_directive(const char *directive)\n+{\n+\tnext_directive = directive;\n+}\n+\n+struct output_ops output_normal_ops = {\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\n+\tnormal_bool,\n+\tnormal_str,\n+\tnormal_int,\n+\tnormal_uint,\n+\tnormal_double,\n+\n+\tnormal_token,\n+\tNULL,\n+\tnormal_newline,\n+\tnormal_next_directive,\n+};\ndiff --git a/output-zero.c b/output-zero.c\nnew file mode 100644\nindex 0000000..a00a3cb\n--- /dev/null\n+++ b/output-zero.c\n@@ -0,0 +1,74 @@\n+#include \"git-compat-util.h\"\n+#include \"output.h\"\n+\n+static const char *next_directive = \"\";\n+static char format_string[100];\n+\n+static void zero_bool(FILE *file, int value)\n+{\n+\tif (value)\n+\t\tfprintf(file, \"true\");\n+\telse\n+\t\tfprintf(file, \"false\");\n+}\n+\n+static void zero_str(FILE *file, const char *value)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%ss\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void zero_int(FILE *file, int64_t value)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%slld\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void zero_uint(FILE *file, uint64_t value)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%sllu\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void zero_double(FILE *file, double value, int precision)\n+{\n+\tsnprintf(format_string, sizeof(format_string), \"%%%s.*f\", next_directive);\n+\tfprintf(file, format_string, value);\n+\tnext_directive = \"\";\n+}\n+\n+static void zero_nul(FILE *file)\n+{\n+\tfprintf(file, \"%c\", 0);\n+}\n+\n+static void zero_next_directive(const char *directive)\n+{\n+\tnext_directive = directive;\n+}\n+\n+struct output_ops output_zero_ops = {\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\n+\tzero_bool,\n+\tzero_str,\n+\tzero_int,\n+\tzero_uint,\n+\tzero_double,\n+\n+\tzero_str,\n+\tzero_nul,\n+\tzero_nul,\n+        zero_next_directive,\n+};\ndiff --git a/output.c b/output.c\nnew file mode 100644\nindex 0000000..ac8feb1\n--- /dev/null\n+++ b/output.c\n@@ -0,0 +1,266 @@\n+#include \"git-compat-util.h\"\n+#include \"output.h\"\n+#include \"strbuf.h\"\n+\n+#define DEFAULT_OUTPUT_FORMAT OUTPUT_JSON\n+\n+extern struct output_ops output_normal_ops;\n+extern struct output_ops output_zero_ops;\n+extern struct output_ops output_json_ops;\n+\n+struct output_ops *output_ops[] = {\n+\t&output_normal_ops,\n+\t&output_zero_ops,\n+\t&output_json_ops,\n+};\n+\n+enum output_style handle_output_arg(char *s)\n+{\n+\tif (!s)\n+\t\treturn OUTPUT_NORMAL;\n+\telse if (!strcmp(s, \"no\"))\n+\t\treturn OUTPUT_NORMAL;\n+\telse if (!strcmp(s, \"zero\"))\n+\t\treturn OUTPUT_ZERO;\n+\telse if (!strcmp(s, \"json\"))\n+\t\treturn OUTPUT_JSON;\n+\telse\n+\t\tdie(\"Invalid output style '%s'\", s);\n+}\n+\n+struct output_context *output_start(enum output_style style)\n+{\n+\tstruct output_context *context = xcalloc(1, sizeof(*context));\n+\n+\tcontext->style = style;\n+\tcontext->file = stdout;\n+\tcontext->ops = output_ops[style];\n+\n+\toutput_start_object(context, \"git\");\n+\n+\treturn context;\n+}\n+\n+void output_end(struct output_context *context)\n+{\n+\twhile(context->current)\n+\t\toutput_end_current(context);\n+\n+\t/*\n+\t * OUTPUT_NORMAL and OUTPUT_ZERO are special cases - the output format\n+\t * is _already_ defined so we have to stick to the rules, we can't add\n+\t * _anything_\n+\t */\n+\tif (context->style > OUTPUT_ZERO)\n+\t\tfprintf(context->file, \"\\n\");\n+\n+\tfree(context);\n+}\n+\n+\n+static void item_start(struct output_context *context, const char *name)\n+{\n+\tif (!context->current)\n+\t\treturn;\n+\n+\tswitch (context->current->type) {\n+\tcase OUTPUT_ITEM_OBJECT:\n+\t\tif (context->ops->obj_item_start)\n+\t\t\tcontext->ops->obj_item_start(context->file, name,\n+\t\t\t\t\t\t     context->current->first);\n+\t\tbreak;\n+\tcase OUTPUT_ITEM_ARRAY:\n+\t\tif (context->ops->array_item_start)\n+\t\t\tcontext->ops->array_item_start(context->file, name,\n+\t\t\t\t\t\t       context->current->first);\n+\t\tbreak;\n+\t}\n+}\n+\n+static void item_end(struct output_context *context, const char *name)\n+{\n+\tif (!context->current)\n+\t\treturn;\n+\n+\tswitch (context->current->type) {\n+\tcase OUTPUT_ITEM_OBJECT:\n+\t\tif (context->ops->obj_item_end)\n+\t\t\tcontext->ops->obj_item_end(context->file, name,\n+\t\t\t\t\t\t   context->current->first);\n+\t\tbreak;\n+\tcase OUTPUT_ITEM_ARRAY:\n+\t\tif (context->ops->array_item_end)\n+\t\t\tcontext->ops->array_item_end(context->file, name,\n+\t\t\t\t\t\t     context->current->first);\n+\t\tbreak;\n+\t}\n+\n+\tcontext->current->first = 0;\n+}\n+\n+\n+void output_new_current(struct output_context *context, const char *name,\n+\t\t\tenum output_item_type type)\n+{\n+\tstruct output_item *item = xmallocz(sizeof(*item));\n+\n+\titem->name = xstrdup(name);\n+\titem->type = type;\n+\titem->first = 1;\n+\n+\titem->prev = context->current;\n+\tcontext->current = item;\n+}\n+\n+void output_start_object(struct output_context *context, const char *name)\n+{\n+\titem_start(context, name);\n+\n+\toutput_new_current(context, name, OUTPUT_ITEM_OBJECT);\n+\n+\tif (context->ops->obj_start)\n+\t\tcontext->ops->obj_start(context->file, name);\n+}\n+\n+void output_start_array(struct output_context *context, const char *name)\n+{\n+\titem_start(context, name);\n+\n+\toutput_new_current(context, name, OUTPUT_ITEM_ARRAY);\n+\n+\tif (context->ops->array_start)\n+\t\tcontext->ops->array_start(context->file, name);\n+}\n+\n+void output_end_current(struct output_context *context)\n+{\n+\tstruct output_item *item = context->current;\n+\n+\tswitch (item->type) {\n+\tcase OUTPUT_ITEM_OBJECT:\n+\t\tif (context->ops->obj_end)\n+\t\t\tcontext->ops->obj_end(context->file, item->name);\n+\t\tbreak;\n+\tcase OUTPUT_ITEM_ARRAY:\n+\t\tif (context->ops->array_end)\n+\t\t\tcontext->ops->array_end(context->file, item->name);\n+\t\tbreak;\n+\t}\n+\n+\titem = context->current;\n+\tcontext->current = context->current->prev;\n+\n+\titem_end(context, item->name);\n+\n+\tfree(item->name);\n+\tfree(item);\n+}\n+\n+\n+void output_bool(struct output_context *context, const char *name, int value)\n+{\n+\titem_start(context, name);\n+\tif (context->ops->bool)\n+\t\tcontext->ops->bool(context->file, value);\n+\titem_end(context, name);\n+}\n+\n+void output_str(struct output_context *context, const char *name, const char *value)\n+{\n+\titem_start(context, name);\n+\tif (context->ops->str)\n+\t\tcontext->ops->str(context->file, value);\n+\titem_end(context, name);\n+}\n+\n+void output_strf(struct output_context *context, const char *name, const char *fmt, ...)\n+{\n+\tstatic char buffer[100];\n+\tint len;\n+\tchar *value = buffer;\n+\tva_list ap;\n+\n+\tva_start(ap, fmt);\n+\tlen = vsnprintf(buffer, sizeof(buffer), fmt, ap);\n+\tva_end(ap);\n+\n+\tif (len >= sizeof(buffer)) {\n+\t\tvalue = xmalloc(len + 1);\n+\t\tva_start(ap, fmt);\n+\t\tvsnprintf(value, len + 1, fmt, ap);\n+\t\tva_end(ap);\n+\t}\n+\n+\toutput_str(context, name, value);\n+\n+\tif (value != buffer)\n+\t\tfree(value);\n+}\n+\n+void output_int(struct output_context *context, const char *name, int64_t value)\n+{\n+\titem_start(context, name);\n+\tif (context->ops->int_)\n+\t\tcontext->ops->int_(context->file, value);\n+\titem_end(context, name);\n+}\n+\n+void output_uint(struct output_context *context, const char *name, uint64_t value)\n+{\n+\titem_start(context, name);\n+\tif (context->ops->uint)\n+\t\tcontext->ops->uint(context->file, value);\n+\titem_end(context, name);\n+}\n+\n+void output_double(struct output_context *context, const char *name, double value,\n+\t\t   int precision)\n+{\n+\titem_start(context, name);\n+\tif (context->ops->double_)\n+\t\tcontext->ops->double_(context->file, value, precision);\n+\titem_end(context, name);\n+}\n+\n+void output_token(struct output_context *context, const char *fmt, ...)\n+{\n+\tstatic char buffer[100];\n+\tint len;\n+\tchar *token = buffer;\n+\tva_list ap;\n+\n+\tva_start(ap, fmt);\n+\tlen = vsnprintf(buffer, sizeof(buffer), fmt, ap);\n+\tva_end(ap);\n+\n+\tif (len >= sizeof(buffer)) {\n+\t\ttoken = xmalloc(len + 1);\n+\t\tva_start(ap, fmt);\n+\t\tvsnprintf(token, len + 1, fmt, ap);\n+\t\tva_end(ap);\n+\t}\n+\n+\tif (context->ops->token)\n+\t\tcontext->ops->token(context->file, token);\n+\n+\tif (token != buffer)\n+\t\tfree(token);\n+}\n+\n+void output_nul(struct output_context *context)\n+{\n+\tif (context->ops->nul)\n+\t\tcontext->ops->nul(context->file);\n+}\n+\n+void output_newline(struct output_context *context)\n+{\n+\tif (context->ops->newline)\n+\t\tcontext->ops->newline(context->file);\n+}\n+\n+void output_next_directive(struct output_context *context, const char *directive)\n+{\n+\tif (context->ops->directive)\n+\t\tcontext->ops->directive(directive);\n+}\ndiff --git a/output.h b/output.h\nnew file mode 100644\nindex 0000000..c1a09d0\n--- /dev/null\n+++ b/output.h\n@@ -0,0 +1,92 @@\n+#ifndef OUTPUT_H\n+#define OUTPUT_H\n+\n+enum output_style {\n+\tOUTPUT_NORMAL,\n+\tOUTPUT_ZERO,\n+\tOUTPUT_JSON,\n+};\n+\n+struct output_ops {\n+\tvoid (*obj_start)(FILE *file, const char *name);\n+\tvoid (*obj_end)(FILE *file, const char *name);\n+\tvoid (*obj_item_start)(FILE *file, const char *name, int first);\n+\tvoid (*obj_item_end)(FILE *file, const char *name, int first);\n+\n+\tvoid (*array_start)(FILE *file, const char *name);\n+\tvoid (*array_end)(FILE *file, const char *name);\n+\tvoid (*array_item_start)(FILE *file, const char *name, int first);\n+\tvoid (*array_item_end)(FILE *file, const char *name, int first);\n+\n+\tvoid (*bool)(FILE *file, int value);\n+\tvoid (*str)(FILE *file, const char *value);\n+\tvoid (*int_)(FILE *file, int64_t value);\n+\tvoid (*uint)(FILE *file, uint64_t value);\n+\tvoid (*double_)(FILE *file, double value, int precision);\n+\n+\t/*\n+\t * ops defined after this comment are for backwards compatability with\n+\t * existing output formats, and shouldn't be provided by new output\n+\t * styles\n+\t */\n+\tvoid (*token)(FILE *file, const char *token);\n+\tvoid (*nul)(FILE *file);\n+\tvoid (*newline)(FILE *file);\n+\tvoid (*directive)(const char *directive);\n+};\n+\n+enum output_item_type {\n+\tOUTPUT_ITEM_OBJECT,\n+\tOUTPUT_ITEM_ARRAY,\n+};\n+\n+struct output_item {\n+\tstruct output_item *prev;\n+\tchar *name;\n+\tenum output_item_type type;\n+\tint first;\n+};\n+\n+struct output_context {\n+\tenum output_style style;\n+\tFILE *file;\n+\tstruct output_ops *ops;\n+\tstruct output_item *current;\n+};\n+\n+extern struct option OUTPUT_OPTION;\n+\n+#define OPT_OUTPUT(s, l, v) { OPTION_STRING, (s), (l), (v), \"style\",     \\\n+\t\t\t      \"Use a structured output style, options: \" \\\n+\t\t\t      \"no, zero, json (Default: zero)\",          \\\n+\t\t\t      PARSE_OPT_OPTARG, NULL, (intptr_t)\"zero\" }\n+\n+enum output_style handle_output_arg(char *s);\n+\n+struct output_context *output_start(enum output_style style);\n+void output_end(struct output_context *context);\n+\n+void output_start_object(struct output_context *context, const char *name);\n+void output_start_array(struct output_context *context, const char *name);\n+void output_end_current(struct output_context *context);\n+\n+void output_bool(struct output_context *context, const char *name, int value);\n+void output_str(struct output_context *context, const char *name, const char *value);\n+void output_strf(struct output_context *context, const char *name, const char *fmt, ...);\n+void output_int(struct output_context *context, const char *name, int64_t value);\n+void output_uint(struct output_context *context, const char *name, uint64_t value);\n+void output_double(struct output_context *context, const char *name, double value,\n+\t\t   int precision);\n+\n+/*\n+ * These functions are used to output the fixed formatting tokens needed to\n+ * output exisitng -z formats.  It should only be needed when reproducing\n+ * existing output.\n+ */\n+\n+void output_token(struct output_context *context, const char *fmt, ...);\n+void output_nul(struct output_context *context);\n+void output_newline(struct output_context *context);\n+void output_next_directive(struct output_context *context, const char *directive);\n+\n+#endif /* OUTPUT_H */\n-- \n1.7.0.4\n"},{"id":"139295","messageId":"20100411232118.67460.12125.julian@quantumfyre.co.uk","threadId":"23423","inReplyTo":"20100411231824.67460.24844.julian@quantumfyre.co.uk","subject":"[RFC/PATCH v2 2/4] ls-tree: complete conversion to using output library","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-11T23:21:15Z","receivedAt":"2010-04-11T23:21:15Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"All output from ls-tree now goes through the output library - even the\nregular output.\n\nSigned-off-by: Julian Phillips <julian@quantumfyre.co.uk>\n---\n builtin/ls-tree.c |   51 +++++++++++++++++++++++++++++++++------------------\n 1 files changed, 33 insertions(+), 18 deletions(-)\n\ndiff --git a/builtin/ls-tree.c b/builtin/ls-tree.c\nindex dc86b0d..7e19d19 100644\n--- a/builtin/ls-tree.c\n+++ b/builtin/ls-tree.c\n@@ -10,8 +10,8 @@\n #include \"quote.h\"\n #include \"builtin.h\"\n #include \"parse-options.h\"\n+#include \"output.h\"\n \n-static int line_termination = '\\n';\n #define LS_RECURSIVE 1\n #define LS_TREE_ONLY 2\n #define LS_SHOW_TREES 4\n@@ -22,6 +22,7 @@ static int ls_options;\n static const char **pathspec;\n static int chomp_prefix;\n static const char *ls_tree_prefix;\n+static struct output_context *oc;\n \n static const  char * const ls_tree_usage[] = {\n \t\"git ls-tree [<options>] <tree-ish> [path...]\",\n@@ -90,27 +91,32 @@ static int show_tree(const unsigned char *sha1, const char *base, int baselen,\n \t    (baselen < chomp_prefix || memcmp(ls_tree_prefix, base, chomp_prefix)))\n \t\treturn 0;\n \n+\toutput_start_object(oc, \"entry\");\n \tif (!(ls_options & LS_NAME_ONLY)) {\n+\t\toutput_strf(oc, \"mode\", \"%06o\", mode);\n+\t\toutput_token(oc, \" \");\n+\t\toutput_str(oc, \"type\", type);\n+\t\toutput_token(oc, \" \");\n+\t\toutput_str(oc, \"hash\", find_unique_abbrev(sha1, abbrev));\n \t\tif (ls_options & LS_SHOW_SIZE) {\n-\t\t\tchar size_text[24];\n-\t\t\tif (!strcmp(type, blob_type)) {\n+\t\t\tif (strcmp(type, blob_type)) {\n+\t\t\t\toutput_token(oc, \"       -\");\n+\t\t\t} else {\n \t\t\t\tunsigned long size;\n+\t\t\t\toutput_token(oc, \" \");\n+\t\t\t\toutput_next_directive(oc, \"7\");\n \t\t\t\tif (sha1_object_info(sha1, &size) == OBJ_BAD)\n-\t\t\t\t\tstrcpy(size_text, \"BAD\");\n+\t\t\t\t\toutput_str(oc, \"size\", \"BAD\");\n \t\t\t\telse\n-\t\t\t\t\tsnprintf(size_text, sizeof(size_text),\n-\t\t\t\t\t\t \"%lu\", size);\n-\t\t\t} else\n-\t\t\t\tstrcpy(size_text, \"-\");\n-\t\t\tprintf(\"%06o %s %s %7s\\t\", mode, type,\n-\t\t\t       find_unique_abbrev(sha1, abbrev),\n-\t\t\t       size_text);\n-\t\t} else\n-\t\t\tprintf(\"%06o %s %s\\t\", mode, type,\n-\t\t\t       find_unique_abbrev(sha1, abbrev));\n+\t\t\t\t\toutput_uint(oc, \"size\", size);\n+\t\t\t}\n+\t\t}\n+\t\toutput_token(oc, \"\\t\");\n \t}\n-\twrite_name_quotedpfx(base + chomp_prefix, baselen - chomp_prefix,\n-\t\t\t  pathname, stdout, line_termination);\n+\toutput_strf(oc, \"path\", \"%.*s%s\", baselen - chomp_prefix,\n+\t\t    base + chomp_prefix, pathname);\n+\toutput_newline(oc);\n+\toutput_end_current(oc);\n \treturn retval;\n }\n \n@@ -119,6 +125,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \tunsigned char sha1[20];\n \tstruct tree *tree;\n \tint full_tree = 0;\n+\tchar *structured_output_arg = NULL;\n+\tenum output_style output_style;\n \tconst struct option ls_tree_options[] = {\n \t\tOPT_BIT('d', NULL, &ls_options, \"only show trees\",\n \t\t\tLS_TREE_ONLY),\n@@ -126,8 +134,6 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\tLS_RECURSIVE),\n \t\tOPT_BIT('t', NULL, &ls_options, \"show trees when recursing\",\n \t\t\tLS_SHOW_TREES),\n-\t\tOPT_SET_INT('z', NULL, &line_termination,\n-\t\t\t    \"terminate entries with NUL byte\", 0),\n \t\tOPT_BIT('l', \"long\", &ls_options, \"include object size\",\n \t\t\tLS_SHOW_SIZE),\n \t\tOPT_BIT(0, \"name-only\", &ls_options, \"list only filenames\",\n@@ -140,6 +146,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t\t\t    \"list entire tree; not just current directory \"\n \t\t\t    \"(implies --full-name)\"),\n \t\tOPT__ABBREV(&abbrev),\n+\t\tOPT_OUTPUT('z', \"output\", &structured_output_arg),\n \t\tOPT_END()\n \t};\n \n@@ -159,6 +166,8 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \t    ((LS_TREE_ONLY|LS_RECURSIVE) & ls_options))\n \t\tls_options |= LS_SHOW_TREES;\n \n+\toutput_style = handle_output_arg(structured_output_arg);\n+\n \tif (argc < 1)\n \t\tusage_with_options(ls_tree_usage, ls_tree_options);\n \tif (get_sha1(argv[0], sha1))\n@@ -168,7 +177,13 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)\n \ttree = parse_tree_indirect(sha1);\n \tif (!tree)\n \t\tdie(\"not a tree object\");\n+\n+\toc = output_start(output_style);\n+\toutput_start_array(oc, \"entries\");\n+\n \tread_tree_recursive(tree, \"\", 0, 0, pathspec, show_tree, NULL);\n \n+\toutput_end(oc);\n+\n \treturn 0;\n }\n-- \n1.7.0.4\n"},{"id":"139298","messageId":"20100411232118.67460.35240.julian@quantumfyre.co.uk","threadId":"23423","inReplyTo":"20100411231824.67460.24844.julian@quantumfyre.co.uk","subject":"[RFC/PATCH v2 3/4] status: use output library for porcelain output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-11T23:21:16Z","receivedAt":"2010-04-11T23:21:16Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"Use the new output library for output when in porcelain mode.\n\nSigned-off-by: Julian Phillips <julian@quantumfyre.co.uk>\n---\n builtin/commit.c |   21 +++++++++++-\n wt-status.c      |   88 ++++++++++++++++++++++++++++++++++++++++++++++++++---\n wt-status.h      |    3 +-\n 3 files changed, 104 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c5ab683..4e506b1 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -25,6 +25,7 @@\n #include \"rerere.h\"\n #include \"unpack-trees.h\"\n #include \"quote.h\"\n+#include \"output.h\"\n \n static const char * const builtin_commit_usage[] = {\n \t\"git commit [options] [--] <filepattern>...\",\n@@ -68,6 +69,8 @@ static int all, edit_flag, also, interactive, only, amend, signoff;\n static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;\n static int no_post_rewrite;\n static char *untracked_files_arg, *force_date;\n+static char *structured_output_arg;\n+static enum output_style output_style;\n /*\n  * The default commit message cleanup mode will remove the lines\n  * beginning with # (shell comments) and leading and trailing\n@@ -137,6 +140,7 @@ static struct option builtin_commit_options[] = {\n \t\t    \"show porcelain output format\", STATUS_FORMAT_PORCELAIN),\n \tOPT_BOOLEAN('z', \"null\", &null_termination,\n \t\t    \"terminate entries with NUL\"),\n+\tOPT_OUTPUT(0, \"output\", &structured_output_arg),\n \tOPT_BOOLEAN(0, \"amend\", &amend, \"amend previous commit\"),\n \tOPT_BOOLEAN(0, \"no-post-rewrite\", &no_post_rewrite, \"bypass post-rewrite hook\"),\n \t{ OPTION_STRING, 'u', \"untracked-files\", &untracked_files_arg, \"mode\", \"show untracked files, optional modes: all, normal, no. (Default: all)\", PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n@@ -420,7 +424,7 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int\n \t\twt_shortstatus_print(s, null_termination);\n \t\tbreak;\n \tcase STATUS_FORMAT_PORCELAIN:\n-\t\twt_porcelain_print(s, null_termination);\n+\t\twt_porcelain_print(s, output_style);\n \t\tbreak;\n \tcase STATUS_FORMAT_LONG:\n \t\twt_status_print(s);\n@@ -933,6 +937,11 @@ static int parse_and_validate_options(int argc, const char *argv[],\n \n \tif (null_termination && status_format == STATUS_FORMAT_LONG)\n \t\tstatus_format = STATUS_FORMAT_PORCELAIN;\n+\toutput_style = handle_output_arg(structured_output_arg);\n+\tif (output_style == OUTPUT_NORMAL && null_termination)\n+\t\toutput_style = OUTPUT_ZERO;\n+\tif (output_style != OUTPUT_NORMAL && status_format == STATUS_FORMAT_LONG)\n+\t\tstatus_format = STATUS_FORMAT_PORCELAIN;\n \tif (status_format != STATUS_FORMAT_LONG)\n \t\tdry_run = 1;\n \n@@ -1031,6 +1040,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t  \"mode\",\n \t\t  \"show untracked files, optional modes: all, normal, no. (Default: all)\",\n \t\t  PARSE_OPT_OPTARG, NULL, (intptr_t)\"all\" },\n+\t\tOPT_OUTPUT(0, \"output\", &structured_output_arg),\n \t\tOPT_END(),\n \t};\n \n@@ -1045,6 +1055,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\t\t     builtin_status_usage, 0);\n \thandle_untracked_files_arg(&s);\n \n+\toutput_style = handle_output_arg(structured_output_arg);\n+\n+\tif (output_style == OUTPUT_NORMAL && null_termination)\n+\t\toutput_style = OUTPUT_ZERO;\n+\tif (output_style != OUTPUT_NORMAL && status_format == STATUS_FORMAT_LONG)\n+\t\tstatus_format = STATUS_FORMAT_PORCELAIN;\n+\n \tif (*argv)\n \t\ts.pathspec = get_pathspec(prefix, argv);\n \n@@ -1066,7 +1083,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)\n \t\twt_shortstatus_print(&s, null_termination);\n \t\tbreak;\n \tcase STATUS_FORMAT_PORCELAIN:\n-\t\twt_porcelain_print(&s, null_termination);\n+\t\twt_porcelain_print(&s, output_style);\n \t\tbreak;\n \tcase STATUS_FORMAT_LONG:\n \t\ts.verbose = verbose;\ndiff --git a/wt-status.c b/wt-status.c\nindex 8ca59a2..2a1e0fe 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -743,10 +743,88 @@ void wt_shortstatus_print(struct wt_status *s, int null_termination)\n \t}\n }\n \n-void wt_porcelain_print(struct wt_status *s, int null_termination)\n+static void wt_porcelain_unmerged(struct string_list_item *it,\n+\t\t\t\t  struct output_context *oc)\n {\n-\ts->use_color = 0;\n-\ts->relative_paths = 0;\n-\ts->prefix = NULL;\n-\twt_shortstatus_print(s, null_termination);\n+\tstruct wt_status_change_data *d = it->util;\n+\tchar *ours = \"?\", *theirs = \"?\";\n+\n+\tswitch (d->stagemask) {\n+\tcase 1: ours = \"D\"; theirs = \"D\"; break; /* both deleted */\n+\tcase 2: ours = \"A\"; theirs = \"U\"; break; /* added by us */\n+\tcase 3: ours = \"U\"; theirs = \"D\"; break; /* deleted by them */\n+\tcase 4: ours = \"U\"; theirs = \"A\"; break; /* added by them */\n+\tcase 5: ours = \"D\"; theirs = \"U\"; break; /* deleted by us */\n+\tcase 6: ours = \"A\"; theirs = \"A\"; break; /* both added */\n+\tcase 7: ours = \"U\"; theirs = \"U\"; break; /* both modified */\n+\t}\n+\n+\toutput_start_object(oc, \"entry\");\n+\toutput_str(oc, \"ours\", ours);\n+\toutput_str(oc, \"theirs\", theirs);\n+\toutput_token(oc, \" \");\n+\toutput_str(oc, \"name\", it->string);\n+\toutput_newline(oc);\n+\toutput_end_current(oc);\n+}\n+\n+\n+static void wt_porcelain_status(struct string_list_item *it,\n+\t\t\t\tstruct output_context *oc)\n+{\n+\tstruct wt_status_change_data *d = it->util;\n+\tchar index = ' ', worktree = ' ';\n+\n+\tif (d->index_status)\n+\t\tindex = d->index_status;\n+\tif (d->worktree_status)\n+\t\tworktree = d->worktree_status;\n+\n+\toutput_start_object(oc, \"entry\");\n+\toutput_strf(oc, \"index\", \"%c\", index);\n+\toutput_strf(oc, \"worktree\", \"%c\", worktree);\n+\toutput_token(oc, \" \");\n+\tif (d->head_path && oc->style == OUTPUT_NORMAL) {\n+\t\toutput_str(oc, \"orig_name\", d->head_path);\n+\t\toutput_token(oc, \" -> \");\n+\t}\n+\toutput_str(oc, \"name\", it->string);\n+\tif (d->head_path && oc->style != OUTPUT_NORMAL) {\n+\t\toutput_nul(oc);\n+\t\toutput_str(oc, \"orig_name\", d->head_path);\n+\t}\n+\toutput_newline(oc);\n+\toutput_end_current(oc);\n+}\n+\n+void wt_porcelain_print(struct wt_status *s, enum output_style style)\n+{\n+\tint i;\n+\tstruct output_context *oc = output_start(style);\n+\n+\toutput_start_array(oc, \"entries\");\n+\n+\tfor (i = 0; i < s->change.nr; i++) {\n+\t\tstruct wt_status_change_data *d;\n+\t\tstruct string_list_item *it;\n+\n+\t\tit = &(s->change.items[i]);\n+\t\td = it->util;\n+\t\tif (d->stagemask)\n+\t\t\twt_porcelain_unmerged(it, oc);\n+\t\telse\n+\t\t\twt_porcelain_status(it, oc);\n+\t}\n+\n+\tfor (i = 0; i < s->untracked.nr; i++) {\n+\t\toutput_start_object(oc, \"entry\");\n+\t\toutput_str(oc, \"index\", \"?\");\n+\t\toutput_str(oc, \"worktree\", \"?\");\n+\t\toutput_token(oc, \" \");\n+\t\toutput_str(oc, \"name\", s->untracked.items[i].string);\n+\t\toutput_newline(oc);\n+\t\toutput_end_current(oc);\n+\t}\n+\n+\toutput_end(oc);\n }\ndiff --git a/wt-status.h b/wt-status.h\nindex 9120673..4461c64 100644\n--- a/wt-status.h\n+++ b/wt-status.h\n@@ -4,6 +4,7 @@\n #include <stdio.h>\n #include \"string-list.h\"\n #include \"color.h\"\n+#include \"output.h\"\n \n enum color_wt_status {\n \tWT_STATUS_HEADER = 0,\n@@ -60,6 +61,6 @@ void wt_status_print(struct wt_status *s);\n void wt_status_collect(struct wt_status *s);\n \n void wt_shortstatus_print(struct wt_status *s, int null_termination);\n-void wt_porcelain_print(struct wt_status *s, int null_termination);\n+void wt_porcelain_print(struct wt_status *s, enum output_style style);\n \n #endif /* STATUS_H */\n-- \n1.7.0.4\n"},{"id":"139297","messageId":"20100411232118.67460.49557.julian@quantumfyre.co.uk","threadId":"23423","inReplyTo":"20100411231824.67460.24844.julian@quantumfyre.co.uk","subject":"[RFC/PATCH v2 4/4] output: WIP: Add XML backend","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-11T23:21:17Z","receivedAt":"2010-04-11T23:21:17Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"This is the very beginnings of an XML style for the output library.\nIt still needs a lot of work.  There is no quoting, and the layout\nstill needs designing.  At this point the main purpose of this code is\nto exercise the frontend/backend API in a different way from JSON.\n\nSigned-off-by: Julian Phillips <julian@quantumfyre.co.uk>\n---\n Makefile     |    1 +\n output-xml.c |   68 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n output.c     |    4 +++\n output.h     |    3 +-\n 4 files changed, 75 insertions(+), 1 deletions(-)\n create mode 100644 output-xml.c\n\ndiff --git a/Makefile b/Makefile\nindex dc38730..c84d7de 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -579,6 +579,7 @@ LIB_OBJS += object.o\n LIB_OBJS += output.o\n LIB_OBJS += output-json.o\n LIB_OBJS += output-normal.o\n+LIB_OBJS += output-xml.o\n LIB_OBJS += output-zero.o\n LIB_OBJS += pack-check.o\n LIB_OBJS += pack-refs.o\ndiff --git a/output-xml.c b/output-xml.c\nnew file mode 100644\nindex 0000000..9c98ac9\n--- /dev/null\n+++ b/output-xml.c\n@@ -0,0 +1,68 @@\n+#include \"git-compat-util.h\"\n+#include \"output.h\"\n+\n+static void xml_obj_start(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"<object name=\\\"%s\\\">\\n\", name);\n+}\n+\n+static void xml_obj_end(FILE *file, const char *name)\n+{\n+\tfprintf(file, \"</object>\\n\");\n+}\n+\n+static void xml_obj_item_start(FILE *file, const char *name, int first)\n+{\n+\tfprintf(file, \"<%s>\", name);\n+}\n+\n+static void xml_obj_item_end(FILE *file, const char *name, int first)\n+{\n+\tfprintf(file, \"</%s>\\n\", name);\n+}\n+\n+static void xml_bool(FILE *file, int value)\n+{\n+\tif (value)\n+\t\tfprintf(file, \"true\");\n+\telse\n+\t\tfprintf(file, \"false\");\n+}\n+\n+static void xml_str(FILE *file, const char *value)\n+{\n+\tfprintf(file, \"\\\"%s\\\"\", value);\n+}\n+\n+static void xml_int(FILE *file, int64_t value)\n+{\n+\tfprintf(file, \"%lld\", value);\n+}\n+\n+static void xml_uint(FILE *file, uint64_t value)\n+{\n+\tfprintf(file, \"%llu\", value);\n+}\n+\n+static void xml_double(FILE *file, double value, int precision)\n+{\n+\tfprintf(file, \"%.*f\", precision, value);\n+}\n+\n+struct output_ops output_xml_ops = {\n+\txml_obj_start,\n+\txml_obj_end,\n+\txml_obj_item_start,\n+\txml_obj_item_end,\n+\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\tNULL,\n+\n+\txml_bool,\n+\txml_str,\n+\txml_int,\n+\txml_uint,\n+\txml_double,\n+};\ndiff --git a/output.c b/output.c\nindex ac8feb1..3be1560 100644\n--- a/output.c\n+++ b/output.c\n@@ -7,11 +7,13 @@\n extern struct output_ops output_normal_ops;\n extern struct output_ops output_zero_ops;\n extern struct output_ops output_json_ops;\n+extern struct output_ops output_xml_ops;\n \n struct output_ops *output_ops[] = {\n \t&output_normal_ops,\n \t&output_zero_ops,\n \t&output_json_ops,\n+\t&output_xml_ops,\n };\n \n enum output_style handle_output_arg(char *s)\n@@ -24,6 +26,8 @@ enum output_style handle_output_arg(char *s)\n \t\treturn OUTPUT_ZERO;\n \telse if (!strcmp(s, \"json\"))\n \t\treturn OUTPUT_JSON;\n+\telse if (!strcmp(s, \"xml\"))\n+\t\treturn OUTPUT_XML;\n \telse\n \t\tdie(\"Invalid output style '%s'\", s);\n }\ndiff --git a/output.h b/output.h\nindex c1a09d0..cc0b921 100644\n--- a/output.h\n+++ b/output.h\n@@ -5,6 +5,7 @@ enum output_style {\n \tOUTPUT_NORMAL,\n \tOUTPUT_ZERO,\n \tOUTPUT_JSON,\n+\tOUTPUT_XML,\n };\n \n struct output_ops {\n@@ -58,7 +59,7 @@ extern struct option OUTPUT_OPTION;\n \n #define OPT_OUTPUT(s, l, v) { OPTION_STRING, (s), (l), (v), \"style\",     \\\n \t\t\t      \"Use a structured output style, options: \" \\\n-\t\t\t      \"no, zero, json (Default: zero)\",          \\\n+\t\t\t      \"no, zero, json, xml (Default: zero)\",     \\\n \t\t\t      PARSE_OPT_OPTARG, NULL, (intptr_t)\"zero\" }\n \n enum output_style handle_output_arg(char *s);\n-- \n1.7.0.4\n"},{"id":"139302","messageId":"l2jfabb9a1e1004111635v16e4dc86g405883ca12d316b9@mail.gmail.com","threadId":"23423","inReplyTo":"20100411231824.67460.24844.julian@quantumfyre.co.uk","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-04-11T23:35:26Z","receivedAt":"2010-04-11T23:35:26Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Mon, Apr 12, 2010 at 01:21, Julian Phillips <julian@quantumfyre.co.uk> wrote:\n> Probably the biggest change from v1 is an expanded aim.  Now the output library\n> is aimed at controlling _all_ plubming output.  This series includes a patch for\n> ls-tree that has all it's output going through the library, and a patch for\n> status that has all the --porcelain output going through the library.\n\nI like where this is going, a lot, especially since we don't have to\nconvert everything in one go, but we can do it as desired, similar to\noptparsification. I still think more commands than just these two\nshould be converted to validate the design though, perhaps something\nlike 'git blame', or 'git for-each-ref'?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"139303","messageId":"20100412004625.GA19373@thyrsus.com","threadId":"23423","inReplyTo":"l2jfabb9a1e1004111635v16e4dc86g405883ca12d316b9@mail.gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-12T00:46:26Z","receivedAt":"2010-04-12T00:46:26Z","isPatch":true,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Sverre Rabbelier <srabbelier@gmail.com>:\n> On Mon, Apr 12, 2010 at 01:21, Julian Phillips <julian@quantumfyre.co.uk> wrote:\n> > Probably the biggest change from v1 is an expanded aim.  Now the\n> > output library is aimed at controlling _all_ plubming\n> > output.  This series includes a patch for ls-tree that has all\n> > it's output going through the library, and a patch for status that\n> > has all the --porcelain output going through the library.\n> \n> I like where this is going, a lot, especially since we don't have to\n> convert everything in one go, but we can do it as desired, similar to\n> optparsification.\n\nSpeaking as a major customer for the new capabilities it will enable,\nI strongly concur.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139406","messageId":"20100413094351.GA2558@LK-Perkele-V2.elisa-laajakaista.fi","threadId":"23423","inReplyTo":"20100411232118.67460.52907.julian@quantumfyre.co.uk","subject":"Re: [RFC/PATCH v2 1/4] output: Add a new library for plumbing output","fromName":"Ilari Liusvaara","fromEmail":"ilari.liusvaara@elisanet.fi","sentAt":"2010-04-13T09:43:51Z","receivedAt":"2010-04-13T09:43:51Z","isPatch":true,"sender":{"key":"ilari.liusvaara@elisanet.fi","avatar":null},"body":"On Mon, Apr 12, 2010 at 12:21:14AM +0100, Julian Phillips wrote:\n\nI'm writing S-Expression output backend as experiment (not yet even sendable\nas WIP) and hit an issue in general framework...\n\nAlso, some comments on documentation...\n\n> +The output consists of objects, arrays and the actual values, the term item is\n> +used where any of these may be used, and container when either an object or\n> +array may be used.  Objects are unordered collections of named items, and arrays\n> +are ordered collections of unnamed items.  For simplicity a name is always\n> +supplied when creating an item - though it may not always be used (e.g. if you\n> +are adding the item to a list).\n\nList? Above says types are 'object', 'array' and 'value'. Then it defines\nterms 'item' and 'container'. But what is 'list'?\n\n> +* Unstructured Output Functions\n\nMaybe add extra note about these. When one sees output_token used in code\noutputting stuff, one can get puzzled until one realizes that token output\nis ignored for non normal/zero outputs.\n\n> diff --git a/output.c b/output.c\n> new file mode 100644\n> index 0000000..ac8feb1\n> --- /dev/null\n> +++ b/output.c\n\n> +void output_end(struct output_context *context)\n> +{\n> +\twhile(context->current)\n> +\t\toutput_end_current(context);\n> +\n> +\t/*\n> +\t * OUTPUT_NORMAL and OUTPUT_ZERO are special cases - the output format\n> +\t * is _already_ defined so we have to stick to the rules, we can't add\n> +\t * _anything_\n> +\t */\n> +\tif (context->style > OUTPUT_ZERO)\n> +\t\tfprintf(context->file, \"\\n\");\n\nThis is AFAIK really inapporiate for canonical S-Expression output. Point of\ncanonical S-Expressions is to have only one way to serialize given tree (bit\nfor bit identicality) and linefeeds are not allowed except as serialization\nof linefeed in string.\n\nPerhaps one could add method/flag to output backend to tell wheither to\nprint trailing linefeed?\n\n> +\n> +\tfree(context);\n> +}\n\n-Ilari\n"},{"id":"139418","messageId":"30465b96f3938b1a993ead64bcfb03b0@212.159.54.234","threadId":"23423","inReplyTo":"20100413094351.GA2558@LK-Perkele-V2.elisa-laajakaista.fi","subject":"Re: [RFC/PATCH v2 1/4] output: Add a new library for plumbing output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-13T11:46:52Z","receivedAt":"2010-04-13T11:46:52Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Tue, 13 Apr 2010 12:43:51 +0300, Ilari Liusvaara\n<ilari.liusvaara@elisanet.fi> wrote:\n> On Mon, Apr 12, 2010 at 12:21:14AM +0100, Julian Phillips wrote:\n> \n> I'm writing S-Expression output backend as experiment (not yet even\n> sendable\n> as WIP) and hit an issue in general framework...\n> \n> Also, some comments on documentation...\n> \n>> +The output consists of objects, arrays and the actual values, the term\n>> item is\n>> +used where any of these may be used, and container when either an\n>> object or\n>> +array may be used.  Objects are unordered collections of named items,\n>> and arrays\n>> +are ordered collections of unnamed items.  For simplicity a name is\n>> always\n>> +supplied when creating an item - though it may not always be used\n(e.g.\n>> if you\n>> +are adding the item to a list).\n> \n> List? Above says types are 'object', 'array' and 'value'. Then it\ndefines\n> terms 'item' and 'container'. But what is 'list'?\n\ntypo - should be array.\n\n>> +* Unstructured Output Functions\n> \n> Maybe add extra note about these. When one sees output_token used in\ncode\n> outputting stuff, one can get puzzled until one realizes that token\noutput\n> is ignored for non normal/zero outputs.\n\nYes.  I intend to revist the header and documentation, as they were mostly\ndone before the normal output was added.\n\n>> diff --git a/output.c b/output.c\n>> new file mode 100644\n>> index 0000000..ac8feb1\n>> --- /dev/null\n>> +++ b/output.c\n> \n>> +void output_end(struct output_context *context)\n>> +{\n>> +\twhile(context->current)\n>> +\t\toutput_end_current(context);\n>> +\n>> +\t/*\n>> +\t * OUTPUT_NORMAL and OUTPUT_ZERO are special cases - the output\nformat\n>> +\t * is _already_ defined so we have to stick to the rules, we can't\nadd\n>> +\t * _anything_\n>> +\t */\n>> +\tif (context->style > OUTPUT_ZERO)\n>> +\t\tfprintf(context->file, \"\\n\");\n> \n> This is AFAIK really inapporiate for canonical S-Expression output.\nPoint\n> of\n> canonical S-Expressions is to have only one way to serialize given tree\n> (bit\n> for bit identicality) and linefeeds are not allowed except as\nserialization\n> of linefeed in string.\n> \n> Perhaps one could add method/flag to output backend to tell wheither to\n> print trailing linefeed?\n\nI think that it probably makes sense to have explicit calls into the\nbackend for start and end rather than assuming that wrapping everything in\nan object is appropriate, an XML backend for example could then use those\ncallbacks to do XML headers and the outmost element tags etc.\n\n-- \nJulian\n"},{"id":"139495","messageId":"201004142110.36453.jnareb@gmail.com","threadId":"23423","inReplyTo":"l2jfabb9a1e1004111635v16e4dc86g405883ca12d316b9@mail.gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-14T19:10:35Z","receivedAt":"2010-04-14T19:10:35Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Mon, 12 April 2010, Sverre Rabbelier wrote:\n> \n> On Mon, Apr 12, 2010 at 01:21, Julian Phillips <julian@quantumfyre.co.uk> wrote:\n> > Probably the biggest change from v1 is an expanded aim.  Now the output library\n> > is aimed at controlling _all_ plubming output.  This series includes a patch for\n> > ls-tree that has all it's output going through the library, and a patch for\n> > status that has all the --porcelain output going through the library.\n> \n> I like where this is going, a lot, especially since we don't have to\n> convert everything in one go, but we can do it as desired, similar to\n> optparsification. I still think more commands than just these two\n> should be converted to validate the design though, perhaps something\n> like 'git blame', or 'git for-each-ref'?\n\nI don't think it is needed for either command.\n\n'git blame' has --porcelain and --incremental output, which is line-based\nand pretty much self-describing (with \"header-name value\" syntax for most\nof it), and well documented.  JSON output would only add unnecessary\nchatter and different quoting rules.\n\n'git for-each-ref' has both --format=<format> to allow to get data what\none needs, and in the format one wants (with e.g. %00 to reresent NUL),\nand [--shell|--perl|--python|--tcl] for placeholders in <format> to be\nquoted as string literals suitable for specified host language.  Although\nI am not sure if this option, meant to produce scriptlets, is used that\nmuch/ note that there is not support for --json quoting, nor --xml \nescaping.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139497","messageId":"m2xfabb9a1e1004141213t7d4f084bk72a3b72544e11542@mail.gmail.com","threadId":"23423","inReplyTo":"201004142110.36453.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2010-04-14T19:13:16Z","receivedAt":"2010-04-14T19:13:16Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Wed, Apr 14, 2010 at 21:10, Jakub Narebski <jnareb@gmail.com> wrote:\n> I don't think it is needed for either command.\n\nThose were just the first two plumbing commands that came to mind that\nI use myself, do you have any suggestions for others that would be\nmore appropriate?\n\n-- \nCheers,\n\nSverre Rabbelier\n"},{"id":"139499","messageId":"7vwrw9q18m.fsf@alter.siamese.dyndns.org","threadId":"23423","inReplyTo":"201004142110.36453.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-14T19:32:09Z","receivedAt":"2010-04-14T19:32:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> I don't think it is needed for either command.\n>\n> 'git blame' has --porcelain and --incremental output, which is line-based\n> and pretty much self-describing (with \"header-name value\" syntax for most\n> of it), and well documented.  JSON output would only add unnecessary\n> chatter and different quoting rules.\n\nWouldn't the exact same argument apply equally well to the output format\nof \"status --porcelain\", by the way?  It is line-based and pretty much\nself-describing (once you know the mnemonic but you can make an educated\nguess from previous SCM experience).\n"},{"id":"139505","messageId":"201004142212.33162.jnareb@gmail.com","threadId":"23423","inReplyTo":"7vwrw9q18m.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-14T20:12:32Z","receivedAt":"2010-04-14T20:12:32Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia środa 14. kwietnia 2010 21:32, Junio C Hamano napisał:\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n> > I don't think it is needed for either command.\n> >\n> > 'git blame' has --porcelain and --incremental output, which is line-based\n> > and pretty much self-describing (with \"header-name value\" syntax for most\n> > of it), and well documented.  JSON output would only add unnecessary\n> > chatter and different quoting rules.\n> \n> Wouldn't the exact same argument apply equally well to the output format\n> of \"status --porcelain\", by the way?  It is line-based and pretty much\n> self-describing (once you know the mnemonic but you can make an educated\n> guess from previous SCM experience).\n\nNo, current \"git status --porcelain\" output is record-based (tabular);\nthe meaning is not described by header but depends on field in record,\ni.e. position in line.\n\nSelf describing output of \"git status --porcelain\" would be\n\n  filename <maybe-quoted filename>\n  renamed-from <maybe-quoted filename>\n  similarity 95%\n  worktree ...\n  index ...\n\nor something like that...\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139512","messageId":"7vbpdlpy5t.fsf@alter.siamese.dyndns.org","threadId":"23423","inReplyTo":"201004142212.33162.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-14T20:38:38Z","receivedAt":"2010-04-14T20:38:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n>> Wouldn't the exact same argument apply equally well to the output format\n>> of \"status --porcelain\", by the way?  It is line-based and pretty much\n>> self-describing (once you know the mnemonic but you can make an educated\n>> guess from previous SCM experience).\n>\n> No, current \"git status --porcelain\" output is record-based (tabular);\n> the meaning is not described by header but depends on field in record,\n> i.e. position in line.\n\nNow, what's wrong about that?  For that matter, would you say \"diff --raw\"\noutput should be JSON/XMLified because it is columnar?\n"},{"id":"139518","messageId":"80f140cdddc016f9b4608d79f1bc3722@212.159.54.234","threadId":"23423","inReplyTo":"201004142110.36453.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-14T20:57:27Z","receivedAt":"2010-04-14T20:57:27Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Wed, 14 Apr 2010 21:10:35 +0200, Jakub Narebski <jnareb@gmail.com>\nwrote:\n> On Mon, 12 April 2010, Sverre Rabbelier wrote:\n>> \n>> On Mon, Apr 12, 2010 at 01:21, Julian Phillips\n>> <julian@quantumfyre.co.uk> wrote:\n>> > Probably the biggest change from v1 is an expanded aim.  Now the\n>> > output library\n>> > is aimed at controlling _all_ plubming output.  This series includes\na\n>> > patch for\n>> > ls-tree that has all it's output going through the library, and a\n>> > patch for\n>> > status that has all the --porcelain output going through the library.\n>> \n>> I like where this is going, a lot, especially since we don't have to\n>> convert everything in one go, but we can do it as desired, similar to\n>> optparsification. I still think more commands than just these two\n>> should be converted to validate the design though, perhaps something\n>> like 'git blame', or 'git for-each-ref'?\n> \n> I don't think it is needed for either command.\n\nI think that the ability to say that all plumbing output is available in a\nvariety of standard outputs is potentially useful.  In particular the\nability to be able to parse the output of all plumbing commands directly\ninto whatever native language the high-level tool is in using an already\nexisting standard parser makes life easier for those writing the tool.\n\n> 'git blame' has --porcelain and --incremental output, which is\nline-based\n> and pretty much self-describing (with \"header-name value\" syntax for\nmost\n> of it), and well documented.  JSON output would only add unnecessary\n> chatter and different quoting rules.\n\nThat depends really.  If you are writing something to parse the output,\nand you already have a JSON parser available then it's the current output\nthat has different quoting rules. ;)\n\nAnyway, I have already converted blame to use the library for both\n--porcelain and --incremental output, so it'll be in the next version of\nthe patch series.  So you can try before you buy ...\n\n> 'git for-each-ref' has both --format=<format> to allow to get data what\n> one needs, and in the format one wants (with e.g. %00 to reresent NUL),\n> and [--shell|--perl|--python|--tcl] for placeholders in <format> to be\n> quoted as string literals suitable for specified host language. \nAlthough\n> I am not sure if this option, meant to produce scriptlets, is used that\n> much/ note that there is not support for --json quoting, nor --xml \n> escaping.\n\n-- \nJulian\n"},{"id":"139520","messageId":"201004142316.07947.jnareb@gmail.com","threadId":"23423","inReplyTo":"80f140cdddc016f9b4608d79f1bc3722@212.159.54.234","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-14T21:16:06Z","receivedAt":"2010-04-14T21:16:06Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Wed,  14 Apr 2010, Julian Phillips wrote:\n> On Wed, 14 Apr 2010 21:10:35 +0200, Jakub Narebski <jnareb@gmail.com>\n> wrote:\n\n> > 'git blame' has --porcelain and --incremental output, which is line-based\n> > and pretty much self-describing (with \"header-name value\" syntax for most\n> > of it), and well documented.  JSON output would only add unnecessary\n> > chatter and different quoting rules.\n> \n> That depends really.  If you are writing something to parse the output,\n> and you already have a JSON parser available then it's the current output\n> that has different quoting rules. ;)\n\nTrue.\n\n> \n> Anyway, I have already converted blame to use the library for both\n> --porcelain and --incremental output, so it'll be in the next version of\n> the patch series.  So you can try before you buy ...\n\nNice.\n\nHow did you managed to work with a bit non-standard rules of --porcelain\nformat, namely maybe-quoting of filenames, and that not all lines conform\nto \"<header> SP <value> LF\" syntax: group definition begins with SHA-1,\nand contents is indented with TAB?\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139523","messageId":"0e50c39792144f5cb18260009384b4d0@212.159.54.234","threadId":"23423","inReplyTo":"201004142316.07947.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-14T21:28:56Z","receivedAt":"2010-04-14T21:28:56Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Wed, 14 Apr 2010 23:16:06 +0200, Jakub Narebski <jnareb@gmail.com>\nwrote:\n> On Wed,  14 Apr 2010, Julian Phillips wrote:\n>> On Wed, 14 Apr 2010 21:10:35 +0200, Jakub Narebski <jnareb@gmail.com>\n>> wrote:\n>> Anyway, I have already converted blame to use the library for both\n>> --porcelain and --incremental output, so it'll be in the next version\nof\n>> the patch series.  So you can try before you buy ...\n> \n> Nice.\n> \n> How did you managed to work with a bit non-standard rules of --porcelain\n> format, namely maybe-quoting of filenames, and that not all lines\nconform\n> to \"<header> SP <value> LF\" syntax: group definition begins with SHA-1,\n> and contents is indented with TAB?\n\nI had to extend the output API for quoting rules.  There are now\nqstr/qstrf output functions that call different backend functions.  The\nnormal output then applies git quoting rules to the q versions only,\nwhereas the JSON output treats them both the same.\n\nFor the rest of it, only the actual data is going through the output_str\netc functions the rest is using output_token that is ignored by the JSON\nbackend.  I actually started converting blame twice - the first time I\nadded an API function that allowed telling the normal output how to format\nvalues, but I decided that I wanted to apply grouping to the structured\noutput, so that e.g. the author and committer information were two objects\nwith name, mail, date and tz members so I never finished the first attempt.\n\n-- \nJulian\n"},{"id":"139522","messageId":"201004142329.38914.jnareb@gmail.com","threadId":"23423","inReplyTo":"7vbpdlpy5t.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-14T21:29:38Z","receivedAt":"2010-04-14T21:29:38Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Wed, 14 April 2010, Junio C Hamano wrote:\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n>>> Wouldn't the exact same argument apply equally well to the output format\n>>> of \"status --porcelain\", by the way?  It is line-based and pretty much\n>>> self-describing (once you know the mnemonic but you can make an educated\n>>> guess from previous SCM experience).\n>>\n>> No, current \"git status --porcelain\" output is record-based (tabular);\n>> the meaning is not described by header but depends on field in record,\n>> i.e. position in line.\n> \n> Now, what's wrong about that?\n\nWell, this whole idea started with the fact, that \"git status --short\"\nwas hard (or impossible) to parse unambigously by scripts[1], and even\n\"git status --porcelain -z\"[2] is not that easy to parse[3].\n\nWith JSON output format one can use existing JSON parsers, which are\navailable in any language.\n\n[1] And it was woefully underdocumented\n[2] I wonder why git-config and git-grep have '--null' as long version\n    of '-z' option... and only those.\n[3] I rather liked the idea of -Z output format, the form that uses\n    NUL as field separator for each field (and not only filenames),\n    and NUL NUL as record terminator; it makes parsing much easier\n    because you don't need to take a look at other field to know\n    where the record ends.\n\n> For that matter, would you say \"diff --raw\" output should be\n> JSON/XMLified because it is columnar? \n\nIt would be nice to have raw diff format JSONified, or have --porcelain\n(like \"git blame --porcelain\" output format) version of it.  Especially\nfor \"diff -c --raw\" i.e. raw output format for merges, which lacks some\ninformation, namely filename and similarity score for n-th pre-image,\nif rename or copy was detected.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139524","messageId":"7viq7toh12.fsf@alter.siamese.dyndns.org","threadId":"23423","inReplyTo":"201004142329.38914.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-04-14T21:34:01Z","receivedAt":"2010-04-14T21:34:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Well, this whole idea started with the fact, that \"git status --short\"\n> was hard (or impossible) to parse unambigously by scripts[1], and even\n> \"git status --porcelain -z\"[2] is not that easy to parse[3].\n\nAnd you apparently seem to agree with that claim, but I don't.  I think\nJeff (who did the --porcelain stuff; by the way, why did we lose him from\nCc list?) has already said that he is open to an update.\n"},{"id":"139525","messageId":"201004142342.06873.jnareb@gmail.com","threadId":"23423","inReplyTo":"m2xfabb9a1e1004141213t7d4f084bk72a3b72544e11542@mail.gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-14T21:42:06Z","receivedAt":"2010-04-14T21:42:06Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Dnia środa 14. kwietnia 2010 21:13, Sverre Rabbelier napisał:\n> Heya,\n> \n> On Wed, Apr 14, 2010 at 21:10, Jakub Narebski <jnareb@gmail.com> wrote:\n> > I don't think it is needed for either command.\n> \n> Those were just the first two plumbing commands that came to mind that\n> I use myself, do you have any suggestions for others that would be\n> more appropriate?\n\n\"git config [<type>] --get <name>\" and friends... especially that even\nin the case of '{ name: \"<name>\"; value: <value> }' (and without e.g.\n\"type: <type>\" field) we can have <value> of correct JSON type.\n\n\"git ls-files\" (especially when mixing information about files in\nworking area and those in index), \"git diff-tree\" (especially for merges,\nas ordinary columnar output do not include all possible information,\nlike pre-image filename in case of renames), perhaps \"git branch\" so\npeople stop trying to parse it in scripts, perhaps \"git describe\"\n(you need \"git describe --long\" to unambiguously parse its output),\nperhaps \"git remote show\" (I am not sure about this case), \"git show-ref\"\nif \"git for-each-ref\" didn't exists...\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139556","messageId":"20100415065700.GA27542@coredump.intra.peff.net","threadId":"23423","inReplyTo":"7viq7toh12.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-15T06:57:00Z","receivedAt":"2010-04-15T06:57:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 14, 2010 at 02:34:01PM -0700, Junio C Hamano wrote:\n\n> Jakub Narebski <jnareb@gmail.com> writes:\n> \n> > Well, this whole idea started with the fact, that \"git status --short\"\n> > was hard (or impossible) to parse unambigously by scripts[1], and even\n> > \"git status --porcelain -z\"[2] is not that easy to parse[3].\n> \n> And you apparently seem to agree with that claim, but I don't.  I think\n> Jeff (who did the --porcelain stuff; by the way, why did we lose him from\n> Cc list?) has already said that he is open to an update.\n\nI haven't seen any evidence that status --porcelain (or its -z form) is\nimpossible to parse unambiguously. I don't even think it's that hard,\nbut it certainly could be easier. But more importantly, from looking at\nthe output it's not necessarily _obvious_ how to parse it correctly\n(e.g., whitespace as value and as field separator, syntax of \"-z\"\ndepends on semantics of field contents).\n\nThe approach I proposed was to leave it be and document it a bit better.\nAdding some format that is close but subtly different is just going to\nlead to more confusion.\n\nBut since Julian was willing to do the JSON work, I think that is a much\nnicer approach. It's not subtly different; it's very different and way\neasier to read and parse. And I'm really happy with the way he has\nstructured the code to handle multiple output formats. It keeps the code\nmuch cleaner, and it should silence any \"but YAML is better than JSON is\nbetter than XML\" debates.\n\nEven with Julian's patches, we should still better document the regular\nand \"-z\" forms. Eric promised to send some patches this week; I'm hoping\nhe is still interested in doing so after seeing a better solution arise.\n:)\n\n-Peff\n"},{"id":"139558","messageId":"20100415071540.GB27542@coredump.intra.peff.net","threadId":"23423","inReplyTo":"80f140cdddc016f9b4608d79f1bc3722@212.159.54.234","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-15T07:15:40Z","receivedAt":"2010-04-15T07:15:40Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 14, 2010 at 09:57:27PM +0100, Julian Phillips wrote:\n\n> > 'git blame' has --porcelain and --incremental output, which is\n> [...]\n> > JSON output would only add unnecessary chatter and different quoting\n> > rules.\n> \n> That depends really.  If you are writing something to parse the output,\n> and you already have a JSON parser available then it's the current output\n> that has different quoting rules. ;)\n\nEvery once in a while, I have some crazy idea for a short script that is\nbuilt around blame output (e.g., counting contributors by line count).\nSomething that I might do in a little one-off perl script. And my\nexperience has been that 90% of the script ends up parsing and managing\ncommit blocks, and not the computation of interest.\n\nNot that it's a lot of lines, mind you, but having to write 20 lines of\nparser to do a perl one-liner on the result is annoying. I would be very\nhappy to have some 1 or 2 line solution where one of the lines is \"use\nJSON;\".\n\n> Anyway, I have already converted blame to use the library for both\n> --porcelain and --incremental output, so it'll be in the next version of\n> the patch series.  So you can try before you buy ...\n\nI'll be curious to see it. I hope you will (at least optionally) wrap\nthe _whole_ output and not just the commit blocks. It would be nice to\njust suck it in all at once and walk the data structure. But it may be\ntricky because the output suppresses the commit description for commits\nthat have already been output. You would probably want a list of lines\nand a map of commits, like:\n\n  {\n    \"lines\": [\n      { sha1 and line info }\n      { sha1 and line info }\n      ...\n    ],\n    \"commits\": {\n      \"$sha1\": { commit info },\n      ...\n  }\n\nwhich is close to what I would parse to in a script, except I would\nactually drop the \"commits\" map and point directly to the commit info\nfrom each line.\n\nIs there a way in JSON to refer to the contents of a previous item\nwithout just outputting the same data again? I assume not, and even if\nthere is, other output formats like XML wouldn't handle it.\n\n-Peff\n"},{"id":"139567","messageId":"201004151107.33892.jnareb@gmail.com","threadId":"23423","inReplyTo":"20100415065700.GA27542@coredump.intra.peff.net","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-15T09:07:32Z","receivedAt":"2010-04-15T09:07:32Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Thu, 15 April 2010, Jeff King wrote:\n> On Wed, Apr 14, 2010 at 02:34:01PM -0700, Junio C Hamano wrote:\n> > Jakub Narebski <jnareb@gmail.com> writes:\n> > \n> > > Well, this whole idea started with the fact, that \"git status --short\"\n> > > was hard (or impossible) to parse unambigously by scripts[1], and even\n> > > \"git status --porcelain -z\"[2] is not that easy to parse[3].\n> > \n> > And you apparently seem to agree with that claim, but I don't.  I think\n> > Jeff (who did the --porcelain stuff; by the way, why did we lose him from\n> > Cc list?) has already said that he is open to an update.\n> \n> I haven't seen any evidence that status --porcelain (or its -z form) is\n> impossible to parse unambiguously. I don't even think it's that hard,\n> but it certainly could be easier. But more importantly, from looking at\n> the output it's not necessarily _obvious_ how to parse it correctly\n> (e.g., whitespace as value and as field separator, syntax of \"-z\"\n> depends on semantics of field contents).\n\nWell, IMVHO output of \"git status --short\" / \"git status --porcelain\"\n(without '-z') is very hard to parse.  Even assuming that in the case\nof ambiguity filenames are quoted (which also means that in the case of\nambiguity whether they are quoted they must be quoted), the fact that\nseparator between source and destination filename in the case of rename\ndetection is \" -> \" (if I understand it correctly), and neither of ' '\n(SPC), '-' nor '>' is replaced by escape sequence means that one needs\nto detect where quoted filename begins and where ends.  This means\neither parsing character by character, taking into account quoting and\nescaping (e.g. '\\\\', '\\\"' etc.), or using 'balanced quote' regexp like\nthe one from Text::Balanced, e.g.:  (?:\\\"(?:[^\\\\\\\"]*(?:\\\\.[^\\\\\\\"]*)*)\\\")\n\nWhat was the reason behind choosing \" -> \" as separator between pair[1]\nof filenames in rename, instead of using default \"git diff --stat\" format\ni.e. 'arch/{i386 => x86}/Makefile' for \"git status --short\" which is\nmeant for end user, and for \"git status --porcelain\" the same format \nthat raw diff format, i.e. with TAB as separator between filenames,\nand filename quited if it contains TAB (then TAB is relaced by '\\t',\nand does not appear in filename, therefore you can split on TAB)?\n\nIMVHO \"git status --porcelain -z\" format is not easy to parse either.\n(The same can be said for \"git diff --raw -z\" output format.)  You\ncan't just split on record separator; you have to take into account\nstatus to check if there are two filenames or one.\n\n[1] A question: we have working area version, index version, and HEAD\n    version of file.  Isn't it possible for *each* of them to have \n    different filename?  What about the case of rename/rename merge\n    conflict?\n> \n> The approach I proposed was to leave it be and document it a bit better.\n> Adding some format that is close but subtly different is just going to\n> lead to more confusion.\n\nWell, the proposed '-Z' output format, in the OFS=\"\\0\", ORS=\"\\0\\0\"\nvariant, would be very easy to parse.  If I understand it correctly\nit is also one of available format in outputification^W in this series.\n\n> \n> But since Julian was willing to do the JSON work, I think that is a much\n> nicer approach. It's not subtly different; it's very different and way\n> easier to read and parse. And I'm really happy with the way he has\n> structured the code to handle multiple output formats. It keeps the code\n> much cleaner, and it should silence any \"but YAML is better than JSON is\n> better than XML\" debates.\n\nI really like this outputification ;-) too.\n\nAlthough if possible I'd like to have it wrapped in utility macros,\nlike parseopt, so one does not need to write output_str / output_int\netc.... but currently it is very, very vague sketch of an idea, rather\nthan realized concept.\n\n> \n> Even with Julian's patches, we should still better document the regular\n> and \"-z\" forms. Eric promised to send some patches this week; I'm hoping\n> he is still interested in doing so after seeing a better solution arise.\n> :)\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139728","messageId":"20100417095259.GA23110@coredump.intra.peff.net","threadId":"23423","inReplyTo":"201004151107.33892.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-17T09:53:00Z","receivedAt":"2010-04-17T09:53:00Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Apr 15, 2010 at 11:07:32AM +0200, Jakub Narebski wrote:\n\n> Well, IMVHO output of \"git status --short\" / \"git status --porcelain\"\n> (without '-z') is very hard to parse.  Even assuming that in the case\n> of ambiguity filenames are quoted (which also means that in the case of\n> ambiguity whether they are quoted they must be quoted), the fact that\n\nFor the record, they are properly quoted in the non-z form.\n\n> [some reasons it's hard to parse]\n\nYeah, I don't disagree with your reasons (which are largely the same as\nEric's). I just don't think it's \"oh no this is useless and we have to\nstart again\" hard.\n\n> What was the reason behind choosing \" -> \" as separator between pair[1]\n> of filenames in rename, instead of using default \"git diff --stat\" format\n> i.e. 'arch/{i386 => x86}/Makefile' for \"git status --short\" which is\n> meant for end user, and for \"git status --porcelain\" the same format \n> that raw diff format, i.e. with TAB as separator between filenames,\n> and filename quited if it contains TAB (then TAB is relaced by '\\t',\n> and does not appear in filename, therefore you can split on TAB)?\n\nI don't know Junio's reason for using \" -> \" in --short; probably\nbecause it was the format used in non-short status. For --porcelain, it\nwas simply because I used exactly --short. I assumed that --short was\nsuitable for parsing (which it _is_, it just has some rough edges), and\nwanted to provide an option right away that would keep the output\nstable, so we didn't run into the usual problem of people wanting to\nenhance the human-readable interface, but being blocked by script\ncompatibility.\n\n> IMVHO \"git status --porcelain -z\" format is not easy to parse either.\n> (The same can be said for \"git diff --raw -z\" output format.)  You\n> can't just split on record separator; you have to take into account\n> status to check if there are two filenames or one.\n\nYep, I agree. I think the JSON approach is the best solution, as it is\nseparating syntax from semantics.\n\n> [1] A question: we have working area version, index version, and HEAD\n>     version of file.  Isn't it possible for *each* of them to have \n>     different filename?  What about the case of rename/rename merge\n>     conflict?\n\nGood question. The answer is no, the three different versions can't have\nthree filenames on the same line, because we don't do rename detection\nbetween the working tree and the index. Which makes sense. Consider\nsomething like this:\n\n  mkdir repo && cd repo && git init\n  echo content >one\n  git add one && git commit -m one\n  mv one two && git add -A\n  mv two three\n  git status\n\nWe will see the movement of \"one -> two\" between the index and HEAD. In\ntheory we could see the movement of \"three -> two\" between the index and\nworking tree. But \"three\" isn't tracked, so instead we see \"two\" deleted\nand \"three\" untracked. We can mark \"three\" with intent-to-add to note\nthat we are interested in it, but then it is not a new file any more\n(since it has an index entry), and is therefore not eligible for rename\ndetection.\n\nAs for a rename/rename conflict, it gets represented in the index as\nboth deleting the source and then each side adding its new version with\na conflict. So:\n\n  mkdir repo && cd repo && git init\n  echo content >one\n  git add one && git commit -m base\n  git mv one two && git commit -m two\n  git checkout -b other HEAD^\n  git mv one three && git commit -m three\n  git merge master\n  git status\n\ngenerates:\n\n  # On branch other\n  # Unmerged paths:\n  #       both deleted:       one\n  #       added by us:        three\n  #       added by them:      two\n\nand an equivalent short-status form.\n\n> Although if possible I'd like to have it wrapped in utility macros,\n> like parseopt, so one does not need to write output_str / output_int\n> etc.... but currently it is very, very vague sketch of an idea, rather\n> than realized concept.\n\nI'm not sure I understand what utility macros you would want.\n\n-Peff\n"},{"id":"139747","messageId":"201004171502.42044.jnareb@gmail.com","threadId":"23423","inReplyTo":"20100417095259.GA23110@coredump.intra.peff.net","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-17T13:02:39Z","receivedAt":"2010-04-17T13:02:39Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sat, 17 Apr 2010, Jeff King wrote:\n> On Thu, Apr 15, 2010 at 11:07:32AM +0200, Jakub Narebski wrote:\n\n> > [1] A question: we have working area version, index version, and HEAD\n> >     version of file.  Isn't it possible for *each* of them to have \n> >     different filename?  What about the case of rename/rename merge\n> >     conflict?\n\n[cut]\n\nThanks for detailed explanation.\n\n> > Although if possible I'd like to have it wrapped in utility macros,\n> > like parseopt, so one does not need to write output_str / output_int\n> > etc.... but currently it is very, very vague sketch of an idea, rather\n> > than realized concept.\n> \n> I'm not sure I understand what utility macros you would want.\n\nSomething like that (please remember that it is still in vague beginnings\nof an idea stage:\n\n  OUT_OBJECT(\n     OUT_FIELD(\"mode\",   OUT_MODE, tree.mode), SP,\n     OUT_FIELD(\"type\",   \"%s\", tree.object.type), SP,\n     OUT_FIELD(\"object\", OUT_SHA1, tree.object.sha1), TAB,\n     OUT_FIELD(\"file\", OUT_FILE(sep), tree.filename), \n     sep\n  );\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139748","messageId":"20100417140053.GA10997@coredump.intra.peff.net","threadId":"23423","inReplyTo":"201004171502.42044.jnareb@gmail.com","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-17T14:00:53Z","receivedAt":"2010-04-17T14:00:53Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Apr 17, 2010 at 03:02:39PM +0200, Jakub Narebski wrote:\n\n> Something like that (please remember that it is still in vague beginnings\n> of an idea stage:\n> \n>   OUT_OBJECT(\n>      OUT_FIELD(\"mode\",   OUT_MODE, tree.mode), SP,\n>      OUT_FIELD(\"type\",   \"%s\", tree.object.type), SP,\n>      OUT_FIELD(\"object\", OUT_SHA1, tree.object.sha1), TAB,\n>      OUT_FIELD(\"file\", OUT_FILE(sep), tree.filename), \n>      sep\n>   );\n\nDoing that would require variadic macros, which are a C99-ism. So you\nwould have to do:\n\n  OUT_OBJECT_START();\n    OUT_FIELD(\"mode\", OUT_MODE, tree.mode); OUT_SP;\n    ...\n  OUT_OBJECT_END();\n\nwhich is not all that different from what Julian has now. I do think\nsome type-specific conversions might be handy. They don't even need to\nbe macros. E.g.,:\n\n  void output_mode(struct output_context *oc, int mode)\n  {\n    output_strf(oc, \"mode\", \"%06o\", mode);\n  }\n\nOTOH, looking over Julian's last patch series, there really aren't that\nmany that would be generally applicable, and as you can see they only\nsave a few characters, not even a line. A few bigger objects could be\nfactored out, but he has already done that (e.g., see\nwt_porcelain_unmerged in his v2 3/4).\n\n-Peff\n"},{"id":"139849","messageId":"203d6cefd3cd1020eb94fbd3d5e25eae@212.159.54.234","threadId":"23423","inReplyTo":"20100417140053.GA10997@coredump.intra.peff.net","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output (inc. current status)","fromName":"Julian Phillips","fromEmail":"julian@quantumfyre.co.uk","sentAt":"2010-04-18T21:46:18Z","receivedAt":"2010-04-18T21:46:18Z","isPatch":true,"sender":{"key":"julian@quantumfyre.co.uk","avatar":"https://avatars.githubusercontent.com/u/948888?v=4"},"body":"On Sat, 17 Apr 2010 10:00:53 -0400, Jeff King <peff@peff.net> wrote:\n> On Sat, Apr 17, 2010 at 03:02:39PM +0200, Jakub Narebski wrote:\n> \n>> Something like that (please remember that it is still in vague\nbeginnings\n>> of an idea stage:\n>> \n>>   OUT_OBJECT(\n>>      OUT_FIELD(\"mode\",   OUT_MODE, tree.mode), SP,\n>>      OUT_FIELD(\"type\",   \"%s\", tree.object.type), SP,\n>>      OUT_FIELD(\"object\", OUT_SHA1, tree.object.sha1), TAB,\n>>      OUT_FIELD(\"file\", OUT_FILE(sep), tree.filename), \n>>      sep\n>>   );\n> \n> Doing that would require variadic macros, which are a C99-ism. So you\n> would have to do:\n> \n>   OUT_OBJECT_START();\n>     OUT_FIELD(\"mode\", OUT_MODE, tree.mode); OUT_SP;\n>     ...\n>   OUT_OBJECT_END();\n\nAlso, backends such as JSON want to know which things are strings, and\nwhich are numbers - as they print differently.  An XML backend may want to\ndistinguish even more (though I guess that depends on the design).\n\n> which is not all that different from what Julian has now. I do think\n> some type-specific conversions might be handy. They don't even need to\n> be macros. E.g.,:\n> \n>   void output_mode(struct output_context *oc, int mode)\n>   {\n>     output_strf(oc, \"mode\", \"%06o\", mode);\n>   }\n> \n> OTOH, looking over Julian's last patch series, there really aren't that\n> many that would be generally applicable, and as you can see they only\n> save a few characters, not even a line. A few bigger objects could be\n> factored out, but he has already done that (e.g., see\n> wt_porcelain_unmerged in his v2 3/4).\n\nIt might help standardise the output between commands if there were helper\nfunctions for some of the larger structures - e.g. commits.  Though I don't\nthink that those functions would be able to do legacy output, due to the\ncurrent lack of cross-command output compatibility.  I'm starting to see\nthis with blame and diff-tree (and family), where they both want to output\ninformation about commits.\n\nI think that maybe I need to design and document the output structure for\ncommon concepts - so that it would be possible to pass the output from any\ncommand to a common parser, with matching utility functions in the code. \nThough, I'm not sure if there actually are any common concepts that need\noutputting apart from commits.\n\nCurrent Status\n--------------\n\nI had been planning to post an updated series this weekend, but I'm too\ntired to attempt tidying things up for posting at the moment ... If you\nwant to see the current state then my current mess is available at\nhttp://git.q42.co.uk/w/output.git.\n\nA quick summary of main changes since v2:\n  - backends are now in a subdirectory\n  - blame, diff-tree, have --ouptut=... support for plumbing output\n  - log has some support for --ouput=...\n  - output library has extended API, including quoted strings and\nis_structured_output function\n  - backend API includes explicit functions for top-level items\n\n-- \nJulian\n"},{"id":"139923","messageId":"20100419194020.GA25883@coredump.intra.peff.net","threadId":"23423","inReplyTo":"203d6cefd3cd1020eb94fbd3d5e25eae@212.159.54.234","subject":"Re: [RFC/PATCH v2 0/4] A new library for plumbing output (inc. current status)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-19T19:40:20Z","receivedAt":"2010-04-19T19:40:20Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sun, Apr 18, 2010 at 10:46:18PM +0100, Julian Phillips wrote:\n\n> It might help standardise the output between commands if there were helper\n> functions for some of the larger structures - e.g. commits.  Though I don't\n> think that those functions would be able to do legacy output, due to the\n> current lack of cross-command output compatibility.  I'm starting to see\n> this with blame and diff-tree (and family), where they both want to output\n> information about commits.\n\nYeah, that was what I saw on looking at the code. And we have to support\nthose old formats, obviously. For the most part, I found the level of\nverbosity in the patches you posted (and I just peeked at your repo) to\nbe fine. Sure, it's more lines, but they're IMHO very easy to read.\n\nIf we have to tradeoff between either duplicating output entirely (for\nboth the output form and traditional form) or having a more flexible but\nslightly more verbose output library, I think I would rather go with the\nlatter. It will be more maintainable in the long run.\n\n-Peff\n"}]}