Index: debian/copyright
===================================================================
--- debian/copyright	(revision 4d82e50a6bdc15ed39723c1dac23027b83330be1)
+++ debian/copyright	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
@@ -7,7 +7,16 @@
          Maurits Dijkstra and others.
 
-Mainly Copyright 2002-2004 Wilmer van der Gaast.
-Some parts are borrowed from Gaim (version 0.58) <http://gaim.sf.net/>.
-For the copyrights on those parts, please read the Gaim source code.
+Mainly Copyright 2002-2012 Wilmer van der Gaast.
+
+
+Bits of third party code, also GPLed:
+* Some parts (mostly protocols/oscar/) are borrowed from Gaim (version
+  0.58), now known as Pidgin <http://www.pidgin.im/>.
+* protocols/yahoo/ is libyahoo2 <http://libyahoo2.sf.net/>.
+
+Other license (but GPL-compatible):
+* lib/json.[ch] is from <https://github.com/udp/json-parser> and licensed
+  under the modified BSD license.
+
 
 BitlBee License:
@@ -33,361 +42,14 @@
 
 The SGML-formatted documentation is written by Jelmer Vernooij
-<jelmer@nl.linux.org> under the GNU Free Documentation License:
+<jelmer@samba.org> under the GNU Free Documentation License:
 
 ============================================================================
-		GNU Free Documentation License
-		   Version 1.1, March 2000
+Copyright (c)  2002-2012 Jelmer Vernooij, Wilmer van der Gaast.
 
