{"thread":{"id":"23397","subject":"More git status --porcelain lossage","startedAt":"2010-04-09T19:06:01Z","lastAt":"2010-04-11T11:04:06Z","messageCount":18,"participants":["Eric Raymond","Jakub Narebski","Jeff King","Simon","Ævar Arnfjörð Bjarmason","Martin Langhoff","Paolo Bonzini","David Aguilar"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"139061","messageId":"20100409190601.47B37475FEF@snark.thyrsus.com","threadId":"23397","inReplyTo":null,"subject":"More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@snark.thyrsus.com","sentAt":"2010-04-09T19:06:01Z","receivedAt":"2010-04-09T19:06:01Z","isPatch":false,"sender":{"key":"esr@snark.thyrsus.com","avatar":null},"body":"After I posted my last, I noticed another crash landing...\n\nA format properly designed for script parseability should use even use\nwhitespace as a field separator.\n\nWhy?\n\nBecause if you do that, front ends *will* do field analysis using a\nnaive split-on-whitespace operation.  And then...someday...someone\nwill try to run one of these of these on a volume from a system where\nfilenames contain embedded whitespace.  Like Mac OS X or Windows.\n\nHilarity will ensue.\n\nConclusion: As it is presently, git status --porcelain format is\nirretrievably botched.  You need a field separator that's musch less\nlikely to land in a filename, like '|' - and to warn in the documentation\nthat careful front ends must check for and ignore '\\|'. \n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n\nThe right of the citizens to keep and bear arms has justly been considered as\nthe palladium of the liberties of a republic; since it offers a strong moral\ncheck against usurpation and arbitrary power of rulers; and will generally,\neven if these are successful in the first instance, enable the people to resist\nand triumph over them.\"\n        -- Supreme Court Justice Joseph Story of the John Marshall Court\n"},{"id":"139062","messageId":"20100409190936.GA15170@thyrsus.com","threadId":"23397","inReplyTo":"20100409190601.47B37475FEF@snark.thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-09T19:09:36Z","receivedAt":"2010-04-09T19:09:36Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Eric Raymond <esr@snark.thyrsus.com>:\n> A format properly designed for script parseability should use even use\n> whitespace as a field separator.\n\nshould *not* even use... \n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139064","messageId":"m3ochsh1oc.fsf@localhost.localdomain","threadId":"23397","inReplyTo":"20100409190601.47B37475FEF@snark.thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-09T19:22:22Z","receivedAt":"2010-04-09T19:22:22Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Eric Raymond <esr@snark.thyrsus.com> writes:\n\n> After I posted my last, I noticed another crash landing...\n> \n> A format properly designed for script parseability should use even use\n> whitespace as a field separator.\n> \n> Why?\n> \n> Because if you do that, front ends *will* do field analysis using a\n> naive split-on-whitespace operation.  And then...someday...someone\n> will try to run one of these of these on a volume from a system where\n> filenames contain embedded whitespace.  Like Mac OS X or Windows.\n> \n> Hilarity will ensue.\n> \n> Conclusion: As it is presently, git status --porcelain format is\n> irretrievably botched.  You need a field separator that's musch less\n> likely to land in a filename, like '|' - and to warn in the documentation\n> that careful front ends must check for and ignore '\\|'. \n\nOr follow what other porcelain does, like git-diff-tree raw output\nformat, where all fields except final filename are space separated,\nfilename is separated by tab character (or NUL when '-z' options is\nused).  If there are two names (in the case of copy or renames),\nthey are separated by a tab (or NUL).  Record ends with LF (or NUL).\n\nWhen '-z' option is not used, TAB, LF, \" and backslash characters\nare represented by '\\t', '\\n', '\\\"' and \\\\, and the filename is\nenclosed in '\"' doublequotes.\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"139069","messageId":"20100409195029.GA15810@thyrsus.com","threadId":"23397","inReplyTo":"m3ochsh1oc.fsf@localhost.localdomain","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-09T19:50:29Z","receivedAt":"2010-04-09T19:50:29Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Jakub Narebski <jnareb@gmail.com>:\n> > Conclusion: As it is presently, git status --porcelain format is\n> > irretrievably botched.  You need a field separator that's musch less\n> > likely to land in a filename, like '|' - and to warn in the documentation\n> > that careful front ends must check for and ignore '\\|'. \n> \n> Or follow what other porcelain does, like git-diff-tree raw output\n> format, where all fields except final filename are space separated,\n> filename is separated by tab character (or NUL when '-z' options is\n> used).  If there are two names (in the case of copy or renames),\n> they are separated by a tab (or NUL).  Record ends with LF (or NUL).\n> \n> When '-z' option is not used, TAB, LF, \" and backslash characters\n> are represented by '\\t', '\\n', '\\\"' and \\\\, and the filename is\n> enclosed in '\"' doublequotes.\n\nThat would be a bit trickier to parse, but acceptable.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139103","messageId":"20100410041247.GB11977@coredump.intra.peff.net","threadId":"23397","inReplyTo":"20100409190601.47B37475FEF@snark.thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-10T04:12:48Z","receivedAt":"2010-04-10T04:12:48Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Fri, Apr 09, 2010 at 03:06:01PM -0400, Eric Raymond wrote:\n\n> A format properly designed for script parseability should use even use\n> whitespace as a field separator.\n> \n> Why?\n> \n> Because if you do that, front ends *will* do field analysis using a\n> naive split-on-whitespace operation.  And then...someday...someone\n> will try to run one of these of these on a volume from a system where\n> filenames contain embedded whitespace.  Like Mac OS X or Windows.\n\nYes, that is why almost every scriptable git interface supports a \"-z\"\nvariant with NUL termination.\n\n> Conclusion: As it is presently, git status --porcelain format is\n> irretrievably botched.  You need a field separator that's musch less\n> likely to land in a filename, like '|' - and to warn in the documentation\n> that careful front ends must check for and ignore '\\|'.\n\nWe already quote correctly, so it is only sloppy parsers that will be in\ntrouble. Yes, space is more common than \"|\", but sloppy is sloppy. Parse\nit right, or use \"-z\".\n\n-Peff\n"},{"id":"139104","messageId":"20100410041442.GC11977@coredump.intra.peff.net","threadId":"23397","inReplyTo":"20100410041247.GB11977@coredump.intra.peff.net","subject":"Re: More git status --porcelain lossage","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2010-04-10T04:14:42Z","receivedAt":"2010-04-10T04:14:42Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Apr 10, 2010 at 12:12:48AM -0400, Jeff King wrote:\n\n> > Conclusion: As it is presently, git status --porcelain format is\n> > irretrievably botched.  You need a field separator that's musch less\n> > likely to land in a filename, like '|' - and to warn in the documentation\n> > that careful front ends must check for and ignore '\\|'.\n> \n> We already quote correctly, so it is only sloppy parsers that will be in\n> trouble. Yes, space is more common than \"|\", but sloppy is sloppy. Parse\n> it right, or use \"-z\".\n\nBTW, this should go on your \"git status --porcelain documentation\nfailures\" list. We really need to note that the output paths may be\nquoted.\n\n-Peff\n"},{"id":"139180","messageId":"l2k5f14cf5e1004101148h5cf8dc4bm1836cf1c5fc8abfb@mail.gmail.com","threadId":"23397","inReplyTo":"20100409190601.47B37475FEF@snark.thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Simon","fromEmail":"turner25@gmail.com","sentAt":"2010-04-10T18:48:05Z","receivedAt":"2010-04-10T18:48:05Z","isPatch":false,"sender":{"key":"turner25@gmail.com","avatar":null},"body":"> A format properly designed for script parseability should use even use\n> whitespace as a field separator.\n>\n> Why?\n>\n> Because if you do that, front ends *will* do field analysis using a\n> naive split-on-whitespace operation.  And then...someday...someone\n> will try to run one of these of these on a volume from a system where\n> filenames contain embedded whitespace.  Like Mac OS X or Windows.\n\nWhy not use an XML output?\nPlain text is easier to parse, but XML may give this extra durability\nyou are looking for?\n\nSimon\n"},{"id":"139181","messageId":"m3k4sfgmjc.fsf@localhost.localdomain","threadId":"23397","inReplyTo":"l2k5f14cf5e1004101148h5cf8dc4bm1836cf1c5fc8abfb@mail.gmail.com","subject":"Re: More git status --porcelain lossage","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-10T19:01:41Z","receivedAt":"2010-04-10T19:01:41Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Simon <turner25@gmail.com> writes:\n\n> > A format properly designed for script parseability should use even use\n> > whitespace as a field separator.\n> >\n> > Why?\n> >\n> > Because if you do that, front ends *will* do field analysis using a\n> > naive split-on-whitespace operation.  And then...someday...someone\n> > will try to run one of these of these on a volume from a system where\n> > filenames contain embedded whitespace.  Like Mac OS X or Windows.\n> \n> Why not use an XML output?\n> Plain text is easier to parse, but XML may give this extra durability\n> you are looking for?\n\nAre out of your f**g mind?  XML, really?  XML might be good choice to\n*define* _document_ formats, but is really poor data exchange /\nserialization format (being overly verbose, among others).  Also, XML\nis not language but meta-language.\n\nI could understand providing JSON format, specified using --json\noption.  I think there is some GPLv2 compatibile JSON generating code\nin C (MIT licensed code is GPLv2 compatibilie, isn't it?); we can\nalways borrow compact JSON generation code from GPSD project (if\nlicense allows it) from ESR.\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"139185","messageId":"20100410193039.GA28768@thyrsus.com","threadId":"23397","inReplyTo":"l2k5f14cf5e1004101148h5cf8dc4bm1836cf1c5fc8abfb@mail.gmail.com","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-10T19:30:39Z","receivedAt":"2010-04-10T19:30:39Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Simon <turner25@gmail.com>:\n> Why not use an XML output?\n> Plain text is easier to parse, but XML may give this extra durability\n> you are looking for?\n\nBecause XML is awfully heavyewight, and XML parsers tend to be slow.\n\nIf we were going to buld on a metaprotocol, JSON would be better.  IMHO.  \n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139188","messageId":"h2t51dd1af81004101239pe340cc54lcbb1a5b3702ec091@mail.gmail.com","threadId":"23397","inReplyTo":"20100410193039.GA28768@thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2010-04-10T19:39:59Z","receivedAt":"2010-04-10T19:39:59Z","isPatch":false,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"On Sat, Apr 10, 2010 at 19:30, Eric Raymond <esr@thyrsus.com> wrote:\n> Simon <turner25@gmail.com>:\n>> Why not use an XML output?\n>> Plain text is easier to parse, but XML may give this extra durability\n>> you are looking for?\n>\n> Because XML is awfully heavyewight, and XML parsers tend to be slow.\n>\n> If we were going to buld on a metaprotocol, JSON would be better.  IMHO.\n\nA lot of web services (like some Catalyst-based applications) support\nall of these equally. If Git had machine readable output like this it\nwould be nice if every git-* program just had --format=* where * could\nbe xml, json, yaml, sexp, perl etc.\n\nThe program would just construct a native datastructure and then there\nwould be an output driver to generate the textual representation.\n"},{"id":"139187","messageId":"20100410194154.GB28768@thyrsus.com","threadId":"23397","inReplyTo":"m3k4sfgmjc.fsf@localhost.localdomain","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-10T19:41:54Z","receivedAt":"2010-04-10T19:41:54Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Jakub Narebski <jnareb@gmail.com>:\n> Are out of your f**g mind?  XML, really?  XML might be good choice to\n> *define* _document_ formats, but is really poor data exchange /\n> serialization format (being overly verbose, among others).  Also, XML\n> is not language but meta-language.\n\nAgreed.\n \n> I could understand providing JSON format, specified using --json\n> option.\n\nYou know, that's actually an interesting idea.  I mentioned it\npreviously as the not-XML if we want to build on a metaprotocol;\nI wasn't considering it seriously then.  But I am now, and it is\nnot without attractions.  JSON would certainly solve all the delimiter\nand empty-object edge cases, and it has excellent extensibility.\n\n>    I think there is some GPLv2 compatibile JSON generating code\n> in C (MIT licensed code is GPLv2 compatibilie, isn't it?); we can\n> always borrow compact JSON generation code from GPSD project (if\n> license allows it) from ESR.\n\nMy license would allow it, but there's not really a lot of win in \ntrying to reuse JSON generator code - writing your own printfs for\nit by hand is easy and fast.\n\nEmacs Lisp has a JSON parser, so it would meet my needs.\n\nAlternatively, a cleaned-up --porcelain -Z along the lines\npreviously suggested would be good.\n\nSupplying both might not be a bad idea.  The volume of code involved\nwould be low.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139194","messageId":"s2i46a038f91004101331g1cdca78cya3e125275446a0a9@mail.gmail.com","threadId":"23397","inReplyTo":"20100410194154.GB28768@thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2010-04-10T20:31:55Z","receivedAt":"2010-04-10T20:31:55Z","isPatch":false,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On Sat, Apr 10, 2010 at 3:41 PM, Eric Raymond <esr@thyrsus.com> wrote:\n>> I could understand providing JSON format, specified using --json\n>> option.\n>\n> You know, that's actually an interesting idea.  I mentioned it\n> previously as the not-XML if we want to build on a metaprotocol;\n\nOne issue is that there's no stream-parser JSON implementations that\nI'm aware of.\n\nEverthing I've seen is in-memory, therefore apt only for memory-bound\noperations. Not sure if all commands with -z output options can be\nassumed to produce bound-sized datasets.\n\ncheers,\n\n\nmartin\n-- \n martin.langhoff@gmail.com\n martin@laptop.org -- School Server Architect\n - ask interesting questions\n - don't get distracted with shiny stuff  - working code first\n - http://wiki.laptop.org/go/User:Martinlanghoff\n"},{"id":"139199","messageId":"201004102321.59263.jnareb@gmail.com","threadId":"23397","inReplyTo":"20100410194154.GB28768@thyrsus.com","subject":"Re: More git status --porcelain lossage","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2010-04-10T21:21:58Z","receivedAt":"2010-04-10T21:21:58Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"On Sat, 10 Apr 2010, Eric Raymond wrote:\n> Jakub Narebski <jnareb@gmail.com>:\n>  \n> > I could understand providing JSON format, specified using --json\n> > option.\n> \n> You know, that's actually an interesting idea.  I mentioned it\n> previously as the not-XML if we want to build on a metaprotocol;\n> I wasn't considering it seriously then.  But I am now, and it is\n> not without attractions.  JSON would certainly solve all the delimiter\n> and empty-object edge cases, and it has excellent extensibility.\n\nIt is a bit chatty, but is to some extent self documenting.\n\nThe question is whether it should output well formed array of objects,\nor just list of objects not wrapped in array...\n\n> >    I think there is some GPLv2 compatibile JSON generating code\n> > in C (MIT licensed code is GPLv2 compatibilie, isn't it?); we can\n> > always borrow compact JSON generation code from GPSD project (if\n> > license allows it) from ESR.\n> \n> My license would allow it, but there's not really a lot of win in \n> trying to reuse JSON generator code - writing your own printfs for\n> it by hand is easy and fast.\n\nWhat I am worrying about is correct handling of escaping, quoting,\nand non-ASCII characters in strings (the JSON-quoting and JSON-escapes\nare different than C escape codes, IIRC).  JSON rules are simple,\nbut are different than C.\n\n-- \nJakub Narebski\nPoland\n"},{"id":"139200","messageId":"p2u5f14cf5e1004101424q2d6a871fs2466988f7a4202fe@mail.gmail.com","threadId":"23397","inReplyTo":"h2t51dd1af81004101239pe340cc54lcbb1a5b3702ec091@mail.gmail.com","subject":"Re: More git status --porcelain lossage","fromName":"Simon","fromEmail":"turner25@gmail.com","sentAt":"2010-04-10T21:24:35Z","receivedAt":"2010-04-10T21:24:35Z","isPatch":false,"sender":{"key":"turner25@gmail.com","avatar":null},"body":"> A lot of web services (like some Catalyst-based applications) support\n> all of these equally. If Git had machine readable output like this it\n> would be nice if every git-* program just had --format=* where * could\n> be xml, json, yaml, sexp, perl etc.\n>\n> The program would just construct a native datastructure and then there\n> would be an output driver to generate the textual representation.\n>\n\nI had something just like this in mind when I suggested XML...\nI would personally avoid it for same reasons others have pointed out, but...\nThere are lots of tools out there that can parse and display XML very\nwell natively.  Firefox is one such example.\n\nMy intention is not to start a flame here, rather try to keep our\noptions flexible.  ASCII would clearly remain the default though! ;)\n\nSimon\n"},{"id":"139207","messageId":"4BC0FB94.6050409@gnu.org","threadId":"23397","inReplyTo":"s2i46a038f91004101331g1cdca78cya3e125275446a0a9@mail.gmail.com","subject":"Re: More git status --porcelain lossage","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2010-04-10T22:28:36Z","receivedAt":"2010-04-10T22:28:36Z","isPatch":false,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"On 04/10/2010 10:31 PM, Martin Langhoff wrote:\n> On Sat, Apr 10, 2010 at 3:41 PM, Eric Raymond<esr@thyrsus.com>  wrote:\n>>> I could understand providing JSON format, specified using --json\n>>> option.\n>>\n>> You know, that's actually an interesting idea.  I mentioned it\n>> previously as the not-XML if we want to build on a metaprotocol;\n>\n> One issue is that there's no stream-parser JSON implementations that\n> I'm aware of.\n\nHere is one.  It's ugly as hell, you're warned.  The only missing piece \nis making the stack state resizable.\n\nPaolo\n\n\n/*\n * An event-based, asynchronous JSON parser.\n *\n * Copyright (C) 2009 Red Hat Inc.\n *\n * Authors:\n *  Paolo Bonzini <pbonzini@redhat.com>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n\n#include \"json.h\"\n#include <string.h>\n#include <stdlib.h>\n\n/* Common character classes.  */\n\n#define CASE_XDIGIT \\\n        case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': \\\n        case 'A': case 'B': case 'C': case 'D': case 'E': case 'F'\n\n#define CASE_DIGIT \\\n        case '0': case '1': case '2': case '3': case '4': \\\n        case '5': case '6': case '7': case '8': case '9'\n\n/* Helper function to go from \\uXXXX-encoded UTF-16 to UTF-8.  */\n\nstatic bool hex_to_utf8 (char *buf, char **dest, char *src)\n{\n    int i, n;\n    uint8_t *p;\n\n    for (i = n = 0; i < 4; i++) {\n        n <<= 4;\n        switch (src[i])\n        {\n        CASE_DIGIT: n |= src[i] - '0'; break;\n        CASE_XDIGIT: n |= (src[i] & ~32) - 'A' + 10; break;\n        default: return false;\n        }\n    }\n\n    p = (uint8_t *)*dest;\n    if (n < 128) {\n        *p++ = n;\n    } else if (n < 2048) {\n        *p++ = 0xC0 | (n >> 6);\n        *p++ = 0x80 | (n & 63);\n    } else if (n < 0xDC00 || n > 0xDFFF) {\n        *p++ = 0xE0 | (n >> 12);\n        *p++ = 0x80 | ((n >> 6) & 63);\n        *p++ = 0x80 | (n & 63);\n    } else {\n        /* Merge with preceding high surrogate.  */\n        if (p - (uint8_t *)buf < 3\n            || p[-3] != 0xED\n            || p[-2] < 0xA0 || p[-2] > 0xAF) /* 0xD800..0xDBFF */\n            return false;\n\n        n += 0x10000 - 0xDC00;\n        n += ((p[-2] & 15) << 16) | ((p[-1] & 63) << 10);\n\n        /* Overwrite high surrogate.  */\n        p[-3] = 0xF0 | (n >> 18);\n        p[-2] = 0x80 | ((n >> 12) & 63);\n        p[-1] = 0x80 | ((n >> 6) & 63);\n        *p++ = 0x80 | (n & 63);\n    }\n    *dest = (char *)p;\n    return true;\n}\n\nstruct json_parser {\n    struct    json_parser_config c;\n    size_t    n, alloc;\n    char      *buf;\n    size_t    sp;\n    uint32_t  state, stack[128];\n    char      start_buffer[128];\n};\n\n/* Managing the state stack.  */\n\nstatic inline void push_state (struct json_parser *p, uint32_t state)\n{\n    p->stack[p->sp++] = p->state;\n    p->state = state;\n}\n\nstatic inline void pop_state (struct json_parser *p)\n{\n    p->state = p->stack[--p->sp];\n}\n\n\n/* Managing the string/number buffer.  */\n\nstatic inline void clear_buffer (struct json_parser *p)\n{\n    p->n = 0;\n}\n\nstatic inline void push_buffer (struct json_parser *p, char c)\n{\n    if (p->n == p->alloc) {\n        size_t new_alloc = p->alloc * 2;\n        if (p->buf == p->start_buffer) {\n            p->buf = malloc (new_alloc);\n            memcpy (p->buf, p->start_buffer, p->alloc);\n        } else {\n            p->buf = realloc (p->buf, new_alloc);\n        }\n        p->alloc = new_alloc;\n    }\n    p->buf[p->n++] = c;\n}\n\n\n/*\n * Parser states are organized like this:\n *   bit 0-7:   enum parser_state\n *   bit 8-15:  for IN_KEYWORD, index in keyword table\n *   bit 16-31: additional substate (enum parser_cookies)\n */\n\nenum parser_state {\n    START_PARSE,                /* at start of parsing */\n    IN_KEYWORD,                 /* parsing keyword (match exactly) */\n    START_KEY,                  /* expecting key */\n    END_KEY,                    /* expecting colon */\n    START_VALUE,                /* expecting value */\n    END_VALUE,                  /* expecting comma or closing parenthesis */\n    IN_NUMBER,                  /* parsing number (up to whitespace) */\n    IN_STRING,                  /* parsing string */\n    IN_STRING_BACKSLASH,        /* parsing string, copy one char verbatim */\n    IN_COMMENT,                 /* comment mini-scanner */\n};\n\nenum parser_cookies {\n    IN_UNUSED,\n\n    IN_TRUE,                    /* for IN_KEYWORD */\n    IN_FALSE,\n    IN_NULL,\n\n    IN_ARRAY,                   /* for {START,END}_{KEY,VALUE} */\n    IN_DICT,\n\n    IN_KEY,                     /* for IN_STRING */\n    IN_VALUE,\n};\n\n#define STATE(state, cookie) \\\n    (((cookie) << 16) | (state))\n\n#define STATE_KEYWORD(n, cookie) \\\n    (((cookie) << 16) | ((n) << 8) | IN_KEYWORD)\n\nstatic const char keyword_table[] = \"rue\\0alse\\0ull\";\nenum keyword_indices {\n    KW_TRUE = 0,\n    KW_FALSE = 4,\n    KW_NULL = 9,\n};\n\n\n\n/* Parser actions.  These transfer to the appropriate state,\n * and invoke the callbacks.\n *\n * If there is a begin/end pair, begin pushes a state\n * and end pops it.\n */\n\nstatic inline bool array_begin (struct json_parser *p)\n{\n    push_state (p, STATE (START_VALUE, IN_ARRAY));\n    return !p->c.array_begin || p->c.array_begin (p->c.data);\n}\n\nstatic inline bool array_end (struct json_parser *p)\n{\n    int state_cookie = (p->state >> 16);\n    if (state_cookie != IN_ARRAY) return false;\n    pop_state (p);\n    return !p->c.array_end || p->c.array_end (p->c.data);\n}\n\n\nstatic inline bool object_begin (struct json_parser *p)\n{\n    push_state (p, STATE (START_KEY, IN_DICT));\n    return !p->c.object_begin || p->c.object_begin (p->c.data);\n}\n\nstatic inline bool object_end (struct json_parser *p)\n{\n    int state_cookie = (p->state >> 16);\n    if (state_cookie != IN_DICT) return false;\n    pop_state (p);\n    return !p->c.object_end || p->c.object_end (p->c.data);\n}\n\n\nstatic inline bool key_user (struct json_parser *p)\n{\n    return p->c.value_user && p->c.key (p->c.data, NULL, 0);\n}\n\n\nstatic inline bool number_begin (struct json_parser *p, char ch)\n{\n    push_state (p, IN_NUMBER);\n    push_buffer (p, ch);\n    return true;\n}\n\nstatic inline bool number_end (struct json_parser *p)\n{\n    char *end;\n    bool result;\n    long long ll;\n    double d;\n\n    pop_state (p);\n    push_buffer (p, 0);\n    ll = strtoll (p->buf, &end, 0);\n    if (!*end)\n        result = (!p->c.value_integer || p->c.value_integer (p->c.data, ll));\n    else {\n        d = strtod (p->buf, &end);\n        result = (!*end &&\n                  (!p->c.value_float || p->c.value_float (p->c.data, d)));\n    }\n\n    clear_buffer(p);\n    return result;\n}\n\n\nstatic inline bool value_null (struct json_parser *p)\n{\n    return !p->c.value_null || p->c.value_null (p->c.data);\n}\n\n\nstatic inline bool value_boolean (struct json_parser *p, int n)\n{\n    return !p->c.value_boolean || p->c.value_boolean (p->c.data, n);\n}\n\n\nstatic inline bool string_begin (struct json_parser *p, int cookie)\n{\n    push_state (p, STATE (IN_STRING, cookie));\n    return true;\n}\n\nstatic inline bool string_end (struct json_parser *p, int cookie)\n{\n    bool result;\n    char *buf, *src, *dest;\n    size_t n;\n\n    pop_state (p); \n    push_buffer (p, 0);\n\n    /* Unescape in place.  */\n    for (n = p->n, buf = src = dest = p->buf; n > 0; n--) {\n        if (*src != '\\\\') {\n            *dest++ = *src++;\n            continue;\n        }\n        if (n < 2)\n            return false;\n\n        src++;\n        n--;\n        switch (*src++) {\n        case 'b': *dest++ = '\\b'; continue;\n        case 'f': *dest++ = '\\f'; continue;\n        case 'n': *dest++ = '\\n'; continue;\n        case 'r': *dest++ = '\\r'; continue;\n        case 't': *dest++ = '\\t'; continue;\n\n        case 'U': case 'u': \n            /* The [uU] has not been removed from n yet, hence subtract 5.  */\n            if (n < 5 || !hex_to_utf8 (buf, &dest, src))\n                return false;\n            src += 4;\n            n -= 4;\n            continue;\n\n        default: *dest++ = src[-1]; continue;\n        }\n    }\n\n    buf = p->buf;\n    n = dest - buf;\n    if (cookie == IN_KEY)\n        result = !p->c.key || p->c.key (p->c.data, buf, n);\n    else\n        result = !p->c.value_string || p->c.value_string (p->c.data, buf, n);\n    clear_buffer(p);\n    return result;\n}\n\n\nstatic inline bool value_user (struct json_parser *p)\n{\n    return p->c.value_user && p->c.value_user (p->c.data);\n}\n\n\nstatic inline bool comment (struct json_parser *p)\n{\n    return !p->c.comment || p->c.comment (p->c.data, p->buf, p->n);\n}\n\n\nbool json_parser_char(struct json_parser *p, char ch)\n{\n    for (;;) {\n        int state = p->state & 255;\n        int state_data = (p->state >> 8) & 255;\n        int state_cookie = (p->state >> 16);\n        // printf (\"%d %d | %d %d\\n\", state, ch, state_cookie, p->sp);\n\n        /* The big ugly parser.  Each case will always return or\n         * continue, and we want to check this at link time if\n         * possible.  */\n#ifndef __OPTIMIZE__\n#define link_error abort\n#endif\n        extern void link_error (void);\n\n        switch (state)\n        {\n        /* First, however, a helpful definition...  */\n#define SKIP_WHITE \\\n            switch (ch) { \\\n            case '/': goto do_start_comment; \\\n            case ' ': case '\\t': case '\\n': case '\\r': case '\\f': return true; \\\n            default: break; \\\n            }\n\n        /* Unlike START_VALUE, this only accepts compound values.  */\n        case START_PARSE:\n            SKIP_WHITE;\n            p->state = STATE (END_VALUE, state_cookie); \n            switch (ch)\n            {\n            case '[': return array_begin (p);\n            case '{': return object_begin (p);\n            default: return false;\n            }\n            link_error ();\n\n        /* Only strings and user values are accepted here.  */\n        case START_KEY:\n            SKIP_WHITE;\n            p->state = STATE (END_KEY, IN_DICT);\n            switch (ch)\n            {\n            case '\"': return string_begin (p, IN_KEY);\n            case '%': return key_user (p);\n            case '}': return object_end (p);\n            default: return false;\n            }\n            link_error ();\n\n        /* Accept any Javascript literal.  Checking p->sp ensures that\n         * something like \"[] []\" is rejected (the first array is parsed\n         * from START_PARSE.  */\n        case START_VALUE:\n            SKIP_WHITE;\n            if (p->sp == 0)\n                return false;\n            p->state = STATE (END_VALUE, state_cookie); \n            switch (ch)\n            {\n            case 't': push_state (p, STATE_KEYWORD(KW_TRUE, IN_TRUE)); return true;\n            case 'f': push_state (p, STATE_KEYWORD(KW_FALSE, IN_FALSE)); return true;\n            case 'n': push_state (p, STATE_KEYWORD(KW_NULL, IN_NULL)); return true;\n            case '\"': return string_begin (p, IN_VALUE);\n            case '-':\n            CASE_DIGIT: return number_begin (p, ch);\n            case '[': return array_begin (p);\n            case '{': return object_begin (p);\n            case '%': return value_user (p);\n            case ']': return array_end (p);\n            default: return false;\n            }\n            link_error ();\n\n        /* End of a key, look for a colon.  */\n        case END_KEY:\n            SKIP_WHITE;\n            p->state = STATE (START_VALUE, IN_DICT);\n            return (ch == ':');\n\n        /* End of a value, look for a comma or closing parenthesis.  */\n        case END_VALUE:\n            SKIP_WHITE;\n            p->state = STATE (state_cookie == IN_DICT ? START_KEY : START_VALUE,\n                              state_cookie);\n            switch (ch)\n            {\n            case ',': return true;\n            case '}': return object_end (p);\n            case ']': return array_end (p);\n            default: return false;\n            }\n            link_error ();\n\n        /* Table-driven keyword scanner.  Advance until mismatch or end\n         * of keyword.  */\n        case IN_KEYWORD:\n            if (ch != keyword_table[state_data])\n                return false;\n            if (keyword_table[state_data + 1] != 0) {\n                p->state = STATE_KEYWORD(state_data + 1, state_cookie);\n                return true;\n            }\n\n            pop_state (p);\n            switch (state_cookie) {\n            case IN_TRUE: return value_boolean (p, 1);\n            case IN_FALSE: return value_boolean (p, 0);\n            case IN_NULL: return value_null (p);\n            default: abort ();\n            }\n            link_error ();\n\n        /* Eat until closing quote (special-casing \\\"). */\n        case IN_STRING:\n            switch (ch) {\n            case '\"': return string_end (p, state_cookie);\n            case '\\\\': p->state = STATE (IN_STRING_BACKSLASH, state_cookie);\n            default: push_buffer (p, ch); return true;\n            }\n            link_error ();\n\n        /* Eat any character */\n        case IN_STRING_BACKSLASH:\n            push_buffer (p, ch); \n            p->state = STATE (IN_STRING, state_cookie);\n            return true;\n\n        /* Eat until a \"bad\" character is found, then we refine with\n         * strtod/strtoll.  The character we end on is reprocessed in\n         * the new state!  */\n        case IN_NUMBER:\n            switch (ch) {\n            case '+':\n            case '-':\n            case '.':\n            case 'x':\n            case 'X':\n            CASE_DIGIT:\n            CASE_XDIGIT: push_buffer (p, ch); return true;\n            default: if (!number_end (p)) return false; continue;\n            }\n            link_error ();\n\n        /* Parse until '*' '/', then convert the whole comment to a\n         * single blank and rescan. */\n        do_start_comment:\n            push_state(p, IN_COMMENT);\n            if (p->c.comment) push_buffer(p, ch);\n            return true;\n\n        case IN_COMMENT:\n            if (p->c.comment) push_buffer(p, ch);\n\n            if      (state_cookie == 0 && ch != '*') return false;\n            else if (state_cookie == 0             ) state_cookie = 1;\n            else if (state_cookie == 1 && ch == '*') state_cookie = 2;\n            else if (state_cookie == 2 && ch == '*') state_cookie = 2;\n            else if (state_cookie == 2 && ch == '/') state_cookie = 3;\n            else                                     state_cookie = 1;\n\n            if (state_cookie < 3) {\n                p->state = STATE(state, state_cookie);\n                return true;\n            } else {\n                comment (p);\n                pop_state (p);\n                ch = ' ';\n                continue;\n            }\n            link_error ();\n\n        default:\n            abort ();\n        }\n\n        link_error ();\n    }\n}\n\nbool json_parser_string(struct json_parser *p, char *s, size_t n)\n{\n    while (n--)\n        if (!json_parser_char(p, *s++))\n            return false;\n    return true;\n}\n\nstruct json_parser *json_parser_new(struct json_parser_config *config)\n{\n    struct json_parser *p;\n    p = malloc (sizeof *p);\n    memcpy (&p->c, config, sizeof *config);\n    p->n = 0;\n    p->alloc = sizeof p->start_buffer;\n    p->state = START_PARSE;\n    p->buf = p->start_buffer;\n    p->sp = 0;\n    return p;\n}\n\nbool json_parser_destroy(struct json_parser *p)\n{\n    bool result = (p->state == END_VALUE) && (p->sp == 0);\n    if (p->buf != p->start_buffer)\n        free (p->buf);\n    free (p);\n    return result;\n}\n\n\n/* main.c */\n\n/*\n    This program demonstrates a simple application of JSON_parser. It reads\n    a JSON text from STDIN, producing an error message if the text is rejected.\n\n        % JSON_parser <test/pass1.json\n*/\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n#include <locale.h>\n\n#include \"json.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nstatic int level = 0;\nstatic int got_key = 0;\n\nstatic void print_indent()\n{\n    printf (\"%*s\", 2 * level, \"\");\n}\n \nstatic bool array_begin (void *data)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"[\\n\");\n    ++level;\n    return true;\n}\n\nstatic bool array_end (void *data)\n{\n    --level;\n    print_indent ();\n    printf (\"]\\n\");\n    return true;\n}\n\nstatic bool object_begin (void *data)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"{\\n\");\n    ++level;\n    return true;\n}\n\nstatic bool object_end (void *data)\n{\n    --level;\n    print_indent ();\n    printf (\"}\\n\");\n    return true;\n}\n\nstatic bool key (void *data, const char *buf, size_t n)\n{\n    got_key = 1;\n    print_indent ();\n    if (buf)\n\tprintf (\"key = '%s', value = \", buf);\n    else\n\tprintf (\"user key = %%%c, value = \", getchar());\n    return true;\n}\n\nstatic bool value_integer (void *data, long long ll)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"integer: %lld\\n\", ll);\n    return true;\n}\n\nstatic bool value_float (void *data, double d)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"float: %f\\n\", d);\n    return true;\n}\n\nstatic bool value_null (void *data)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"null\\n\");\n    return true;\n}\n\nstatic bool value_boolean (void *data, int val)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"%s\\n\", val ? \"true\" : \"false\");\n    return true;\n}\n\nstatic bool value_string (void *data, const char *buf, size_t n)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"string: '%s'\\n\", buf);\n    return true;\n}\n\nstatic bool value_user (void *data)\n{\n    if (!got_key) print_indent(); else got_key = 0;\n    printf (\"user: %%%c\\n\", getchar());\n    return true;\n}\n\n\n\nint main(int argc, char* argv[]) {\n    static struct json_parser_config parser_config = {\n        .array_begin = array_begin,\n        .array_end = array_end,\n        .object_begin = object_begin,\n        .object_end = object_end,\n        .key = key,\n        .value_integer = value_integer,\n        .value_float = value_float,\n        .value_null = value_null,\n        .value_boolean = value_boolean,\n        .value_string = value_string,\n        .value_user = value_user,\n    };\n\n    struct json_parser *p = json_parser_new(&parser_config);\n    int count = 0;\n    int ch;\n    while ((ch = getchar ()) != EOF && json_parser_char (p, ch))\n\tcount++;\n\n    if (ch != EOF) {\n\tfprintf (stderr, \"error at character %d\\n\", count);\n\texit (1);\n    }\n    if (!json_parser_destroy (p)) {\n\tfprintf (stderr, \"error at end of file\\n\");\n\texit (1);\n    }\n\n    exit (0);\n}\n\n\n/*\n * An event-based, asynchronous JSON parser.\n *\n * Copyright (C) 2009 Red Hat Inc.\n *\n * Authors:\n *  Paolo Bonzini <pbonzini@redhat.com>\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n * \n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n\n#ifndef JSON_H\n#define JSON_H\n\n#include <stddef.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nstruct json_parser_config {\n    bool (*array_begin) (void *);\n    bool (*array_end) (void *);\n    bool (*object_begin) (void *);\n    bool (*object_end) (void *);\n    bool (*key) (void *, const char *, size_t);\n    bool (*value_integer) (void *, long long);\n    bool (*value_float) (void *, double);\n    bool (*value_null) (void *);\n    bool (*value_boolean) (void *, int);\n    bool (*value_string) (void *, const char *, size_t);\n    bool (*value_user) (void *);\n    bool (*comment) (void *, const char *, size_t);\n    void *data;\n};\n\nstruct json_parser;\n\nstruct json_parser *json_parser_new(struct json_parser_config *config);\nbool json_parser_destroy(struct json_parser *p);\nbool json_parser_char(struct json_parser *p, char ch);\nbool json_parser_string(struct json_parser *p, char *buf, size_t n);\n\n#endif /* JSON_H */\n\n"},{"id":"139215","messageId":"20100410225704.GA4623@thyrsus.com","threadId":"23397","inReplyTo":"201004102321.59263.jnareb@gmail.com","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-10T22:57:04Z","receivedAt":"2010-04-10T22:57:04Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Jakub Narebski <jnareb@gmail.com>:\n> [JSON] is a bit chatty, but is to some extent self documenting.\n\nYes. But to my mind, the big win of JSON is that you can extend it without\nbreaking parsers looking for older versions - they just skip the new\nfields and all is happy.\n\nJakub, you seem to know this, but other listmermbers may not: I've\nrecently re-engineered GPSD, a service daemon for watching geolocation\nsensors, to report JSON objects up the socket to client apps.  The\nbenefits in clarity and extensibility of the protocol have been\n*huge*.  Like, today I'm adding a reporting type for digital\ncompass/gyroscope sensors.\n\n> The question is whether it should output well formed array of objects,\n> or just list of objects not wrapped in array...\n\nYes, I know this dance.  Answer: one big JSON object, tagged by the\nname of the output generator, and also *containing a version-stamp\nfield*.  Array of file status objects is another top-level member.\n\nThe point is: later, if we want to enrich the reporting format, we add\nwhatever fields we want and bump the version stamp.  Self-describing\ngoodness.  Python, Perl, JavaScript, and Emacs LISP clients win\nespecially big.  Slurping this into a native data structure is one\nfunction call.\n\nThe more I think about this, the better I like it.\n \n> What I am worrying about is correct handling of escaping, quoting,\n> and non-ASCII characters in strings (the JSON-quoting and JSON-escapes\n> are different than C escape codes, IIRC).  JSON rules are simple,\n> but are different than C.\n\nYes. Perhaps there's some scope for reuse here after all.  GPSD has\nwell-tested code for uttering the JSON quote/escape conventions. \nThe git project is welcome to it.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139217","messageId":"20100410230610.GB4623@thyrsus.com","threadId":"23397","inReplyTo":"4BC0FB94.6050409@gnu.org","subject":"Re: More git status --porcelain lossage","fromName":"Eric Raymond","fromEmail":"esr@thyrsus.com","sentAt":"2010-04-10T23:06:10Z","receivedAt":"2010-04-10T23:06:10Z","isPatch":false,"sender":{"key":"esr@thyrsus.com","avatar":"https://avatars.githubusercontent.com/u/727961?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org>:\n> >One issue is that there's no stream-parser JSON implementations that\n> >I'm aware of.\n> \n> Here is one.  It's ugly as hell, you're warned.  The only missing\n> piece is making the stack state resizable.\n\nI wrote one in C for the GPSD project that has two interesting\nproperties:\n\n(1) No use of malloc(),\n\n(2) Unpacks to *fixed-extent* data structures.\n\nIt has one language restriction: Array subelements all have to be the same type.\n\nIt's not a stream parser, so there will be compile-time limits on the\nvolume of data it can handle.  This isn't a big deal in the GPSD \ncontext, where the objects are relatively short (< 1K) datagrams.\n\nIt's very well tested and, I think, pretty bulletproof.  I've been thinking\nof spinning it out as a reusable project.\n-- \n\t\t<a href=\"http://www.catb.org/~esr/\">Eric S. Raymond</a>\n"},{"id":"139243","messageId":"20100413050247.GA31108@gmail.com","threadId":"23397","inReplyTo":"4BC0FB94.6050409@gnu.org","subject":"Re: More git status --porcelain lossage","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2010-04-11T11:04:06Z","receivedAt":"2010-04-11T11:04:06Z","isPatch":false,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Sun, Apr 11, 2010 at 12:28:36AM +0200, Paolo Bonzini wrote:\n> On 04/10/2010 10:31 PM, Martin Langhoff wrote:\n>> On Sat, Apr 10, 2010 at 3:41 PM, Eric Raymond<esr@thyrsus.com>  wrote:\n>>>> I could understand providing JSON format, specified using --json\n>>>> option.\n>>>\n>>> You know, that's actually an interesting idea.  I mentioned it\n>>> previously as the not-XML if we want to build on a metaprotocol;\n>>\n>> One issue is that there's no stream-parser JSON implementations that\n>> I'm aware of.\n>\n> Here is one.  It's ugly as hell, you're warned.  The only missing piece  \n> is making the stack state resizable.\n>\n> Paolo\n\nHere's a fairly popular stream parser:\n\nhttp://lloyd.github.com/yajl/\n\nYet Another JSON Library. YAJL is a small event-driven\n(SAX-style) JSON parser written in ANSI C, and a small\nvalidating JSON generator. YAJL is released under the BSD\nlicense.\n\nThe license is BSD-with-advertising-clause.\nPerhaps the author did not know about modified BSD.\n\n-- \n\t\tDavid\n"}]}