- Copyright (C) 2000  Free Software Foundation, Inc.
- 	51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
-0. PREAMBLE
-
-The purpose of this License is to make a manual, textbook, or other
-written document "free" in the sense of freedom: to assure everyone
-the effective freedom to copy and redistribute it, with or without
-modifying it, either commercially or noncommercially.  Secondarily,
-this License preserves for the author and publisher a way to get
-credit for their work, while not being considered responsible for
-modifications made by others.
-
-This License is a kind of "copyleft", which means that derivative
-works of the document must themselves be free in the same sense.  It
-complements the GNU General Public License, which is a copyleft
-license designed for free software.
-
-We have designed this License in order to use it for manuals for free
-software, because free software needs free documentation: a free
-program should come with manuals providing the same freedoms that the
-software does.  But this License is not limited to software manuals;
-it can be used for any textual work, regardless of subject matter or
-whether it is published as a printed book.  We recommend this License
-principally for works whose purpose is instruction or reference.
-
-
-1. APPLICABILITY AND DEFINITIONS
-
-This License applies to any manual or other work that contains a
-notice placed by the copyright holder saying it can be distributed
-under the terms of this License.  The "Document", below, refers to any
-such manual or work.  Any member of the public is a licensee, and is
-addressed as "you".
-
-A "Modified Version" of the Document means any work containing the
-Document or a portion of it, either copied verbatim, or with
-modifications and/or translated into another language.
-
-A "Secondary Section" is a named appendix or a front-matter section of
-the Document that deals exclusively with the relationship of the
-publishers or authors of the Document to the Document's overall subject
-(or to related matters) and contains nothing that could fall directly
-within that overall subject.  (For example, if the Document is in part a
-textbook of mathematics, a Secondary Section may not explain any
-mathematics.)  The relationship could be a matter of historical
-connection with the subject or with related matters, or of legal,
-commercial, philosophical, ethical or political position regarding
-them.
-
-The "Invariant Sections" are certain Secondary Sections whose titles
-are designated, as being those of Invariant Sections, in the notice
-that says that the Document is released under this License.
-
-The "Cover Texts" are certain short passages of text that are listed,
-as Front-Cover Texts or Back-Cover Texts, in the notice that says that
-the Document is released under this License.
-
-A "Transparent" copy of the Document means a machine-readable copy,
-represented in a format whose specification is available to the
-general public, whose contents can be viewed and edited directly and
-straightforwardly with generic text editors or (for images composed of
-pixels) generic paint programs or (for drawings) some widely available
-drawing editor, and that is suitable for input to text formatters or
-for automatic translation to a variety of formats suitable for input
-to text formatters.  A copy made in an otherwise Transparent file
-format whose markup has been designed to thwart or discourage
-subsequent modification by readers is not Transparent.  A copy that is
-not "Transparent" is called "Opaque".
-
-Examples of suitable formats for Transparent copies include plain
-ASCII without markup, Texinfo input format, LaTeX input format, SGML
-or XML using a publicly available DTD, and standard-conforming simple
-HTML designed for human modification.  Opaque formats include
-PostScript, PDF, proprietary formats that can be read and edited only
-by proprietary word processors, SGML or XML for which the DTD and/or
-processing tools are not generally available, and the
-machine-generated HTML produced by some word processors for output
-purposes only.
-
-The "Title Page" means, for a printed book, the title page itself,
-plus such following pages as are needed to hold, legibly, the material
-this License requires to appear in the title page.  For works in
-formats which do not have any title page as such, "Title Page" means
-the text near the most prominent appearance of the work's title,
-preceding the beginning of the body of the text.
-
-
-2. VERBATIM COPYING
-
-You may copy and distribute the Document in any medium, either
-commercially or noncommercially, provided that this License, the
-copyright notices, and the license notice saying this License applies
-to the Document are reproduced in all copies, and that you add no other
-conditions whatsoever to those of this License.  You may not use
-technical measures to obstruct or control the reading or further
-copying of the copies you make or distribute.  However, you may accept
-compensation in exchange for copies.  If you distribute a large enough
-number of copies you must also follow the conditions in section 3.
-
-You may also lend copies, under the same conditions stated above, and
-you may publicly display copies.
-
-
-3. COPYING IN QUANTITY
-
-If you publish printed copies of the Document numbering more than 100,
-and the Document's license notice requires Cover Texts, you must enclose
-the copies in covers that carry, clearly and legibly, all these Cover
-Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
-the back cover.  Both covers must also clearly and legibly identify
-you as the publisher of these copies.  The front cover must present
-the full title with all words of the title equally prominent and
-visible.  You may add other material on the covers in addition.
-Copying with changes limited to the covers, as long as they preserve
-the title of the Document and satisfy these conditions, can be treated
-as verbatim copying in other respects.
-
-If the required texts for either cover are too voluminous to fit
-legibly, you should put the first ones listed (as many as fit
-reasonably) on the actual cover, and continue the rest onto adjacent
-pages.
-
-If you publish or distribute Opaque copies of the Document numbering
-more than 100, you must either include a machine-readable Transparent
-copy along with each Opaque copy, or state in or with each Opaque copy
-a publicly-accessible computer-network location containing a complete
-Transparent copy of the Document, free of added material, which the
-general network-using public has access to download anonymously at no
-charge using public-standard network protocols.  If you use the latter
-option, you must take reasonably prudent steps, when you begin
-distribution of Opaque copies in quantity, to ensure that this
-Transparent copy will remain thus accessible at the stated location
-until at least one year after the last time you distribute an Opaque
-copy (directly or through your agents or retailers) of that edition to
-the public.
-
-It is requested, but not required, that you contact the authors of the
-Document well before redistributing any large number of copies, to give
-them a chance to provide you with an updated version of the Document.
-
-
-4. MODIFICATIONS
-
-You may copy and distribute a Modified Version of the Document under
-the conditions of sections 2 and 3 above, provided that you release
-the Modified Version under precisely this License, with the Modified
-Version filling the role of the Document, thus licensing distribution
-and modification of the Modified Version to whoever possesses a copy
-of it.  In addition, you must do these things in the Modified Version:
-
-A. Use in the Title Page (and on the covers, if any) a title distinct
-   from that of the Document, and from those of previous versions
-   (which should, if there were any, be listed in the History section
-   of the Document).  You may use the same title as a previous version
-   if the original publisher of that version gives permission.
-B. List on the Title Page, as authors, one or more persons or entities
-   responsible for authorship of the modifications in the Modified
-   Version, together with at least five of the principal authors of the
-   Document (all of its principal authors, if it has less than five).
-C. State on the Title page the name of the publisher of the
-   Modified Version, as the publisher.
-D. Preserve all the copyright notices of the Document.
-E. Add an appropriate copyright notice for your modifications
-   adjacent to the other copyright notices.
-F. Include, immediately after the copyright notices, a license notice
-   giving the public permission to use the Modified Version under the
-   terms of this License, in the form shown in the Addendum below.
-G. Preserve in that license notice the full lists of Invariant Sections
-   and required Cover Texts given in the Document's license notice.
-H. Include an unaltered copy of this License.
-I. Preserve the section entitled "History", and its title, and add to
-   it an item stating at least the title, year, new authors, and
-   publisher of the Modified Version as given on the Title Page.  If
-   there is no section entitled "History" in the Document, create one
-   stating the title, year, authors, and publisher of the Document as
-   given on its Title Page, then add an item describing the Modified
-   Version as stated in the previous sentence.
-J. Preserve the network location, if any, given in the Document for
-   public access to a Transparent copy of the Document, and likewise
-   the network locations given in the Document for previous versions
-   it was based on.  These may be placed in the "History" section.
-   You may omit a network location for a work that was published at
-   least four years before the Document itself, or if the original
-   publisher of the version it refers to gives permission.
-K. In any section entitled "Acknowledgements" or "Dedications",
-   preserve the section's title, and preserve in the section all the
-   substance and tone of each of the contributor acknowledgements
-   and/or dedications given therein.
-L. Preserve all the Invariant Sections of the Document,
-   unaltered in their text and in their titles.  Section numbers
-   or the equivalent are not considered part of the section titles.
-M. Delete any section entitled "Endorsements".  Such a section
-   may not be included in the Modified Version.
-N. Do not retitle any existing section as "Endorsements"
-   or to conflict in title with any Invariant Section.
-
-If the Modified Version includes new front-matter sections or
-appendices that qualify as Secondary Sections and contain no material
-copied from the Document, you may at your option designate some or all
-of these sections as invariant.  To do this, add their titles to the
-list of Invariant Sections in the Modified Version's license notice.
-These titles must be distinct from any other section titles.
-
-You may add a section entitled "Endorsements", provided it contains
-nothing but endorsements of your Modified Version by various
-parties--for example, statements of peer review or that the text has
-been approved by an organization as the authoritative definition of a
-standard.
-
-You may add a passage of up to five words as a Front-Cover Text, and a
-passage of up to 25 words as a Back-Cover Text, to the end of the list
-of Cover Texts in the Modified Version.  Only one passage of
-Front-Cover Text and one of Back-Cover Text may be added by (or
-through arrangements made by) any one entity.  If the Document already
-includes a cover text for the same cover, previously added by you or
-by arrangement made by the same entity you are acting on behalf of,
-you may not add another; but you may replace the old one, on explicit
-permission from the previous publisher that added the old one.
-
-The author(s) and publisher(s) of the Document do not by this License
-give permission to use their names for publicity for or to assert or
-imply endorsement of any Modified Version.
-
-
-5. COMBINING DOCUMENTS
-
-You may combine the Document with other documents released under this
-License, under the terms defined in section 4 above for modified
-versions, provided that you include in the combination all of the
-Invariant Sections of all of the original documents, unmodified, and
-list them all as Invariant Sections of your combined work in its
-license notice.
-
-The combined work need only contain one copy of this License, and
-multiple identical Invariant Sections may be replaced with a single
-copy.  If there are multiple Invariant Sections with the same name but
-different contents, make the title of each such section unique by
-adding at the end of it, in parentheses, the name of the original
-author or publisher of that section if known, or else a unique number.
-Make the same adjustment to the section titles in the list of
-Invariant Sections in the license notice of the combined work.
-
-In the combination, you must combine any sections entitled "History"
-in the various original documents, forming one section entitled
-"History"; likewise combine any sections entitled "Acknowledgements",
-and any sections entitled "Dedications".  You must delete all sections
-entitled "Endorsements."
-
-
-6. COLLECTIONS OF DOCUMENTS
-
-You may make a collection consisting of the Document and other documents
-released under this License, and replace the individual copies of this
-License in the various documents with a single copy that is included in
-the collection, provided that you follow the rules of this License for
-verbatim copying of each of the documents in all other respects.
-
-You may extract a single document from such a collection, and distribute
-it individually under this License, provided you insert a copy of this
-License into the extracted document, and follow this License in all
-other respects regarding verbatim copying of that document.
-
-
-7. AGGREGATION WITH INDEPENDENT WORKS
-
-A compilation of the Document or its derivatives with other separate
-and independent documents or works, in or on a volume of a storage or
-distribution medium, does not as a whole count as a Modified Version
-of the Document, provided no compilation copyright is claimed for the
-compilation.  Such a compilation is called an "aggregate", and this
-License does not apply to the other self-contained works thus compiled
-with the Document, on account of their being thus compiled, if they
-are not themselves derivative works of the Document.
-
-If the Cover Text requirement of section 3 is applicable to these
-copies of the Document, then if the Document is less than one quarter
-of the entire aggregate, the Document's Cover Texts may be placed on
-covers that surround only the Document within the aggregate.
-Otherwise they must appear on covers around the whole aggregate.
-
-
-8. TRANSLATION
-
-Translation is considered a kind of modification, so you may
-distribute translations of the Document under the terms of section 4.
-Replacing Invariant Sections with translations requires special
-permission from their copyright holders, but you may include
-translations of some or all Invariant Sections in addition to the
-original versions of these Invariant Sections.  You may include a
-translation of this License provided that you also include the
-original English version of this License.  In case of a disagreement
-between the translation and the original English version of this
-License, the original English version will prevail.
-
-
-9. TERMINATION
-
-You may not copy, modify, sublicense, or distribute the Document except
-as expressly provided for under this License.  Any other attempt to
-copy, modify, sublicense or distribute the Document is void, and will
-automatically terminate your rights under this License.  However,
-parties who have received copies, or rights, from you under this
-License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
-
-10. FUTURE REVISIONS OF THIS LICENSE
-
-The Free Software Foundation may publish new, revised versions
-of the GNU Free Documentation License from time to time.  Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.  See
-http://www.gnu.org/copyleft/.
-
-Each version of the License is given a distinguishing version number.
-If the Document specifies that a particular numbered version of this
-License "or any later version" applies to it, you have the option of
-following the terms and conditions either of that specified version or
-of any later version that has been published (not as a draft) by the
-Free Software Foundation.  If the Document does not specify a version
-number of this License, you may choose any version ever published (not
-as a draft) by the Free Software Foundation.
-
-
-ADDENDUM: How to use this License for your documents
-
-To use this License in a document you have written, include a copy of
-the License in the document and put the following copyright and
-license notices just after the title page:
-
-      Copyright (c)  YEAR  YOUR NAME.
-      Permission is granted to copy, distribute and/or modify this document
-      under the terms of the GNU Free Documentation License, Version 1.1
-      or any later version published by the Free Software Foundation;
-      with the Invariant Sections being LIST THEIR TITLES, with the
-      Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
-      A copy of the license is included in the section entitled "GNU
-      Free Documentation License".
-
-If you have no Invariant Sections, write "with no Invariant Sections"
-instead of saying which ones are invariant.  If you have no
-Front-Cover Texts, write "no Front-Cover Texts" instead of
-"Front-Cover Texts being LIST"; likewise for Back-Cover Texts.
-
-If your document contains nontrivial examples of program code, we
-recommend releasing these examples in parallel under your choice of
-free software license, such as the GNU General Public License,
-to permit their use in free software.
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.1 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
+Texts.  A copy of the license is included in the section entitled `GNU
+Free Documentation License''.
 ============================================================================
Index: lib/Makefile
===================================================================
--- lib/Makefile	(revision 4d82e50a6bdc15ed39723c1dac23027b83330be1)
+++ lib/Makefile	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
@@ -13,5 +13,5 @@
 
 # [SH] Program variables
-objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
+objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o json.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
 
 LFLAGS += -r
Index: lib/json.c
===================================================================
--- lib/json.c	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
+++ lib/json.c	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
@@ -0,0 +1,742 @@
+
+/* vim: set et ts=3 sw=3 ft=c:
+ *
+ * Copyright (C) 2012 James McLaughlin et al.  All rights reserved.
+ * https://github.com/udp/json-parser
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "json.h"
+
+#ifdef _MSC_VER
+   #ifndef _CRT_SECURE_NO_WARNINGS
+      #define _CRT_SECURE_NO_WARNINGS
+   #endif
+#endif
+
+#ifdef __cplusplus
+   const struct _json_value json_value_none; /* zero-d by ctor */
+#else
+   const struct _json_value json_value_none = { 0 };
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+typedef unsigned short json_uchar;
+
+static unsigned char hex_value (json_char c)
+{
+   if (c >= 'A' && c <= 'F')
+      return (c - 'A') + 10;
+
+   if (c >= 'a' && c <= 'f')
+      return (c - 'a') + 10;
+
+   if (c >= '0' && c <= '9')
+      return c - '0';
+
+   return 0xFF;
+}
+
+typedef struct
+{
+   json_settings settings;
+   int first_pass;
+
+   unsigned long used_memory;
+
+   unsigned int uint_max;
+   unsigned long ulong_max;
+
+} json_state;
+
+static void * json_alloc (json_state * state, unsigned long size, int zero)
+{
+   void * mem;
+
+   if ((state->ulong_max - state->used_memory) < size)
+      return 0;
+
+   if (state->settings.max_memory
+         && (state->used_memory += size) > state->settings.max_memory)
+   {
+      return 0;
+   }
+
+   if (! (mem = zero ? calloc (size, 1) : malloc (size)))
+      return 0;
+
+   return mem;
+}
+
+static int new_value
+   (json_state * state, json_value ** top, json_value ** root, json_value ** alloc, json_type type)
+{
+   json_value * value;
+   int values_size;
+
+   if (!state->first_pass)
+   {
+      value = *top = *alloc;
+      *alloc = (*alloc)->_reserved.next_alloc;
+
+      if (!*root)
+         *root = value;
+
+      switch (value->type)
+      {
+         case json_array:
+
+            if (! (value->u.array.values = (json_value **) json_alloc
+               (state, value->u.array.length * sizeof (json_value *), 0)) )
+            {
+               return 0;
+            }
+
+            break;
+
+         case json_object:
+
+            values_size = sizeof (*value->u.object.values) * value->u.object.length;
+
+            if (! ((*(void **) &value->u.object.values) = json_alloc
+                  (state, values_size + ((unsigned long) value->u.object.values), 0)) )
+            {
+               return 0;
+            }
+
+            value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size;
+
+            break;
+
+         case json_string:
+
+            if (! (value->u.string.ptr = (json_char *) json_alloc
+               (state, (value->u.string.length + 1) * sizeof (json_char), 0)) )
+            {
+               return 0;
+            }
+
+            break;
+
+         default:
+            break;
+      };
+
+      value->u.array.length = 0;
+
+      return 1;
+   }
+
+   value = (json_value *) json_alloc (state, sizeof (json_value), 1);
+
+   if (!value)
+      return 0;
+
+   if (!*root)
+      *root = value;
+
+   value->type = type;
+   value->parent = *top;
+
+   if (*alloc)
+      (*alloc)->_reserved.next_alloc = value;
+
+   *alloc = *top = value;
+
+   return 1;
+}
+
+#define e_off \
+   ((int) (i - cur_line_begin))
+
+#define whitespace \
+   case '\n': ++ cur_line;  cur_line_begin = i; \
+   case ' ': case '\t': case '\r'
+
+#define string_add(b)  \
+   do { if (!state.first_pass) string [string_length] = b;  ++ string_length; } while (0);
+
+const static int
+   flag_next = 1, flag_reproc = 2, flag_need_comma = 4, flag_seek_value = 8, flag_exponent = 16,
+   flag_got_exponent_sign = 32, flag_escaped = 64, flag_string = 128, flag_need_colon = 256,
+   flag_done = 512;
+
+json_value * json_parse_ex (json_settings * settings, const json_char * json, char * error_buf)
+{
+   json_char error [128];
+   unsigned int cur_line;
+   const json_char * cur_line_begin, * i;
+   json_value * top, * root, * alloc = 0;
+   json_state state;
+   int flags;
+
+   error[0] = '\0';
+
+   memset (&state, 0, sizeof (json_state));
+   memcpy (&state.settings, settings, sizeof (json_settings));
+
+   memset (&state.uint_max, 0xFF, sizeof (state.uint_max));
+   memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max));
+
+   state.uint_max -= 8; /* limit of how much can be added before next check */
+   state.ulong_max -= 8;
+
+   for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass)
+   {
+      json_uchar uchar;
+      unsigned char uc_b1, uc_b2, uc_b3, uc_b4;
+      json_char * string;
+      unsigned int string_length;
+
+      top = root = 0;
+      flags = flag_seek_value;
+
+      cur_line = 1;
+      cur_line_begin = json;
+
+      for (i = json ;; ++ i)
+      {
+         json_char b = *i;
+
+         if (flags & flag_done)
+         {
+            if (!b)
+               break;
+
+            switch (b)
+            {
+               whitespace:
+                  continue;
+
+               default:
+                  sprintf (error, "%d:%d: Trailing garbage: `%c`", cur_line, e_off, b);
+                  goto e_failed;
+            };
+         }
+
+         if (flags & flag_string)
+         {
+            if (!b)
+            {  sprintf (error, "Unexpected EOF in string (at %d:%d)", cur_line, e_off);
+               goto e_failed;
+            }
+
+            if (string_length > state.uint_max)
+               goto e_overflow;
+
+            if (flags & flag_escaped)
+            {
+               flags &= ~ flag_escaped;
+
+               switch (b)
+               {
+                  case 'b':  string_add ('\b');  break;
+                  case 'f':  string_add ('\f');  break;
+                  case 'n':  string_add ('\n');  break;
+                  case 'r':  string_add ('\r');  break;
+                  case 't':  string_add ('\t');  break;
+                  case 'u':
+
+                    if ((uc_b1 = hex_value (*++ i)) == 0xFF || (uc_b2 = hex_value (*++ i)) == 0xFF
+                          || (uc_b3 = hex_value (*++ i)) == 0xFF || (uc_b4 = hex_value (*++ i)) == 0xFF)
+                    {
+                        sprintf (error, "Invalid character value `%c` (at %d:%d)", b, cur_line, e_off);
+                        goto e_failed;
+                    }
+
+                    uc_b1 = uc_b1 * 16 + uc_b2;
+                    uc_b2 = uc_b3 * 16 + uc_b4;
+
+                    uchar = ((json_char) uc_b1) * 256 + uc_b2;
+
+                    if (sizeof (json_char) >= sizeof (json_uchar) || (uc_b1 == 0 && uc_b2 <= 0x7F))
+                    {
+                       string_add ((json_char) uchar);
+                       break;
+                    }
+
+                    if (uchar <= 0x7FF)
+                    {
+                        if (state.first_pass)
+                           string_length += 2;
+                        else
+                        {  string [string_length ++] = 0xC0 | ((uc_b2 & 0xC0) >> 6) | ((uc_b1 & 0x3) << 3);
+                           string [string_length ++] = 0x80 | (uc_b2 & 0x3F);
+                        }
+
+                        break;
+                    }
+
+                    if (state.first_pass)
+                       string_length += 3;
+                    else
+                    {  string [string_length ++] = 0xE0 | ((uc_b1 & 0xF0) >> 4);
+                       string [string_length ++] = 0x80 | ((uc_b1 & 0xF) << 2) | ((uc_b2 & 0xC0) >> 6);
+                       string [string_length ++] = 0x80 | (uc_b2 & 0x3F);
+                    }
+
+                    break;
+
+                  default:
+                     string_add (b);
+               };
+
+               continue;
+            }
+
+            if (b == '\\')
+            {
+               flags |= flag_escaped;
+               continue;
+            }
+
+            if (b == '"')
+            {
+               if (!state.first_pass)
+                  string [string_length] = 0;
+
+               flags &= ~ flag_string;
+               string = 0;
+
+               switch (top->type)
+               {
+                  case json_string:
+
+                     top->u.string.length = string_length;
+                     flags |= flag_next;
+
+                     break;
+
+                  case json_object:
+
+                     if (state.first_pass)
+                        (*(json_char **) &top->u.object.values) += string_length + 1;
+                     else
+                     {  
+                        top->u.object.values [top->u.object.length].name
+                           = (json_char *) top->_reserved.object_mem;
+
+                        (*(json_char **) &top->_reserved.object_mem) += string_length + 1;
+                     }
+
+                     flags |= flag_seek_value | flag_need_colon;
+                     continue;
+
+                  default:
+                     break;
+               };
+            }
+            else
+            {
+               string_add (b);
+               continue;
+            }
+         }
+
+         if (flags & flag_seek_value)
+         {
+            switch (b)
+            {
+               whitespace:
+                  continue;
+
+               case ']':
+
+                  if (top->type == json_array)
+                     flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next;
+                  else if (!state.settings.settings & json_relaxed_commas)
+                  {  sprintf (error, "%d:%d: Unexpected ]", cur_line, e_off);
+                     goto e_failed;
+                  }
+
+                  break;
+
+               default:
+
+                  if (flags & flag_need_comma)
+                  {
+                     if (b == ',')
+                     {  flags &= ~ flag_need_comma;
+                        continue;
+                     }
+                     else
+                     {  sprintf (error, "%d:%d: Expected , before %c", cur_line, e_off, b);
+                        goto e_failed;
+                     }
+                  }
+
+                  if (flags & flag_need_colon)
+                  {
+                     if (b == ':')
+                     {  flags &= ~ flag_need_colon;
+                        continue;
+                     }
+                     else
+                     {  sprintf (error, "%d:%d: Expected : before %c", cur_line, e_off, b);
+                        goto e_failed;
+                     }
+                  }
+
+                  flags &= ~ flag_seek_value;
+
+                  switch (b)
+                  {
+                     case '{':
+
+                        if (!new_value (&state, &top, &root, &alloc, json_object))
+                           goto e_alloc_failure;
+
+                        continue;
+
+                     case '[':
+
+                        if (!new_value (&state, &top, &root, &alloc, json_array))
+                           goto e_alloc_failure;
+
+                        flags |= flag_seek_value;
+                        continue;
+
+                     case '"':
+
+                        if (!new_value (&state, &top, &root, &alloc, json_string))
+                           goto e_alloc_failure;
+
+                        flags |= flag_string;
+
+                        string = top->u.string.ptr;
+                        string_length = 0;
+
+                        continue;
+
+                     case 't':
+
+                        if (*(++ i) != 'r' || *(++ i) != 'u' || *(++ i) != 'e')
+                           goto e_unknown_value;
+
+                        if (!new_value (&state, &top, &root, &alloc, json_boolean))
+                           goto e_alloc_failure;
+
+                        top->u.boolean = 1;
+
+                        flags |= flag_next;
+                        break;
+
+                     case 'f':
+
+                        if (*(++ i) != 'a' || *(++ i) != 'l' || *(++ i) != 's' || *(++ i) != 'e')
+                           goto e_unknown_value;
+
+                        if (!new_value (&state, &top, &root, &alloc, json_boolean))
+                           goto e_alloc_failure;
+
+                        flags |= flag_next;
+                        break;
+
+                     case 'n':
+
+                        if (*(++ i) != 'u' || *(++ i) != 'l' || *(++ i) != 'l')
+                           goto e_unknown_value;
+
+                        if (!new_value (&state, &top, &root, &alloc, json_null))
+                           goto e_alloc_failure;
+
+                        flags |= flag_next;
+                        break;
+
+                     default:
+
+                        if (isdigit (b) || b == '-')
+                        {
+                           if (!new_value (&state, &top, &root, &alloc, json_integer))
+                              goto e_alloc_failure;
+
+                           flags &= ~ (flag_exponent | flag_got_exponent_sign);
+
+                           if (state.first_pass)
+                              continue;
+
+                           if (top->type == json_double)
+                              top->u.dbl = strtod (i, (json_char **) &i);
+                           else
+                              top->u.integer = strtol (i, (json_char **) &i, 10);
+
+                           flags |= flag_next | flag_reproc;
+                        }
+                        else
+                        {  sprintf (error, "%d:%d: Unexpected %c when seeking value", cur_line, e_off, b);
+                           goto e_failed;
+                        }
+                  };
+            };
+         }
+         else
+         {
+            switch (top->type)
+            {
+            case json_object:
+               
+               switch (b)
+               {
+                  whitespace:
+                     continue;
+
+                  case '"':
+
+                     if (flags & flag_need_comma && (!state.settings.settings & json_relaxed_commas))
+                     {
+                        sprintf (error, "%d:%d: Expected , before \"", cur_line, e_off);
+                        goto e_failed;
+                     }
+
+                     flags |= flag_string;
+
+                     string = (json_char *) top->_reserved.object_mem;
+                     string_length = 0;
+
+                     break;
+                  
+                  case '}':
+
+                     flags = (flags & ~ flag_need_comma) | flag_next;
+                     break;
+
+                  case ',':
+
+                     if (flags & flag_need_comma)
+                     {
+                        flags &= ~ flag_need_comma;
+                        break;
+                     }
+
+                  default:
+
+                     sprintf (error, "%d:%d: Unexpected `%c` in object", cur_line, e_off, b);
+                     goto e_failed;
+               };
+
+               break;
+
+            case json_integer:
+            case json_double:
+
+               if (isdigit (b))
+                  continue;
+
+               if (b == 'e' || b == 'E')
+               {
+                  if (!(flags & flag_exponent))
+                  {
+                     flags |= flag_exponent;
+                     top->type = json_double;
+
+                     continue;
+                  }
+               }
+               else if (b == '+' || b == '-')
+               {
+                  if (flags & flag_exponent && !(flags & flag_got_exponent_sign))
+                  {
+                     flags |= flag_got_exponent_sign;
+                     continue;
+                  }
+               }
+               else if (b == '.' && top->type == json_integer)
+               {
+                  top->type = json_double;
+                  continue;
+               }
+
+               flags |= flag_next | flag_reproc;
+               break;
+
+            default:
+               break;
+            };
+         }
+
+         if (flags & flag_reproc)
+         {
+            flags &= ~ flag_reproc;
+            -- i;
+         }
+
+         if (flags & flag_next)
+         {
+            flags = (flags & ~ flag_next) | flag_need_comma;
+
+            if (!top->parent)
+            {
+               /* root value done */
+
+               flags |= flag_done;
+               continue;
+            }
+
+            if (top->parent->type == json_array)
+               flags |= flag_seek_value;
+               
+            if (!state.first_pass)
+            {
+               json_value * parent = top->parent;
+
+               switch (parent->type)
+               {
+                  case json_object:
+
+                     parent->u.object.values
+                        [parent->u.object.length].value = top;
+
+                     break;
+
+                  case json_array:
+
+                     parent->u.array.values
+                           [parent->u.array.length] = top;
+
+                     break;
+
+                  default:
+                     break;
+               };
+            }
+
+            if ( (++ top->parent->u.array.length) > state.uint_max)
+               goto e_overflow;
+
+            top = top->parent;
+
+            continue;
+         }
+      }
+
+      alloc = root;
+   }
+
+   return root;
+
+e_unknown_value:
+
+   sprintf (error, "%d:%d: Unknown value", cur_line, e_off);
+   goto e_failed;
+
+e_alloc_failure:
+
+   strcpy (error, "Memory allocation failure");
+   goto e_failed;
+
+e_overflow:
+
+   sprintf (error, "%d:%d: Too long (caught overflow)", cur_line, e_off);
+   goto e_failed;
+
+e_failed:
+
+   if (error_buf)
+   {
+      if (*error)
+         strcpy (error_buf, error);
+      else
+         strcpy (error_buf, "Unknown error");
+   }
+
+   if (state.first_pass)
+      alloc = root;
+
+   while (alloc)
+   {
+      top = alloc->_reserved.next_alloc;
+      free (alloc);
+      alloc = top;
+   }
+
+   if (!state.first_pass)
+      json_value_free (root);
+
+   return 0;
+}
+
+json_value * json_parse (const json_char * json)
+{
+   json_settings settings;
+   memset (&settings, 0, sizeof (json_settings));
+
+   return json_parse_ex (&settings, json, 0);
+}
+
+void json_value_free (json_value * value)
+{
+   json_value * cur_value;
+
+   if (!value)
+      return;
+
+   value->parent = 0;
+
+   while (value)
+   {
+      switch (value->type)
+      {
+         case json_array:
+
+            if (!value->u.array.length)
+            {
+               free (value->u.array.values);
+               break;
+            }
+
+            value = value->u.array.values [-- value->u.array.length];
+            continue;
+
+         case json_object:
+
+            if (!value->u.object.length)
+            {
+               free (value->u.object.values);
+               break;
+            }
+
+            value = value->u.object.values [-- value->u.object.length].value;
+            continue;
+
+         case json_string:
+
+            free (value->u.string.ptr);
+            break;
+
+         default:
+            break;
+      };
+
+      cur_value = value;
+      value = value->parent;
+      free (cur_value);
+   }
+}
+
+
Index: lib/json.h
===================================================================
--- lib/json.h	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
+++ lib/json.h	(revision 696dc9e555f335d3a97a5815f9a395e5e4ce0e90)
@@ -0,0 +1,192 @@
+
+/* vim: set et ts=3 sw=3 ft=c:
+ *
+ * Copyright (C) 2012 James McLaughlin et al.  All rights reserved.
+ * https://github.com/udp/json-parser
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _JSON_H
+#define _JSON_H
+
+#ifndef json_char
+   #define json_char char
+#endif
+
+#ifdef __cplusplus
+
+   #include <string.h>
+
+   extern "C"
+   {
+
+#endif
+
+typedef struct
+{
+   unsigned long max_memory;
+   int settings;
+
+} json_settings;
+
+#define json_relaxed_commas 1
+
+typedef enum
+{
+   json_none,
+   json_object,
+   json_array,
+   json_integer,
+   json_double,
+   json_string,
+   json_boolean,
+   json_null
+
+} json_type;
+
+extern const struct _json_value json_value_none;
+
+typedef struct _json_value
+{
+   struct _json_value * parent;
+
+   json_type type;
+
+   union
+   {
+      int boolean;
+      long integer;
+      double dbl;
+
+      struct
+      {
+         unsigned int length;
+         json_char * ptr; /* null terminated */
+
+      } string;
+
+      struct
+      {
+         unsigned int length;
+
+         struct
+         {
+            json_char * name;
+            struct _json_value * value;
+
+         } * values;
+
+      } object;
+
+      struct
+      {
+         unsigned int length;
+         struct _json_value ** values;
+
+      } array;
+
+   } u;
+
+   union
+   {
+      struct _json_value * next_alloc;
+      void * object_mem;
+
+   } _reserved;
+
+
+   /* Some C++ operator sugar */
+
+   #ifdef __cplusplus
+
+      public:
+
+         inline _json_value ()
+         {  memset (this, 0, sizeof (_json_value));
+         }
+
+         inline const struct _json_value &operator [] (int index) const
+         {
+            if (type != json_array || index < 0
+                     || ((unsigned int) index) >= u.array.length)
+            {
+               return json_value_none;
+            }
+
+            return *u.array.values [index];
+         }
+
+         inline const struct _json_value &operator [] (const char * index) const
+         { 
+            if (type != json_object)
+               return json_value_none;
+
+            for (unsigned int i = 0; i < u.object.length; ++ i)
+               if (!strcmp (u.object.values [i].name, index))
+                  return *u.object.values [i].value;
+
+            return json_value_none;
+         }
+
+         inline operator const char * () const
+         {  
+            switch (type)
+            {
+               case json_string:
+                  return u.string.ptr;
+
+               default:
+                  return "";
+            };
+         }
+
+         inline operator long () const
+         {  return u.integer;
+         }
+
+         inline operator bool () const
+         {  return u.boolean != 0;
+         }
+
+   #endif
+
+} json_value;
+
+json_value * json_parse
+   (const json_char * json);
+
+json_value * json_parse_ex
+   (json_settings * settings, const json_char * json, char * error);
+
+void json_value_free (json_value *);
+
+
+#ifdef __cplusplus
+   } /* extern "C" */
+#endif
+
+#endif
+
+
