[libvirt] [PATCH 19/21] Start of support for QEMU JSON based monitor protocol

Daniel P. Berrange berrange at redhat.com
Fri Oct 23 13:05:48 UTC 2009


Import a JSON parsing / formatting code from

  http://mjson.sourceforge.net/

with some API changes to better cope with libvirt's needs. Then
add basic implementation of the JSON monitor for QEMU driver.

* src/util/json.c, src/util/json.h: Import code for simple JSON
  parsing & formatting
* src/libvirt_private.syms: Include all json parser symbols
* src/qemu/qemu_conf.c: Support for settin 'control' attribute to
  turn on JSON protocol mode
* src/qemu/qemu_monitor.c: Conditionally call either Text or JSON
  monitor command serialization code as appropriate
* src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: new
  JSON based command serialization code
---
 src/Makefile.am              |    3 +
 src/libvirt_private.syms     |   10 +
 src/qemu/qemu_conf.c         |   13 +-
 src/qemu/qemu_conf.h         |    3 +
 src/qemu/qemu_monitor.c      |  202 ++-
 src/qemu/qemu_monitor_json.c |  797 +++++++++
 src/qemu/qemu_monitor_json.h |  155 ++
 src/util/json.c              | 3904 ++++++++++++++++++++++++++++++++++++++++++
 src/util/json.h              |  311 ++++
 9 files changed, 5350 insertions(+), 48 deletions(-)
 create mode 100644 src/qemu/qemu_monitor_json.c
 create mode 100644 src/qemu/qemu_monitor_json.h
 create mode 100644 src/util/json.c
 create mode 100644 src/util/json.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 9ed9bc3..b87e615 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,7 @@ UTIL_SOURCES =							\
 		util/event.c util/event.h			\
 		util/hash.c util/hash.h				\
 		util/iptables.c util/iptables.h			\
+		util/json.c util/json.h				\
 		util/logging.c util/logging.h			\
 		util/memory.c util/memory.h			\
 		util/pci.c util/pci.h				\
@@ -184,6 +185,8 @@ QEMU_DRIVER_SOURCES =						\
 		qemu/qemu_monitor.c qemu/qemu_monitor.h		\
 		qemu/qemu_monitor_text.c			\
 		qemu/qemu_monitor_text.h			\
+		qemu/qemu_monitor_json.c			\
+		qemu/qemu_monitor_json.h			\
 		qemu/qemu_driver.c qemu/qemu_driver.h
 
 UML_DRIVER_SOURCES =						\
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 4f2f048..f0809f8 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -260,6 +260,16 @@ virRegisterDeviceMonitor;
 virRegisterSecretDriver;
 
 
+# json.h
+json_tree_to_string;
+json_parse_document;
+json_new_string;
+json_new_object;
+json_free_value;
+json_insert_pair_into_object;
+json_find_first_label;
+
+
 # logging.h
 virLogMessage;
 virLogGetNbFilters;
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 951a6c6..16bde05 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -1748,10 +1748,18 @@ int qemudBuildCommandLine(virConnectPtr conn,
         ADD_ARG_LIT("-nographic");
 
     if (monitor_chr) {
+        /* XXX gross hack for testing */
+#if QEMU_WITH_JSON
+        char buf[4096] = "control,";
+
+        if (qemudBuildCommandLineChrDevStr(monitor_chr, buf + 8, sizeof(buf)-8) < 0)
+            goto error;
+#else
         char buf[4096];
 
-        if (qemudBuildCommandLineChrDevStr(monitor_chr, buf, sizeof(buf)) < 0)
+        if (qemudBuildCommandLineChrDevStr(monitor_chr, buf, sizeof(buf)-8) < 0)
             goto error;
+#endif
 
         ADD_ARG_LIT("-monitor");
         ADD_ARG_LIT(buf);
@@ -1885,7 +1893,8 @@ int qemudBuildCommandLine(virConnectPtr conn,
                 break;
             }
 
-            virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : "");
+            if (disk->src)
+                virBufferVSprintf(&opt, "file=%s", disk->src ? disk->src : "");
             virBufferVSprintf(&opt, ",if=%s", bus);
             if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM)
                 virBufferAddLit(&opt, ",media=cdrom");
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index a6e68f8..65c6489 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -39,6 +39,9 @@
 
 #define qemudDebug(fmt, ...) do {} while(0)
 
+/* XXX gross hack for testing purposes */
+#define QEMU_WITH_JSON 0
+
 #define QEMUD_CPUMASK_LEN CPU_SETSIZE
 
 /* Internal flags to keep track of qemu command line capabilities */
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index fb89f9d..875d339 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -30,6 +30,7 @@
 
 #include "qemu_monitor.h"
 #include "qemu_monitor_text.h"
+#include "qemu_monitor_json.h"
 #include "qemu_conf.h"
 #include "event.h"
 #include "virterror_internal.h"
@@ -71,6 +72,8 @@ struct _qemuMonitor {
     unsigned eofcb: 1;
     /* If the monitor callback should free the closed monitor */
     unsigned closed: 1;
+
+    unsigned json: 1;
 };
 
 static void qemuMonitorLock(qemuMonitorPtr mon)
@@ -180,9 +183,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon)
         msg = mon->msg;
 
     VIR_DEBUG("Process %d", mon->bufferOffset);
-    len = qemuMonitorTextIOProcess(mon,
-                                   mon->buffer, mon->bufferOffset,
-                                   msg);
+    if (mon->json)
+        len = qemuMonitorJSONIOProcess(mon,
+                                       mon->buffer, mon->bufferOffset,
+                                       msg);
+    else
+        len = qemuMonitorTextIOProcess(mon,
+                                       mon->buffer, mon->bufferOffset,
+                                       msg);
 
     if (len < 0) {
         mon->lastErrno = errno;
@@ -455,6 +463,7 @@ qemuMonitorOpen(virDomainObjPtr vm,
     mon->fd = -1;
     mon->vm = vm;
     mon->eofCB = eofCB;
+    mon->json = QEMU_WITH_JSON;
     qemuMonitorLock(mon);
 
     switch (vm->monitor_chr->type) {
@@ -594,7 +603,10 @@ qemuMonitorStartCPUs(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextStartCPUs(mon, conn);
+    if (mon->json)
+        ret = qemuMonitorJSONStartCPUs(mon, conn);
+    else
+        ret = qemuMonitorTextStartCPUs(mon, conn);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -606,7 +618,10 @@ qemuMonitorStopCPUs(qemuMonitorPtr mon) {
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextStopCPUs(mon);
+    if (mon->json)
+        ret = qemuMonitorJSONStopCPUs(mon);
+    else
+        ret = qemuMonitorTextStopCPUs(mon);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -617,7 +632,10 @@ int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) {
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextSystemPowerdown(mon);
+    if (mon->json)
+        ret = qemuMonitorJSONSystemPowerdown(mon);
+    else
+        ret = qemuMonitorTextSystemPowerdown(mon);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -630,7 +648,10 @@ int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextGetCPUInfo(mon, pids);
+    if (mon->json)
+        ret = qemuMonitorJSONGetCPUInfo(mon, pids);
+    else
+        ret = qemuMonitorTextGetCPUInfo(mon, pids);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -642,7 +663,10 @@ int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextGetBalloonInfo(mon, currmem);
+    if (mon->json)
+        ret = qemuMonitorJSONGetBalloonInfo(mon, currmem);
+    else
+        ret = qemuMonitorTextGetBalloonInfo(mon, currmem);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -660,10 +684,16 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname);
 
-    ret = qemuMonitorTextGetBlockStatsInfo(mon, devname,
-                                           rd_req, rd_bytes,
-                                           wr_req, wr_bytes,
-                                           errs);
+    if (mon->json)
+        ret = qemuMonitorJSONGetBlockStatsInfo(mon, devname,
+                                               rd_req, rd_bytes,
+                                               wr_req, wr_bytes,
+                                               errs);
+    else
+        ret = qemuMonitorTextGetBlockStatsInfo(mon, devname,
+                                               rd_req, rd_bytes,
+                                               wr_req, wr_bytes,
+                                               errs);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -676,7 +706,10 @@ int qemuMonitorSetVNCPassword(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextSetVNCPassword(mon, password);
+    if (mon->json)
+        ret = qemuMonitorJSONSetVNCPassword(mon, password);
+    else
+        ret = qemuMonitorTextSetVNCPassword(mon, password);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -689,7 +722,10 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem);
 
-    ret = qemuMonitorTextSetBalloon(mon, newmem);
+    if (mon->json)
+        ret = qemuMonitorJSONSetBalloon(mon, newmem);
+    else
+        ret = qemuMonitorTextSetBalloon(mon, newmem);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -701,7 +737,10 @@ int qemuMonitorEjectMedia(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname);
 
-    ret = qemuMonitorTextEjectMedia(mon, devname);
+    if (mon->json)
+        ret = qemuMonitorJSONEjectMedia(mon, devname);
+    else
+        ret = qemuMonitorTextEjectMedia(mon, devname);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -716,7 +755,10 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d devname=%s newmedia=%s",
           mon, mon->fd, devname, newmedia);
 
-    ret = qemuMonitorTextChangeMedia(mon, devname, newmedia);
+    if (mon->json)
+        ret = qemuMonitorJSONChangeMedia(mon, devname, newmedia);
+    else
+        ret = qemuMonitorTextChangeMedia(mon, devname, newmedia);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -732,7 +774,10 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s",
           mon, mon->fd, offset, length, path);
 
-    ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path);
+    if (mon->json)
+        ret = qemuMonitorJSONSaveVirtualMemory(mon, offset, length, path);
+    else
+        ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -747,7 +792,10 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s",
           mon, mon->fd, offset, length, path);
 
-    ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path);
+    if (mon->json)
+        ret = qemuMonitorJSONSavePhysicalMemory(mon, offset, length, path);
+    else
+        ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -760,7 +808,10 @@ int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth);
 
-    ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth);
+    if (mon->json)
+        ret = qemuMonitorJSONSetMigrationSpeed(mon, bandwidth);
+    else
+        ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -775,10 +826,16 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextGetMigrationStatus(mon, status,
-                                            transferred,
-                                            remaining,
-                                            total);
+    if (mon->json)
+        ret = qemuMonitorJSONGetMigrationStatus(mon, status,
+                                                transferred,
+                                                remaining,
+                                                total);
+    else
+        ret = qemuMonitorTextGetMigrationStatus(mon, status,
+                                                transferred,
+                                                remaining,
+                                                total);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -794,7 +851,10 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d hostname=%s port=%d",
           mon, mon->fd, hostname, port);
 
-    ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port);
+    if (mon->json)
+        ret = qemuMonitorJSONMigrateToHost(mon, background, hostname, port);
+    else
+        ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -810,7 +870,10 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d argv=%p target=%s",
           mon, mon->fd, argv, target);
 
-    ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target);
+    if (mon->json)
+        ret = qemuMonitorJSONMigrateToCommand(mon, background, argv, target);
+    else
+        ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -824,7 +887,10 @@ int qemuMonitorMigrateToUnix(qemuMonitorPtr mon,
     DEBUG("mon=%p fd=%d unixfile=%s",
           mon, mon->fd, unixfile);
 
-    ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile);
+    if (mon->json)
+        ret = qemuMonitorJSONMigrateToUnix(mon, background, unixfile);
+    else
+        ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -835,7 +901,10 @@ int qemuMonitorMigrateCancel(qemuMonitorPtr mon)
     qemuMonitorLock(mon);
     DEBUG("mon=%p fd=%d", mon, mon->fd);
 
-    ret = qemuMonitorTextMigrateCancel(mon);
+    if (mon->json)
+        ret = qemuMonitorJSONMigrateCancel(mon);
+    else
+        ret = qemuMonitorTextMigrateCancel(mon);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -847,7 +916,10 @@ int qemuMonitorAddUSBDisk(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path);
 
-    ret = qemuMonitorTextAddUSBDisk(mon, path);
+    if (mon->json)
+        ret = qemuMonitorJSONAddUSBDisk(mon, path);
+    else
+        ret = qemuMonitorTextAddUSBDisk(mon, path);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -861,7 +933,10 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev);
 
-    ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev);
+    if (mon->json)
+        ret = qemuMonitorJSONAddUSBDeviceExact(mon, bus, dev);
+    else
+        ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -875,7 +950,10 @@ int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d vendor=%d product=%d",
           mon, mon->fd, vendor, product);
 
-    ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product);
+    if (mon->json)
+        ret = qemuMonitorJSONAddUSBDeviceMatch(mon, vendor, product);
+    else
+        ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -896,12 +974,20 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon,
           mon, mon->fd,
           hostDomain, hostBus, hostSlot, hostFunction);
 
-    ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain,
-                                          hostBus, hostSlot,
-                                          hostFunction,
-                                          guestDomain,
-                                          guestBus,
-                                          guestSlot);
+    if (mon->json)
+        ret = qemuMonitorJSONAddPCIHostDevice(mon, hostDomain,
+                                              hostBus, hostSlot,
+                                              hostFunction,
+                                              guestDomain,
+                                              guestBus,
+                                              guestSlot);
+    else
+        ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain,
+                                              hostBus, hostSlot,
+                                              hostFunction,
+                                              guestDomain,
+                                              guestBus,
+                                              guestSlot);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -919,8 +1005,12 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d path=%s bus=%s",
           mon, mon->fd, path, bus);
 
-    ret = qemuMonitorTextAddPCIDisk(mon, path, bus,
-                                    guestDomain, guestBus, guestSlot);
+    if (mon->json)
+        ret = qemuMonitorJSONAddPCIDisk(mon, path, bus,
+                                        guestDomain, guestBus, guestSlot);
+    else
+        ret = qemuMonitorTextAddPCIDisk(mon, path, bus,
+                                        guestDomain, guestBus, guestSlot);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -936,8 +1026,12 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon,
     qemuMonitorLock(mon);
     DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr);
 
-    ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain,
-                                       guestBus, guestSlot);
+    if (mon->json)
+        ret = qemuMonitorJSONAddPCINetwork(mon, nicstr, guestDomain,
+                                           guestBus, guestSlot);
+    else
+        ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain,
+                                           guestBus, guestSlot);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -953,8 +1047,12 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d",
           mon, mon->fd, guestDomain, guestBus, guestSlot);
 
-    ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain,
-                                         guestBus, guestSlot);
+    if (mon->json)
+        ret = qemuMonitorJSONRemovePCIDevice(mon, guestDomain,
+                                             guestBus, guestSlot);
+    else
+        ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain,
+                                             guestBus, guestSlot);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -969,7 +1067,10 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d fdname=%s fd=%d",
           mon, mon->fd, fdname, fd);
 
-    ret = qemuMonitorTextSendFileHandle(mon, fdname, fd);
+    if (mon->json)
+        ret = qemuMonitorJSONSendFileHandle(mon, fdname, fd);
+    else
+        ret = qemuMonitorTextSendFileHandle(mon, fdname, fd);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -983,7 +1084,10 @@ int qemuMonitorCloseFileHandle(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d fdname=%s",
           mon, mon->fd, fdname);
 
-    ret = qemuMonitorTextCloseFileHandle(mon, fdname);
+    if (mon->json)
+        ret = qemuMonitorJSONCloseFileHandle(mon, fdname);
+    else
+        ret = qemuMonitorTextCloseFileHandle(mon, fdname);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -997,7 +1101,10 @@ int qemuMonitorAddHostNetwork(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d netstr=%s",
           mon, mon->fd, netstr);
 
-    ret = qemuMonitorTextAddHostNetwork(mon, netstr);
+    if (mon->json)
+        ret = qemuMonitorJSONAddHostNetwork(mon, netstr);
+    else
+        ret = qemuMonitorTextAddHostNetwork(mon, netstr);
     qemuMonitorUnlock(mon);
     return ret;
 }
@@ -1012,7 +1119,10 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon,
     DEBUG("mon=%p, fd=%d netname=%s",
           mon, mon->fd, netname);
 
-    ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname);
+    if (mon->json)
+        ret = qemuMonitorJSONRemoveHostNetwork(mon, vlan, netname);
+    else
+        ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname);
     qemuMonitorUnlock(mon);
     return ret;
 }
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
new file mode 100644
index 0000000..d23569c
--- /dev/null
+++ b/src/qemu/qemu_monitor_json.c
@@ -0,0 +1,797 @@
+/*
+ * qemu_monitor_json.c: interaction with QEMU monitor console
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "qemu_monitor_json.h"
+#include "qemu_conf.h"
+#include "c-ctype.h"
+#include "memory.h"
+#include "logging.h"
+#include "driver.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+#include "json.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+
+#define LINE_ENDING "\r\n"
+
+static int
+qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                             const char *line,
+                             qemuMonitorMessagePtr msg)
+{
+    json_t *obj = NULL;
+    json_t *val;
+    int ret = -1;
+
+    VIR_DEBUG("Line [%s]", line);
+
+    if (json_parse_document(&obj, line) != JSON_OK) {
+        VIR_DEBUG0("Parsing JSON string failed");
+        errno = EINVAL;
+        goto cleanup;
+    }
+
+    if (obj->type != JSON_OBJECT) {
+        VIR_DEBUG0("Parsed JSON string isn't an object");
+        errno = EINVAL;
+    }
+
+    if (json_find_first_label(obj, "QMP", &val) == JSON_OK) {
+        VIR_DEBUG0("Got QMP capabilities data");
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (json_find_first_label(obj, "event", &val) == JSON_OK) {
+        VIR_DEBUG0("Got an event");
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (msg) {
+        msg->rxBuffer = strdup(line);
+        msg->rxLength = strlen(line);
+        msg->finished = 1;
+    } else {
+        VIR_DEBUG("Ignoring unexpected JSON message [%s]", line);
+    }
+
+    ret = 0;
+
+cleanup:
+    if (obj)
+        json_free_value(&obj);
+    return ret;
+}
+
+int qemuMonitorJSONIOProcess(qemuMonitorPtr mon,
+                             const char *data,
+                             size_t len,
+                             qemuMonitorMessagePtr msg)
+{
+    int used = 0;
+    VIR_DEBUG("Data %d bytes [%s]", len, data);
+
+    while (used < len) {
+        char *nl = strstr(data + used, LINE_ENDING);
+
+        if (nl) {
+            int got = nl - (data + used);
+            char *line = strndup(data + used, got);
+            used += got + strlen(LINE_ENDING);
+
+            if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) {
+                VIR_FREE(line);
+                return -1;
+            }
+
+            VIR_FREE(line);
+        } else {
+            break;
+        }
+    }
+
+    VIR_DEBUG("Total used %d", used);
+    return used;
+}
+
+static int
+qemuMonitorCommandWithFd(qemuMonitorPtr mon,
+                         json_t *cmd,
+                         int scm_fd,
+                         json_t **reply)
+{
+    int ret = -1;
+    qemuMonitorMessage msg;
+    char *cmdstr = NULL;
+
+    *reply = NULL;
+
+    memset(&msg, 0, sizeof msg);
+
+    if (json_tree_to_string(cmd, &cmdstr) != JSON_OK) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+    if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) {
+        virReportOOMError(NULL);
+        goto cleanup;
+    }
+    msg.txLength = strlen(msg.txBuffer);
+    msg.txFD = scm_fd;
+
+    VIR_DEBUG("Send command '%s' for write with FD %d", cmdstr, scm_fd);
+
+    ret = qemuMonitorSend(mon, &msg);
+
+    VIR_DEBUG("Receive command reply ret=%d errno=%d %d bytes '%s'",
+              ret, msg.lastErrno, msg.rxLength, msg.rxBuffer);
+
+
+    /* To make life safer for callers, already ensure there's at least an empty string */
+    if (msg.rxBuffer && ret == 0) {
+        if (json_parse_document(reply, msg.rxBuffer) != JSON_OK) {
+            qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+                             _("cannot parse JSON doc '%s'"), msg.rxBuffer);
+            goto cleanup;
+        }
+    }
+
+    if (ret < 0)
+        virReportSystemError(NULL, msg.lastErrno,
+                             _("cannot send monitor command '%s'"), cmdstr);
+
+cleanup:
+    VIR_FREE(cmdstr);
+    VIR_FREE(msg.txBuffer);
+    VIR_FREE(msg.rxBuffer);
+
+    return ret;
+}
+
+
+static int
+qemuMonitorCommand(qemuMonitorPtr mon,
+                   json_t *cmd,
+                   json_t **reply) {
+    return qemuMonitorCommandWithFd(mon, cmd, -1, reply);
+}
+
+
+static json_t *
+qemuMonitorJSONMakeCommand(const char *cmdname,
+                           json_t *args)
+{
+    json_t *obj, *tmp = NULL;
+    if (!(obj  = json_new_object()))
+        goto no_memory;
+
+    if (!(tmp = json_new_string(cmdname)))
+        goto no_memory;
+
+    if (json_insert_pair_into_object(obj, "execute", tmp) != JSON_OK)
+        goto no_memory;
+    tmp = NULL;
+
+    if (args &&
+        json_insert_pair_into_object(obj, "arguments", args) != JSON_OK)
+        goto no_memory;
+
+    return obj;
+
+no_memory:
+    virReportOOMError(NULL);
+    if (obj)
+        json_free_value(&obj);
+    if (tmp)
+        json_free_value(&tmp);
+    return NULL;
+}
+
+int
+qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
+                         virConnectPtr conn ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("cont", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int
+qemuMonitorJSONStopCPUs(qemuMonitorPtr mon)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("stop", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
+                              int **pids)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("info cpus", NULL);
+    json_t *reply = NULL;
+
+    *pids = NULL;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+
+/*
+ * Returns: 0 if balloon not supported, +1 if balloon query worked
+ * or -1 on failure
+ */
+int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
+                                  unsigned long *currmem)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("info balloon", NULL);
+    json_t *reply = NULL;
+
+    *currmem = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
+                                     const char *devname ATTRIBUTE_UNUSED,
+                                     long long *rd_req,
+                                     long long *rd_bytes,
+                                     long long *wr_req,
+                                     long long *wr_bytes,
+                                     long long *errs)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("info blockstats", NULL);
+    json_t *reply = NULL;
+
+    *rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
+                                  const char *password ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("change vnc password", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+/*
+ * Returns: 0 if balloon not supported, +1 if balloon adjust worked
+ * or -1 on failure
+ */
+int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon,
+                              unsigned long newmem ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("balloon", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
+                              const char *devname ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("eject", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon,
+                               const char *devname ATTRIBUTE_UNUSED,
+                               const char *newmedia ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("change", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon,
+                                     const char *cmdtype,
+                                     unsigned long long offset ATTRIBUTE_UNUSED,
+                                     size_t length ATTRIBUTE_UNUSED,
+                                     const char *path ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand(cmdtype, NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon,
+                                     unsigned long long offset,
+                                     size_t length,
+                                     const char *path)
+{
+    return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path);
+}
+
+int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon,
+                                      unsigned long long offset,
+                                      size_t length,
+                                      const char *path)
+{
+    return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path);
+}
+
+
+int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon,
+                                     unsigned long bandwidth ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("migrate_set_speed", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
+                                      int *status,
+                                      unsigned long long *transferred,
+                                      unsigned long long *remaining,
+                                      unsigned long long *total) {
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("info migration", NULL);
+    json_t *reply = NULL;
+
+    *status = 0;
+    *transferred = *remaining = *total = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon,
+                                 int background ATTRIBUTE_UNUSED,
+                                 const char *hostname ATTRIBUTE_UNUSED,
+                                 int port ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon,
+                                    int background ATTRIBUTE_UNUSED,
+                                    const char * const *argv ATTRIBUTE_UNUSED,
+                                    const char *target ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon,
+                                 int background ATTRIBUTE_UNUSED,
+                                 const char *unixfile ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("migrate", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon,
+                              const char *path ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon,
+                                     int bus ATTRIBUTE_UNUSED,
+                                     int dev ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+
+}
+
+int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon,
+                                     int vendor ATTRIBUTE_UNUSED,
+                                     int product ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("usb_add", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon,
+                                    unsigned hostDomain ATTRIBUTE_UNUSED,
+                                    unsigned hostBus ATTRIBUTE_UNUSED,
+                                    unsigned hostSlot ATTRIBUTE_UNUSED,
+                                    unsigned hostFunction ATTRIBUTE_UNUSED,
+                                    unsigned *guestDomain,
+                                    unsigned *guestBus,
+                                    unsigned *guestSlot)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL);
+    json_t *reply = NULL;
+
+    *guestDomain = *guestBus = *guestSlot = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon,
+                              const char *path ATTRIBUTE_UNUSED,
+                              const char *bus ATTRIBUTE_UNUSED,
+                              unsigned *guestDomain,
+                              unsigned *guestBus,
+                              unsigned *guestSlot) {
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL);
+    json_t *reply = NULL;
+
+    *guestDomain = *guestBus = *guestSlot = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon,
+                                 const char *nicstr ATTRIBUTE_UNUSED,
+                                 unsigned *guestDomain,
+                                 unsigned *guestBus,
+                                 unsigned *guestSlot)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("pci_add", NULL);
+    json_t *reply = NULL;
+
+    *guestDomain = *guestBus = *guestSlot = 0;
+
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon,
+                                   unsigned guestDomain ATTRIBUTE_UNUSED,
+                                   unsigned guestBus ATTRIBUTE_UNUSED,
+                                   unsigned guestSlot ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("pci_del", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon,
+                                  const char *fdname ATTRIBUTE_UNUSED,
+                                  int fd)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("getfd", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommandWithFd(mon, cmd, fd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon,
+                                   const char *fdname ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("closefd", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon,
+                                  const char *netstr ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("host_net_add", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
+
+
+int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
+                                     int vlan ATTRIBUTE_UNUSED,
+                                     const char *netname ATTRIBUTE_UNUSED)
+{
+    int ret;
+    json_t *cmd = qemuMonitorJSONMakeCommand("host_net_remove", NULL);
+    json_t *reply = NULL;
+    if (!cmd)
+        return -1;
+
+    ret = qemuMonitorCommand(mon, cmd, &reply);
+
+    json_free_value(&cmd);
+    if (reply)
+        json_free_value(&reply);
+    return ret;
+}
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
new file mode 100644
index 0000000..a44158a
--- /dev/null
+++ b/src/qemu/qemu_monitor_json.h
@@ -0,0 +1,155 @@
+/*
+ * qemu_monitor_json.h: interaction with QEMU monitor console
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+
+#ifndef QEMU_MONITOR_JSON_H
+#define QEMU_MONITOR_JSON_H
+
+#include "internal.h"
+
+#include "qemu_monitor.h"
+
+int qemuMonitorJSONIOProcess(qemuMonitorPtr mon,
+                             const char *data,
+                             size_t len,
+                             qemuMonitorMessagePtr msg);
+
+int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
+                             virConnectPtr conn);
+int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon);
+
+int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon);
+
+int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
+                              int **pids);
+int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
+                                  unsigned long *currmem);
+int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
+                                     const char *devname,
+                                     long long *rd_req,
+                                     long long *rd_bytes,
+                                     long long *wr_req,
+                                     long long *wr_bytes,
+                                     long long *errs);
+
+
+int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
+                                  const char *password);
+int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon,
+                              unsigned long newmem);
+
+int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
+                              const char *devname);
+int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon,
+                               const char *devname,
+                               const char *newmedia);
+
+
+int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon,
+                                     unsigned long long offset,
+                                     size_t length,
+                                     const char *path);
+int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon,
+                                      unsigned long long offset,
+                                      size_t length,
+                                      const char *path);
+
+int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon,
+                                     unsigned long bandwidth);
+
+int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
+                                      int *status,
+                                      unsigned long long *transferred,
+                                      unsigned long long *remaining,
+                                      unsigned long long *total);
+
+int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon,
+                                 int background,
+                                 const char *hostname,
+                                 int port);
+
+int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon,
+                                    int background,
+                                    const char * const *argv,
+                                    const char *target);
+
+int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon,
+                                 int background,
+                                 const char *unixfile);
+
+int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon);
+
+int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon,
+                              const char *path);
+
+int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon,
+                                     int bus,
+                                     int dev);
+int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon,
+                                     int vendor,
+                                     int product);
+
+
+int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon,
+                                    unsigned hostDomain,
+                                    unsigned hostBus,
+                                    unsigned hostSlot,
+                                    unsigned hostFunction,
+                                    unsigned *guestDomain,
+                                    unsigned *guestBus,
+                                    unsigned *guestSlot);
+
+int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon,
+                              const char *path,
+                              const char *bus,
+                              unsigned *guestDomain,
+                              unsigned *guestBus,
+                              unsigned *guestSlot);
+
+int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon,
+                                 const char *nicstr,
+                                 unsigned *guestDomain,
+                                 unsigned *guestBus,
+                                 unsigned *guestSlot);
+
+int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon,
+                                   unsigned guestDomain,
+                                   unsigned guestBus,
+                                   unsigned guestSlot);
+
+
+int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon,
+                                  const char *fdname,
+                                  int fd);
+
+int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon,
+                                   const char *fdname);
+
+int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon,
+                                  const char *netstr);
+
+int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
+                                     int vlan,
+                                     const char *netname);
+
+#endif /* QEMU_MONITOR_JSON_H */
diff --git a/src/util/json.c b/src/util/json.c
new file mode 100644
index 0000000..69b4434
--- /dev/null
+++ b/src/util/json.c
@@ -0,0 +1,3904 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by Rui Maciel   *
+ *   rui.maciel at gmail.com   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Library General Public License as       *
+ *   published by the Free Software Foundation; either version 2 of the    *
+ *   License, or (at your option) any later version.                       *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU Library General Public     *
+ *   License along with this program; if not, write to the                 *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "json.h"
+#include "memory.h"
+
+enum LEX_VALUE
+{ LEX_MORE = 0,
+  LEX_INVALID_CHARACTER,
+  LEX_TRUE,
+  LEX_FALSE,
+  LEX_NULL,
+  LEX_BEGIN_OBJECT,
+  LEX_END_OBJECT,
+  LEX_BEGIN_ARRAY,
+  LEX_END_ARRAY,
+  LEX_NAME_SEPARATOR,
+  LEX_VALUE_SEPARATOR,
+  LEX_STRING,
+  LEX_NUMBER,
+  LEX_ERROR,
+  LEX_MEMORY
+};
+
+
+/* rc_string part */
+
+#define RSTRING_INCSTEP 5
+#define RSTRING_DEFAULT 8
+
+enum rui_string_error_codes
+{ RS_MEMORY, RS_OK = 1, RS_UNKNOWN };
+
+typedef enum rui_string_error_codes rstring_code;
+
+
+static rcstring *
+rcs_create (size_t length)
+{
+    rcstring *rcs;
+    if (VIR_ALLOC(rcs) < 0)
+        return NULL;
+
+    rcs->max = length;
+    rcs->length = 0;
+
+    if (VIR_ALLOC_N(rcs->text, rcs->max + 1) < 0)
+    {
+        VIR_FREE(rcs);
+        return NULL;
+    }
+    rcs->text[0] = '\0';
+
+    return rcs;
+}
+
+
+static void
+rcs_free (rcstring ** rcs)
+{
+    if (*rcs != NULL)
+    {
+        VIR_FREE((*rcs)->text);
+        VIR_FREE(*rcs);
+    }
+
+}
+
+
+static rstring_code
+rcs_resize (rcstring * rcs, size_t length)
+{
+    if (VIR_REALLOC_N(rcs->text, length + 1) < 0)
+    {
+        VIR_FREE(rcs);
+        return RS_MEMORY;
+    }
+    rcs->max = length;
+    rcs->text[rcs->max] = '\0';
+    return RS_OK;
+}
+
+
+static rstring_code
+rcs_catcs (rcstring * pre, const char *pos, const size_t length)
+{
+    if (pre->max < pre->length + length)
+    {
+        if (rcs_resize (pre, pre->length + length + RSTRING_INCSTEP) != RS_OK)
+            return RS_MEMORY;
+    }
+    strncpy (pre->text + pre->length, pos, length);
+    pre->text[pre->length + length] = '\0';
+    pre->length += length;
+    return RS_OK;
+}
+
+
+static rstring_code
+rcs_catc (rcstring * pre, const char c)
+{
+    if (pre->max <= pre->length)
+    {
+        if (rcs_resize (pre, pre->max + RSTRING_INCSTEP) != RS_OK)
+            return RS_MEMORY;
+    }
+    pre->text[pre->length] = c;
+    pre->length++;
+    pre->text[pre->length] = '\0';
+    return RS_OK;
+}
+
+
+static char *
+rcs_unwrap (rcstring * rcs)
+{
+    char *out;
+
+    if (rcs->text == NULL)
+        out = NULL;
+    else
+    {
+        if (VIR_REALLOC_N(rcs->text, strlen(rcs->text)+1) < 0)
+        {} /* Ignore error to shrink */
+        out = rcs->text;
+    }
+
+    VIR_FREE(rcs);
+    return out;
+}
+
+
+
+static size_t
+rcs_length (rcstring * rcs)
+{
+    /*TODO account for UTF8 */
+    return rcs->length;
+}
+
+
+/* end of rc_string part */
+
+
+enum json_error
+json_stream_parse (FILE * file, json_t ** document)
+{
+    char buffer[1024];    /* hard-coded value */
+    unsigned int error = JSON_INCOMPLETE_DOCUMENT;
+
+    struct json_parsing_info state;
+
+    json_jpi_init (&state);    /* initializes the json_parsing_info object */
+
+    while ((error == JSON_WAITING_FOR_EOF) || (error == JSON_INCOMPLETE_DOCUMENT))
+    {
+        if (fgets (buffer, 1024, file) != NULL)
+        {
+            switch (error = json_parse_fragment (&state, buffer))
+            {
+            case JSON_OK:
+            case JSON_WAITING_FOR_EOF:
+            case JSON_INCOMPLETE_DOCUMENT:
+                break;
+
+            default:
+                json_free_value (&state.cursor);
+                return error;
+                break;
+            }
+        }
+        else
+        {
+            if (error == JSON_WAITING_FOR_EOF)
+                error = JSON_OK;
+            else
+            {
+                /*TODO refine this error code */
+                error = JSON_UNKNOWN_PROBLEM;
+            }
+        }
+    }
+
+    if (error == JSON_OK)
+    {
+        *document = state.cursor;
+    }
+
+    return error;
+}
+
+
+json_t *
+json_new_value (const enum json_value_type type)
+{
+    json_t *new_object;
+    if (VIR_ALLOC(new_object) < 0)
+        return NULL;
+
+    /* initialize members */
+    new_object->text = NULL;
+    new_object->parent = NULL;
+    new_object->child = NULL;
+    new_object->child_end = NULL;
+    new_object->previous = NULL;
+    new_object->next = NULL;
+    new_object->type = type;
+    return new_object;
+}
+
+
+json_t *
+json_new_string (const char *text)
+{
+    json_t *new_object;
+    size_t length;
+
+    if (VIR_ALLOC(new_object) < 0)
+        return NULL;
+
+    /* initialize members */
+    length = strlen (text) + 1;
+    if (VIR_ALLOC_N(new_object->text, length) < 0)
+    {
+        VIR_FREE(new_object);
+        return NULL;
+    }
+    strncpy (new_object->text, text, length);
+    new_object->parent = NULL;
+    new_object->child = NULL;
+    new_object->child_end = NULL;
+    new_object->previous = NULL;
+    new_object->next = NULL;
+    new_object->type = JSON_STRING;
+    return new_object;
+}
+
+
+json_t *
+json_new_number (const char *text)
+{
+    json_t *new_object;
+    size_t length;
+
+    /* allocate memory for the new object */
+    if (VIR_ALLOC(new_object) < 0)
+        return NULL;
+
+    /* initialize members */
+    length = strlen (text) + 1;
+    if (VIR_ALLOC_N(new_object->text, length) < 0)
+    {
+        VIR_FREE(new_object);
+        return NULL;
+    }
+    strncpy (new_object->text, text, length);
+    new_object->parent = NULL;
+    new_object->child = NULL;
+    new_object->child_end = NULL;
+    new_object->previous = NULL;
+    new_object->next = NULL;
+    new_object->type = JSON_NUMBER;
+    return new_object;
+}
+
+
+json_t *
+json_new_object (void)
+{
+    return json_new_value (JSON_OBJECT);
+}
+
+
+json_t *
+json_new_array (void)
+{
+    return json_new_value (JSON_ARRAY);
+}
+
+
+json_t *
+json_new_null (void)
+{
+    return json_new_value (JSON_NULL);
+}
+
+
+json_t *
+json_new_true (void)
+{
+    return json_new_value (JSON_TRUE);
+}
+
+
+json_t *
+json_new_false (void)
+{
+    return json_new_value (JSON_FALSE);
+}
+
+
+void
+json_free_value (json_t ** value)
+{
+    /* free each and every child node */
+    if ((*value)->child != NULL)
+    {
+        json_t *i, *j;
+        i = (*value)->child_end;
+        while (i != NULL)
+        {
+            j = i->previous;
+            json_free_value (&i);    /*TODO replace recursive solution with an iterative one */
+            i = j;
+        }
+    }
+
+    /* fixing sibling linked list connections */
+    if ((*value)->previous && (*value)->next)
+    {
+        (*value)->previous->next = (*value)->next;
+        (*value)->next->previous = (*value)->previous;
+    }
+    else
+    {
+        if ((*value)->previous)
+        {
+            (*value)->previous->next = NULL;
+        }
+        if ((*value)->next)
+        {
+            (*value)->next->previous = NULL;
+        }
+    }
+
+    /*fixing parent node connections */
+    if ((*value)->parent)
+    {
+        /* fix the tree connection to the first node in the children's list */
+        if ((*value)->parent->child == (*value))
+        {
+            if ((*value)->next)
+            {
+                (*value)->parent->child = (*value)->next;    /* the parent node always points to the first node in the children linked list */
+            }
+            else
+            {
+                (*value)->parent->child = NULL;
+            }
+        }
+
+        /* fix the tree connection to the last node in the children's list */
+        if ((*value)->parent->child_end == (*value))
+        {
+            if ((*value)->previous)
+            {
+                (*value)->parent->child_end = (*value)->previous;    /* the parent node always points to the last node in the children linked list */
+            }
+            else
+            {
+                (*value)->parent->child_end = NULL;
+            }
+        }
+    }
+
+    /*finally, freeing the memory allocated for this value */
+    VIR_FREE((*value)->text);
+    VIR_FREE(*value);
+}
+
+
+enum json_error
+json_insert_child (json_t * parent, json_t * child)
+{
+    /*TODO change the child list from FIFO to LIFO, in order to get rid of the child_end pointer */
+    /* enforce tree structure correctness */
+    switch (parent->type)
+    {
+    case JSON_STRING:
+        /* a string accepts every JSON type as a child value */
+        /* therefore, the sanity check must be performed on the child node */
+        switch (child->type)
+        {
+        case JSON_STRING:
+        case JSON_NUMBER:
+        case JSON_TRUE:
+        case JSON_FALSE:
+        case JSON_NULL:
+            if (child->child != NULL)
+                return JSON_BAD_TREE_STRUCTURE;
+            break;
+
+        case JSON_OBJECT:
+        case JSON_ARRAY:
+            break;
+
+        default:
+            return JSON_BAD_TREE_STRUCTURE;    /* this part should never be reached */
+            break;
+        }
+        break;
+
+    case JSON_OBJECT:    /* JSON objects may only accept JSON string objects which already have child nodes of their own */
+        if (child->type != JSON_STRING)
+            return JSON_BAD_TREE_STRUCTURE;
+        break;
+
+    case JSON_ARRAY:
+        switch (child->type)
+        {
+        case JSON_STRING:
+        case JSON_TRUE:
+        case JSON_FALSE:
+        case JSON_NULL:
+        case JSON_NUMBER:
+            if (child->child)
+                return JSON_BAD_TREE_STRUCTURE;
+            break;
+
+        case JSON_OBJECT:
+        case JSON_ARRAY:
+            break;
+
+        default:
+            return JSON_BAD_TREE_STRUCTURE;
+        }
+        break;
+
+    default:
+        return JSON_BAD_TREE_STRUCTURE;
+    }
+
+    child->parent = parent;
+    if (parent->child)
+    {
+        child->previous = parent->child_end;
+        parent->child_end->next = child;
+        parent->child_end = child;
+    }
+    else
+    {
+        parent->child = child;
+        parent->child_end = child;
+    }
+
+    return JSON_OK;
+}
+
+
+enum json_error
+json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value)
+{
+    enum json_error error;
+    json_t *label;
+
+    /* create label json_value */
+    label = json_new_string (text_label);
+    if (label == NULL)
+        return JSON_MEMORY;
+
+    /*insert value and check for error */
+    error = json_insert_child (label, value);
+    if (error != JSON_OK)
+        return error;
+    /*insert value and check for error */
+    error = json_insert_child (parent, label);
+    if (error != JSON_OK)
+        return error;
+
+    return JSON_OK;
+}
+
+
+enum json_error
+json_tree_to_string (json_t * root, char **text)
+{
+    json_t *cursor;
+    rcstring *output;
+
+    cursor = root;
+    /* set up the output and temporary rwstrings */
+    output = rcs_create (RSTRING_DEFAULT);
+
+    /* start the convoluted fun */
+state1:            /* open value */
+    {
+        if ((cursor->previous) && (cursor != root))    /*if cursor is children and not root than it is a followup sibling */
+        {
+            /* append comma */
+            if (rcs_catc (output, ',') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+        }
+        switch (cursor->type)
+        {
+        case JSON_STRING:
+            /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */
+            /* set the new output size */
+            if (rcs_catc (output, '\"') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc (output, '\"') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+            if (cursor->parent != NULL)
+            {
+                if (cursor->parent->type == JSON_OBJECT)    /* cursor is label in label:value pair */
+                {
+                    /* error checking: if parent is object and cursor is string then cursor must have a single child */
+                    if (cursor->child != NULL)
+                    {
+                        if (rcs_catc (output, ':') != RS_OK)
+                        {
+                            return JSON_MEMORY;
+                        }
+                    }
+                    else
+                    {
+                        /* malformed document tree: label without value in label:value pair */
+                        rcs_free (&output);
+                        text = NULL;
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+            }
+            else    /* does not have a parent */
+            {
+                if (cursor->child != NULL)    /* is root label in label:value pair */
+                {
+                    if (rcs_catc (output, ':') != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    /* malformed document tree: label without value in label:value pair */
+                    rcs_free (&output);
+                    text = NULL;
+                    return JSON_BAD_TREE_STRUCTURE;
+                }
+            }
+            break;
+
+        case JSON_NUMBER:
+            /* must not have any children */
+            /* set the new size */
+            if (rcs_catcs (output, cursor->text, strlen (cursor->text)) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            goto state2;    /* close value */
+            break;
+
+        case JSON_OBJECT:
+            if (rcs_catc (output, '{') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+            if (cursor->child)
+            {
+                cursor = cursor->child;
+                goto state1;    /* open value */
+            }
+            else
+            {
+                goto state2;    /* close value */
+            }
+            break;
+
+        case JSON_ARRAY:
+            if (rcs_catc (output, '[') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+            if (cursor->child != NULL)
+            {
+                cursor = cursor->child;
+                goto state1;
+            }
+            else
+            {
+                goto state2;    /* close value */
+            }
+            break;
+
+        case JSON_TRUE:
+            /* must not have any children */
+            if (rcs_catcs (output, "true", 4) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            goto state2;    /* close value */
+            break;
+
+        case JSON_FALSE:
+            /* must not have any children */
+            if (rcs_catcs (output, "false", 5) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            goto state2;    /* close value */
+            break;
+
+        case JSON_NULL:
+            /* must not have any children */
+            if (rcs_catcs (output, "null", 4) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            goto state2;    /* close value */
+            break;
+
+        default:
+            goto error;
+        }
+        if (cursor->child)
+        {
+            cursor = cursor->child;
+            goto state1;    /* open value */
+        }
+        else
+        {
+            /* does not have any children */
+            goto state2;    /* close value */
+        }
+    }
+
+state2:            /* close value */
+    {
+        switch (cursor->type)
+        {
+        case JSON_OBJECT:
+            if (rcs_catc (output, '}') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            break;
+
+        case JSON_ARRAY:
+            if (rcs_catc (output, ']') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            break;
+
+        case JSON_STRING:
+            break;
+        case JSON_NUMBER:
+            break;
+        case JSON_TRUE:
+            break;
+        case JSON_FALSE:
+            break;
+        case JSON_NULL:
+            break;
+        default:
+            goto error;
+        }
+        if ((cursor->parent == NULL) || (cursor == root))
+        {
+            goto end;
+        }
+        else if (cursor->next)
+        {
+            cursor = cursor->next;
+            goto state1;    /* open value */
+        }
+        else
+        {
+            cursor = cursor->parent;
+            goto state2;    /* close value */
+        }
+    }
+
+error:
+    {
+        rcs_free (&output);
+        return JSON_UNKNOWN_PROBLEM;
+    }
+
+end:
+    {
+        *text = rcs_unwrap (output);
+        return JSON_OK;
+    }
+}
+
+
+enum json_error
+json_stream_output (FILE * file, json_t * root)
+{
+    json_t *cursor;
+
+    cursor = root;
+    /* set up the output and temporary rwstrings */
+
+    /* start the convoluted fun */
+state1:            /* open value */
+    {
+        if ((cursor->previous) && (cursor != root))    /*if cursor is children and not root than it is a followup sibling */
+        {
+            /* append comma */
+            fprintf (file, ",");
+        }
+        switch (cursor->type)
+        {
+        case JSON_STRING:
+            /* append the "text"\0, which means 1 + wcslen(cursor->text) + 1 + 1 */
+            /* set the new output size */
+            fprintf (file, "\"%s\"", cursor->text);
+
+            if (cursor->parent != NULL)
+            {
+                if (cursor->parent->type == JSON_OBJECT)    /* cursor is label in label:value pair */
+                {
+                    /* error checking: if parent is object and cursor is string then cursor must have a single child */
+                    if (cursor->child != NULL)
+                    {
+                        if (fprintf (file, ":") != RS_OK)
+                        {
+                            return JSON_MEMORY;
+                        }
+                    }
+                    else
+                    {
+                        /* malformed document tree: label without value in label:value pair */
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+            }
+            else    /* does not have a parent */
+            {
+                if (cursor->child != NULL)    /* is root label in label:value pair */
+                {
+                    fprintf (file, ":");
+                }
+                else
+                {
+                    /* malformed document tree: label without value in label:value pair */
+                    return JSON_BAD_TREE_STRUCTURE;
+                }
+            }
+            break;
+
+        case JSON_NUMBER:
+            /* must not have any children */
+            /* set the new size */
+            fprintf (file, "%s", cursor->text);
+            goto state2;    /* close value */
+            break;
+
+        case JSON_OBJECT:
+            fprintf (file, "{");
+
+            if (cursor->child)
+            {
+                cursor = cursor->child;
+                goto state1;    /* open value */
+            }
+            else
+            {
+                goto state2;    /* close value */
+            }
+            break;
+
+        case JSON_ARRAY:
+            fprintf (file, "[");
+
+            if (cursor->child != NULL)
+            {
+                cursor = cursor->child;
+                goto state1;
+            }
+            else
+            {
+                goto state2;    /* close value */
+            }
+            break;
+
+        case JSON_TRUE:
+            /* must not have any children */
+            fprintf (file, "true");
+            goto state2;    /* close value */
+            break;
+
+        case JSON_FALSE:
+            /* must not have any children */
+            fprintf (file, "false");
+            goto state2;    /* close value */
+            break;
+
+        case JSON_NULL:
+            /* must not have any children */
+            fprintf (file, "null");
+            goto state2;    /* close value */
+            break;
+
+        default:
+            goto error;
+        }
+        if (cursor->child)
+        {
+            cursor = cursor->child;
+            goto state1;    /* open value */
+        }
+        else
+        {
+            /* does not have any children */
+            goto state2;    /* close value */
+        }
+    }
+
+state2:            /* close value */
+    {
+        switch (cursor->type)
+        {
+        case JSON_OBJECT:
+            fprintf (file, "}");
+            break;
+
+        case JSON_ARRAY:
+            fprintf (file, "]");
+            break;
+
+        case JSON_STRING:
+            break;
+        case JSON_NUMBER:
+            break;
+        case JSON_TRUE:
+            break;
+        case JSON_FALSE:
+            break;
+        case JSON_NULL:
+            break;
+        default:
+            goto error;
+        }
+        if ((cursor->parent == NULL) || (cursor == root))
+        {
+            goto end;
+        }
+        else if (cursor->next)
+        {
+            cursor = cursor->next;
+            goto state1;    /* open value */
+        }
+        else
+        {
+            cursor = cursor->parent;
+            goto state2;    /* close value */
+        }
+    }
+
+error:
+    {
+        return JSON_UNKNOWN_PROBLEM;
+    }
+
+end:
+    {
+        fprintf (file, "\n");
+        return JSON_OK;
+    }
+}
+
+
+void
+json_strip_white_spaces (char *text)
+{
+    size_t in, out, length;
+    int state;
+
+    in = 0;
+    out = 0;
+    length = strlen (text);
+    state = 0;        /* possible states: 0 -> document, 1 -> inside a string */
+
+    while (in < length)
+    {
+        switch (text[in])
+        {
+        case '\x20':    /* space */
+        case '\x09':    /* horizontal tab */
+        case '\x0A':    /* line feed or new line */
+        case '\x0D':    /* Carriage return */
+            if (state == 1)
+            {
+                text[out++] = text[in];
+            }
+        break;
+
+        case '\"':
+            switch (state)
+            {
+            case 0:    /* not inside a JSON string */
+                state = 1;
+                break;
+
+            case 1:    /* inside a JSON string */
+                if (text[in - 1] != '\\')
+                {
+                    state = 0;
+                }
+                break;
+            }
+            text[out++] = text[in];
+            break;
+
+        default:
+            text[out++] = text[in];
+        }
+        ++in;
+    }
+    text[out] = '\0';
+}
+
+
+char *
+json_format_string (const char *text)
+{
+    size_t pos = 0, text_length;
+    unsigned int indentation = 0;    /* the current indentation level */
+    unsigned int i;        /* loop iterator variable */
+    char loop;
+
+    rcstring *output;
+    text_length = strlen (text);
+
+    output = rcs_create (text_length);
+    while (pos < text_length)
+    {
+        switch (text[pos])
+        {
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            pos++;
+        break;
+
+        case '{':
+            indentation++;
+            rcs_catcs (output, "{\n", 2);
+            for (i = 0; i < indentation; i++)
+            {
+                rcs_catc (output, '\t');
+            }
+            pos++;
+            break;
+
+        case '}':
+            indentation--;
+            rcs_catc (output, '\n');
+            for (i = 0; i < indentation; i++)
+            {
+                rcs_catc (output, '\t');
+            }
+            rcs_catc (output, '}');
+            pos++;
+            break;
+
+        case ':':
+            rcs_catcs (output, ": ", 2);
+            pos++;
+            break;
+
+        case ',':
+            rcs_catcs (output, ",\n", 2);
+            for (i = 0; i < indentation; i++)
+            {
+                rcs_catc (output, '\t');
+            }
+            pos++;
+            break;
+
+        case '\"':    /* open string */
+            rcs_catc (output, text[pos]);
+            pos++;
+            loop = 1;    /* inner string loop trigger is enabled */
+            while (loop)
+            {
+                if (text[pos] == '\\')    /* escaped sequence */
+                {
+                    rcs_catc (output, '\\');
+                    pos++;
+                    if (text[pos] == '\"')    /* don't consider a \" escaped sequence as an end of string */
+                    {
+                        rcs_catc (output, '\"');
+                        pos++;
+                    }
+                }
+                else if (text[pos] == '\"')    /* reached end of string */
+                {
+                    loop = 0;
+                }
+
+                rcs_catc (output, text[pos]);
+
+                pos++;
+                if (pos >= text_length)
+                {
+                    loop = 0;
+                }
+            }
+            break;
+
+        default:
+            rcs_catc (output, text[pos]);
+            pos++;
+            break;
+        }
+    }
+
+    return rcs_unwrap (output);
+}
+
+
+char *
+json_escape (char *text)
+{
+    rcstring *output;
+    size_t i, length;
+    char buffer[6];
+
+    /* defining the temporary variables */
+    length = strlen (text);
+    output = rcs_create (length);
+    if (output == NULL)
+        return NULL;
+    for (i = 0; i < length; i++)
+    {
+        if (text[i] == '\\')
+        {
+            rcs_catcs (output, "\\\\", 2);
+        }
+        else if (text[i] == '\"')
+        {
+            rcs_catcs (output, "\\\"", 2);
+        }
+        else if (text[i] == '/')
+        {
+            rcs_catcs (output, "\\/", 2);
+        }
+        else if (text[i] == '\b')
+        {
+            rcs_catcs (output, "\\b", 2);
+        }
+        else if (text[i] == '\f')
+        {
+            rcs_catcs (output, "\\f", 2);
+        }
+        else if (text[i] == '\n')
+        {
+            rcs_catcs (output, "\\n", 2);
+        }
+        else if (text[i] == '\r')
+        {
+            rcs_catcs (output, "\\r", 2);
+        }
+        else if (text[i] == '\t')
+        {
+            rcs_catcs (output, "\\t", 2);
+        }
+        else if (text[i] < 0)    /* non-BMP character */
+        {
+            rcs_catc (output, text[i]);
+        }
+        else if (text[i] < 0x20)
+        {
+            sprintf (buffer, "\\u%4.4x", text[i]);
+            rcs_catcs (output, buffer, 6);
+        }
+        else
+        {
+            rcs_catc (output, text[i]);
+        }
+    }
+    return rcs_unwrap (output);
+}
+
+
+void
+json_jpi_init (struct json_parsing_info *jpi)
+{
+    jpi->state = 0;
+    jpi->lex_state = 0;
+    jpi->lex_text = NULL;
+    jpi->p = NULL;
+    jpi->cursor = NULL;
+    jpi->string_length_limit_reached = 0;
+}
+
+
+static int
+lexer (const char *buffer, const char **p, unsigned int *state, rcstring ** text)
+{
+    if (*p == NULL)
+        *p = buffer;
+
+    while (**p != '\0')
+    {
+        switch (*state)
+        {
+
+        case 0:    /* Root document */
+        {
+            switch (*(*p)++)
+            {
+            case '\x20':    /* space */
+            case '\x09':    /* horizontal tab */
+            case '\x0A':    /* line feed or new line */
+            case '\x0D':    /* Carriage return */
+                break;
+
+            case '{':
+                return LEX_BEGIN_OBJECT;
+            case '}':
+                return LEX_END_OBJECT;
+            case '[':
+                return LEX_BEGIN_ARRAY;
+            case ']':
+                return LEX_END_ARRAY;
+            case ':':
+                return LEX_NAME_SEPARATOR;
+            case ',':
+                return LEX_VALUE_SEPARATOR;
+
+            case '\"':
+                *text = rcs_create (RSTRING_DEFAULT);
+                if (*text == NULL)
+                    return LEX_MEMORY;
+                *state = 1;    /* inside a JSON string */
+                break;
+
+            case 't':
+                *state = 7;    /* true: 1 */
+                break;
+
+            case 'f':
+                *state = 10;    /* false: 1 */
+                break;
+
+            case 'n':
+                *state = 14;    /* false: 1 */
+                break;
+
+            case '-':
+                *text = rcs_create (RSTRING_DEFAULT);
+                if (*text == NULL)
+                    return LEX_MEMORY;
+                if (rcs_catc (*text, '-') != RS_OK)
+                    return LEX_MEMORY;
+                *state = 17;    /* number: '0' */
+                break;
+
+            case '0':
+                *text = rcs_create (RSTRING_DEFAULT);
+                if (*text == NULL)
+                    return LEX_MEMORY;
+                if (rcs_catc (*text, '0') != RS_OK)
+                    return LEX_MEMORY;
+                *state = 18;    /* number: '0' */
+                break;
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                *text = rcs_create (RSTRING_DEFAULT);
+            if (*text == NULL)
+                return LEX_MEMORY;
+            if (rcs_catc (*text, *(*p - 1)) != RS_OK)
+                return LEX_MEMORY;
+            *state = 19;    /* number: decimal followup */
+            break;
+
+
+            default:
+                return LEX_INVALID_CHARACTER;
+            }
+        }
+        break;
+
+        case 1:    /* inside a JSON string */
+        {
+            switch (**p)
+            {
+            case 1:
+            case 2:
+            case 3:
+            case 4:
+            case 5:
+            case 6:
+            case 7:
+            case 8:
+            case 9:
+            case 10:    /* line feed */
+            case 11:
+            case 12:
+            case 13:    /* carriage return */
+            case 14:
+            case 15:
+            case 16:
+            case 17:
+            case 18:
+            case 19:
+            case 20:
+            case 21:
+            case 22:
+            case 23:
+            case 24:
+            case 25:
+            case 26:
+            case 27:
+            case 28:
+            case 29:
+            case 30:
+            case 31:
+                /* ASCII control characters can only be present in a JSON string if they are escaped. If not then the document is invalid */
+                return LEX_INVALID_CHARACTER;
+                break;
+
+            case '\"':    /* close JSON string */
+                /* it is expected that, in the routine that calls this function, text is set to NULL */
+                *state = 0;
+                ++*p;
+                return LEX_STRING;
+                break;
+
+            case '\\':
+                if (rcs_catc (*text, '\\') != RS_OK)
+                    return LEX_MEMORY;
+                *state = 2;    /* inside a JSON string: start escape sequence */
+                break;
+
+            default:
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            }
+            ++*p;
+        }
+        break;
+
+        case 2:    /* inside a JSON string: start escape sequence */
+        {
+            switch (**p)
+            {
+            case '\\':
+            case '\"':
+            case '/':
+            case 'b':
+            case 'f':
+            case 'n':
+            case 'r':
+            case 't':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            *state = 1;    /* inside a JSON string */
+            break;
+
+            case 'u':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 3;    /* inside a JSON string: escape unicode */
+                break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+            }
+            ++*p;
+        }
+        break;
+
+        case 3:    /*inside a JSON string: escape unicode */
+        {
+            if ((**p >= 'a') && (**p <= 'f'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 4;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= 'A') && (**p <= 'F'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 4;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= '0') && (**p <= '9'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 4;    /* inside a JSON string: escape unicode */
+            }
+            else
+                return LEX_INVALID_CHARACTER;
+            ++*p;
+        }
+        break;
+
+        case 4:    /* inside a JSON string: escape unicode */
+        {
+            if ((**p >= 'a') && (**p <= 'f'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 5;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= 'A') && (**p <= 'F'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 5;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= '0') && (**p <= '9'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 5;    /* inside a JSON string: escape unicode */
+            }
+            else
+                return LEX_INVALID_CHARACTER;
+            ++*p;
+        }
+
+        case 5:    /* inside a JSON string: escape unicode */
+        {
+            if ((**p >= 'a') && (**p <= 'f'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 6;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= 'A') && (**p <= 'F'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 6;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= '0') && (**p <= '9'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 6;    /* inside a JSON string: escape unicode */
+            }
+            else
+                return LEX_INVALID_CHARACTER;
+            ++*p;
+        }
+        break;
+
+        case 6:    /* inside a JSON string: escape unicode */
+        {
+            if ((**p >= 'a') && (**p <= 'f'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 1;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= 'A') && (**p <= 'F'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 1;    /* inside a JSON string: escape unicode */
+            }
+            else if ((**p >= '0') && (**p <= '9'))
+            {
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                *state = 1;    /* inside a JSON string: escape unicode */
+            }
+            else
+                return LEX_INVALID_CHARACTER;
+            ++*p;
+        }
+        break;
+
+        case 7:    /* true: 1 */
+        {
+            switch (*(*p)++)
+            {
+            case 'r':
+                *state = 8;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 8:    /* true: 2 */
+        {
+            switch (*(*p)++)
+            {
+            case 'u':
+                *state = 9;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 9:    /* true: 3 */
+        {
+            switch (*(*p)++)
+            {
+            case 'e':
+                *state = 0;
+                return LEX_TRUE;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 10:    /* false: 1 */
+        {
+            switch (*(*p)++)
+            {
+            case 'a':
+                *state = 11;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 11:    /* false: 2 */
+        {
+            switch (*(*p)++)
+            {
+            case 'l':
+                *state = 12;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 12:    /* false: 3 */
+        {
+            switch (*(*p)++)
+            {
+            case 's':
+                *state = 13;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 13:    /* false: 4 */
+        {
+            switch (*(*p)++)
+            {
+            case 'e':
+                *state = 0;
+                return LEX_FALSE;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 14:    /* null: 1 */
+        {
+            switch (*(*p)++)
+            {
+            case 'u':
+                *state = 15;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 15:    /* null: 2 */
+        {
+            switch (*(*p)++)
+            {
+            case 'l':
+                *state = 16;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 16:    /* null: 3 */
+        {
+            switch (*(*p)++)
+            {
+            case 'l':
+                *state = 0;
+                return LEX_NULL;
+                break;
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 17:    /* number: minus sign */
+        {
+            switch (**p)
+            {
+            case '0':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                ++*p;
+                *state = 18;    /* number: '0' */
+                break;
+
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 19;    /* number: decimal followup */
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 18:    /* number: '0' */
+        {
+            switch (**p)
+            {
+            case '\x20':    /* space */
+            case '\x09':    /* horizontal tab */
+            case '\x0A':    /* line feed or new line */
+            case '\x0D':    /* Carriage return */
+                ++*p;
+            case ']':
+            case '}':
+            case ',':
+                *state = 0;
+            return LEX_NUMBER;
+            break;
+
+            case '.':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                ++*p;
+                *state = 20;    /* number: frac start */
+                break;
+
+            case 'e':
+            case 'E':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 22;    /* number: exp start */
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 19:    /* number: int followup */
+        {
+            switch (**p)
+            {
+            case '\x20':    /* space */
+            case '\x09':    /* horizontal tab */
+            case '\x0A':    /* line feed or new line */
+            case '\x0D':    /* Carriage return */
+                ++*p;
+            case ']':
+            case '}':
+            case ',':
+                *state = 0;
+            return LEX_NUMBER;
+            break;
+
+            case '.':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+                ++*p;
+                *state = 20;    /* number: frac start */
+                break;
+
+            case 'e':
+            case 'E':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 22;    /* number: exp start */
+            break;
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 20:    /* number: frac start */
+        {
+            switch (**p)
+            {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 21;    /* number: frac continue */
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 21:    /* number: frac continue */
+        {
+            switch (**p)
+            {
+            case '\x20':    /* space */
+            case '\x09':    /* horizontal tab */
+            case '\x0A':    /* line feed or new line */
+            case '\x0D':    /* Carriage return */
+                ++*p;
+            case ']':
+            case '}':
+            case ',':
+                *state = 0;
+            return LEX_NUMBER;
+            break;
+
+            case 'e':
+            case 'E':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 22;    /* number: exp start */
+            break;
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 22:    /* number: exp start */
+        {
+            switch (**p)
+            {
+            case '-':
+            case '+':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 23;    /* number: exp continue */
+            break;
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 24;    /* number: exp end */
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 23:    /* number: exp continue */
+        {
+            switch (**p)
+            {
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            *state = 24;    /* number: exp end */
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        case 24:    /* number: exp end */
+        {
+            switch (**p)
+            {
+            case '\x20':    /* space */
+            case '\x09':    /* horizontal tab */
+            case '\x0A':    /* line feed or new line */
+            case '\x0D':    /* Carriage return */
+                ++*p;
+            case ']':
+            case '}':
+            case ',':
+                *state = 0;
+            return LEX_NUMBER;
+            break;
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                if (rcs_catc (*text, **p) != RS_OK)
+                    return LEX_MEMORY;
+            ++*p;
+            break;
+
+            default:
+                return LEX_INVALID_CHARACTER;
+                break;
+            }
+        }
+        break;
+
+        default:
+            printf ("*state missing: %d\n", *state);
+            return LEX_INVALID_CHARACTER;
+        }
+
+    }
+
+    *p = NULL;
+    return LEX_MORE;
+}
+
+
+enum json_error
+json_parse_fragment (struct json_parsing_info *info, const char *buffer)
+{
+    json_t *temp = NULL;
+
+    info->p = buffer;
+    while (*info->p != '\0')
+    {
+        switch (info->state)
+        {
+        case 0:    /* starting point */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_BEGIN_OBJECT:
+                info->state = 1;    /* begin object */
+                break;
+
+            case LEX_BEGIN_ARRAY:
+                info->state = 7;    /* begin array */
+                break;
+
+            case LEX_INVALID_CHARACTER:
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 1:    /* open object */
+        {
+            if (info->cursor == NULL)
+            {
+                if ((info->cursor = json_new_object ()) == NULL)
+                {
+                    return JSON_MEMORY;
+                }
+            }
+            else
+            {
+                if ((temp = json_new_object ()) == NULL)
+                {
+                    return JSON_MEMORY;
+                }
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->cursor = temp;
+                temp = NULL;
+            }
+            info->state = 2;    /* just entered an object */
+        }
+        break;
+
+        case 2:    /* opened object */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_STRING:
+                if ((temp = json_new_value (JSON_STRING)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO return value according to the value returned from json_insert_child() */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->cursor = temp;
+                temp = NULL;
+                info->state = 5;    /* label, pre label:value separator */
+                break;
+
+            case LEX_END_OBJECT:
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accept whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                    switch (info->cursor->type)
+                    {
+                    case JSON_STRING:
+                        info->cursor = info->cursor->parent;
+                        if (info->cursor->type != JSON_OBJECT)
+                        {
+                            return JSON_BAD_TREE_STRUCTURE;
+                        }
+                        else
+                        {
+                            info->state = 3;    /* finished adding a field to an object */
+                        }
+                        break;
+
+                    case JSON_ARRAY:
+                        info->state = 9;
+                        break;
+
+                    default:
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 3:    /* finished adding a field to an object */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_VALUE_SEPARATOR:
+                info->state = 4;    /* sibling, post-object */
+                break;
+
+            case LEX_END_OBJECT:
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* parse until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                    switch (info->cursor->type)
+                    {
+                    case JSON_STRING:
+                        info->cursor = info->cursor->parent;
+                        if (info->cursor->type != JSON_OBJECT)
+                        {
+                            return JSON_BAD_TREE_STRUCTURE;
+                        }
+                        else
+                        {
+                            info->state = 3;    /* finished adding a field to an object */
+                        }
+                        break;
+
+                    case JSON_ARRAY:
+                        info->state = 9;
+                        break;
+
+                    default:
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 4:    /* sibling, post-object */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_STRING:
+                if ((temp = json_new_value (JSON_STRING)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->cursor = temp;
+                temp = NULL;
+                info->state = 5;
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            case LEX_INVALID_CHARACTER:
+                return JSON_ILLEGAL_CHARACTER;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 5:    /* label, pre name separator */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_NAME_SEPARATOR:
+                info->state = 6;    /* label, pos label:value separator */
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 6:    /* label, pos name separator */
+        {
+            unsigned int value;    /* to avoid redundant code */
+
+            switch (value = lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_STRING:
+                if ((temp = json_new_value (JSON_STRING)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO specify the exact error message */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accepts whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                }
+                temp = NULL;
+                info->state = 3;    /* finished adding a field to an object */
+                break;
+
+            case LEX_NUMBER:
+                if ((temp = json_new_value (JSON_NUMBER)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO specify the exact error message */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accepts whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                }
+                temp = NULL;
+                info->state = 3;    /* finished adding a field to an object */
+                break;
+
+            case LEX_TRUE:
+                if ((temp = json_new_value (JSON_TRUE)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO specify the exact error message */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accepts whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                }
+                temp = NULL;
+                info->state = 3;    /* finished adding a field to an object */
+                break;
+
+            case LEX_FALSE:
+                if ((temp = json_new_value (JSON_FALSE)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO specify the exact error message */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accepts whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                }
+                temp = NULL;
+                info->state = 3;    /* finished adding a field to an object */
+                break;
+
+            case LEX_NULL:
+                if ((temp = json_new_value (JSON_NULL)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    /*TODO specify the exact error message */
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accepts whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                }
+                temp = NULL;
+                info->state = 3;    /* finished adding a field to an object */
+                break;
+
+            case LEX_BEGIN_OBJECT:
+                info->state = 1;
+                break;
+
+            case LEX_BEGIN_ARRAY:
+                info->state = 7;
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            case LEX_MEMORY:
+                return JSON_MEMORY;
+                break;
+
+            case LEX_INVALID_CHARACTER:
+                return JSON_ILLEGAL_CHARACTER;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 7:    /* open array */
+        {
+            if (info->cursor == NULL)
+            {
+                if ((info->cursor = json_new_array ()) == NULL)
+                {
+                    return JSON_MEMORY;
+                }
+            }
+            else
+            {
+                if ((temp = json_new_array ()) == NULL)
+                {
+                    return JSON_MEMORY;
+                }
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->cursor = temp;
+                temp = NULL;
+            }
+            info->state = 8;    /* just entered an array */
+        }
+        break;
+
+        case 8:    /* just entered an array */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_STRING:
+                if ((temp = json_new_value (JSON_STRING)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                temp = NULL;
+                info->state = 9;    /* label, pre label:value separator */
+                break;
+
+            case LEX_NUMBER:
+                if ((temp = json_new_value (JSON_NUMBER)) == NULL)
+                    return JSON_MEMORY;
+                temp->text = rcs_unwrap (info->lex_text), info->lex_text = NULL;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                temp = NULL;
+                info->state = 9;    /* label, pre label:value separator */
+                break;
+
+            case LEX_TRUE:
+                if ((temp = json_new_value (JSON_TRUE)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->state = 9;    /* label, pre label:value separator */
+                break;
+
+            case LEX_FALSE:
+                if ((temp = json_new_value (JSON_FALSE)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->state = 9;    /* label, pre label:value separator */
+                break;
+
+            case LEX_NULL:
+                if ((temp = json_new_value (JSON_NULL)) == NULL)
+                    return JSON_MEMORY;
+                if (json_insert_child (info->cursor, temp) != JSON_OK)
+                {
+                    return JSON_UNKNOWN_PROBLEM;
+                }
+                info->state = 9;    /* label, pre label:value separator */
+                break;
+
+            case LEX_BEGIN_ARRAY:
+                info->state = 7;    /* open array */
+                break;
+
+            case LEX_END_ARRAY:
+                if (info->cursor->parent == NULL)
+                {
+                    /*TODO implement this */
+                    info->state = 99;    /* finished document. only accept whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                    switch (info->cursor->type)
+                    {
+                    case JSON_STRING:
+                        if (info->cursor->parent == NULL)
+                            return JSON_BAD_TREE_STRUCTURE;
+                        else
+                        {
+                            info->cursor = info->cursor->parent;
+                            if (info->cursor->type != JSON_OBJECT)
+                            {
+                                return JSON_BAD_TREE_STRUCTURE;
+                            }
+
+                            info->state = 3;    /* followup to adding child to array */
+                        }
+                        break;
+
+                    case JSON_ARRAY:
+                        info->state = 9;    /* followup to adding child to array */
+                        break;
+
+                    default:
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+                break;
+
+            case LEX_BEGIN_OBJECT:
+                info->state = 1;    /* open object */
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            case LEX_INVALID_CHARACTER:
+                return JSON_ILLEGAL_CHARACTER;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 9:    /* followup to adding child to array */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_VALUE_SEPARATOR:
+                info->state = 8;
+                break;
+
+            case LEX_END_ARRAY:
+                if (info->cursor->parent == NULL)
+                {
+                    info->state = 99;    /* finished document. only accept whitespaces until EOF */
+                }
+                else
+                {
+                    info->cursor = info->cursor->parent;
+                    switch (info->cursor->type)
+                    {
+                    case JSON_STRING:
+                        if (info->cursor->parent == NULL)
+                        {
+                            info->state = 99;    /* finished document. only accept whitespaces until EOF */
+                        }
+                        else
+                        {
+                            info->cursor = info->cursor->parent;
+                            if (info->cursor->type != JSON_OBJECT)
+                            {
+                                return JSON_BAD_TREE_STRUCTURE;
+                            }
+                            else
+                            {
+                                info->state = 3;    /* followup to adding child to array */
+                            }
+                        }
+                        break;
+
+                    case JSON_ARRAY:
+                        info->state = 9;    /* followup to adding child to array */
+                        break;
+
+                    default:
+                        return JSON_BAD_TREE_STRUCTURE;
+                    }
+                }
+                break;
+
+            case LEX_MORE:
+                return JSON_INCOMPLETE_DOCUMENT;
+                break;
+
+            default:
+                printf ("state %d: defaulted\n", info->state);
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        case 99:    /* finished document. only accept whitespaces until EOF */
+        {
+            switch (lexer (buffer, &info->p, &info->lex_state, & info->lex_text))
+            {
+            case LEX_MORE:
+                return JSON_WAITING_FOR_EOF;
+                break;
+
+            case LEX_MEMORY:
+                return JSON_MEMORY;
+                break;
+
+            default:
+                return JSON_MALFORMED_DOCUMENT;
+                break;
+            }
+        }
+        break;
+
+        default:
+            printf ("invalid parser state %d: defaulted\n", info->state);
+            return JSON_UNKNOWN_PROBLEM;
+        }
+    }
+    info->p = NULL;
+    if (info->state == 99)
+        return JSON_WAITING_FOR_EOF;
+    else
+        return JSON_INCOMPLETE_DOCUMENT;
+}
+
+
+
+enum json_error
+json_parse_document (json_t ** root, const char *text)
+{
+    enum json_error error;
+    struct json_parsing_info *jpi;
+
+    /* initialize the parsing structure */
+    if (VIR_ALLOC(jpi) < 0)
+    {
+        return JSON_MEMORY;
+    }
+    json_jpi_init (jpi);
+
+    error = json_parse_fragment (jpi, text);
+    if ((error == JSON_WAITING_FOR_EOF) || (error == JSON_OK))
+    {
+        *root = jpi->cursor;
+        VIR_FREE(jpi);
+        return JSON_OK;
+    }
+    else
+    {
+        VIR_FREE(jpi);
+        return error;
+    }
+}
+
+
+enum json_error
+json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c)
+{
+    /*TODO handle a string instead of a single char */
+    /* temp variables */
+    rcstring *temp;
+
+    temp = NULL;
+
+    /* goto where we left off */
+    switch (jsps->state)
+    {
+    case 0:        /* general state. everything goes. */
+        goto state0;
+        break;
+    case 1:        /* parse string */
+        goto state1;
+        break;
+    case 2:        /* parse string: escaped character */
+        goto state2;
+        break;
+    case 3:        /* parse string: escaped unicode 1 */
+        goto state3;
+        break;
+    case 4:        /* parse string: escaped unicode 2 */
+        goto state4;
+        break;
+    case 5:        /* parse string: escaped unicode 3 */
+        goto state5;
+        break;
+    case 6:        /* parse string: escaped unicode 4 */
+        goto state6;
+        break;
+    case 7:        /* parse true: tr */
+        goto state7;
+        break;
+    case 8:        /* parse true: tru */
+        goto state8;
+        break;
+    case 9:        /* parse true: true */
+        goto state9;
+        break;
+    case 10:        /* parse false: fa */
+        goto state10;
+        break;
+    case 11:        /* parse false: fal */
+        goto state11;
+        break;
+    case 12:        /* parse false: fals */
+        goto state12;
+        break;
+    case 13:        /* parse false: false */
+        goto state13;
+        break;
+    case 14:        /* parse null: nu */
+        goto state14;
+        break;
+    case 15:        /* parse null: nul */
+        goto state15;
+        break;
+    case 16:        /* parse null: null */
+        goto state16;
+        break;
+    case 17:        /* parse number: 0 */
+        goto state17;
+        break;
+    case 18:        /* parse number: start fraccional part */
+        goto state18;
+        break;
+    case 19:        /* parse number: fraccional part */
+        goto state19;
+        break;
+    case 20:        /* parse number: start exponent part */
+        goto state20;
+        break;
+    case 21:        /* parse number: exponent part */
+        goto state21;
+        break;
+    case 22:        /* parse number: exponent sign part */
+        goto state22;
+        break;
+    case 23:        /* parse number: start negative */
+        goto state23;
+        break;
+    case 24:        /* parse number: decimal part */
+        goto state24;
+        break;
+    case 25:        /* open object */
+        goto state25;
+        break;
+    case 26:        /* close object/array */
+        goto state26;
+        break;
+    case 27:        /* sibling followup */
+        goto state27;
+        break;
+
+    default:        /* oops... this should never be reached */
+        return JSON_UNKNOWN_PROBLEM;
+    }
+
+state0:            /* starting point */
+    {
+        switch (c)
+        {
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            break;
+
+        case '\"':    /* starting a string */
+            jsps->string_length_limit_reached = 0;
+            jsps->state = 1;
+            break;
+
+        case '{':
+            if (jsf->open_object != NULL)
+                jsf->open_object ();
+            jsps->state = 25;    /*open object */
+            break;
+
+        case '}':
+            if (jsf->close_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case '[':
+            if (jsf->open_array != NULL)
+                jsf->open_array ();
+/*                      jsps->state = 0;        // redundant*/
+            break;
+
+        case ']':
+            if (jsf->close_array != NULL)
+                jsf->close_array ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case 't':
+            jsps->state = 7;    /* parse true: tr */
+            break;
+
+        case 'f':
+            jsps->state = 10;    /* parse false: fa */
+            break;
+
+        case 'n':
+            jsps->state = 14;    /* parse null: nu */
+            break;
+
+        case ':':
+            if (jsf->label_value_separator != NULL)
+                jsf->label_value_separator ();
+/*                      jsps->state = 0;        // redundant*/
+            break;
+
+        case ',':
+            if (jsf->sibling_separator != NULL)
+                jsf->sibling_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+        case '0':
+            jsps->string_length_limit_reached = 0;
+            jsps->state = 17;    /* parse number: 0 */
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '0') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            break;
+
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            jsps->string_length_limit_reached = 0;
+        jsps->state = 24;    /* parse number: decimal */
+        if ((jsps->temp = rcs_create (5)) == NULL)
+        {
+            return JSON_MEMORY;
+        }
+        if (rcs_catc ((jsps->temp), c) != RS_OK)
+        {
+            return JSON_MEMORY;
+        }
+        break;
+
+        case '-':
+            jsps->string_length_limit_reached = 0;
+            jsps->state = 23;    /* number: */
+            jsps->temp = NULL;
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '-') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state1:            /* parse string */
+    {
+        switch (c)
+        {
+        case '\\':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1)    /* check if there is space for a two character escape sequence */
+                {
+                    if (rcs_catc ((jsps->temp), '\\') != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+            jsps->state = 2;    /* parse string: escaped character */
+            break;
+
+        case '\"':    /* end of string */
+            if ((jsps->temp) != NULL)
+            {
+                jsps->state = 0;    /* starting point */
+                if (jsf->new_string != NULL)
+                    jsf->new_string (((jsps->temp))->text);    /*copied or integral? */
+                rcs_free (& jsps->temp);
+            }
+            else
+                return JSON_UNKNOWN_PROBLEM;
+            break;
+
+        default:
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)    /* check if there is space for a two character escape sequence */
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+            break;
+        }
+        return JSON_OK;
+    }
+
+state2:            /* parse string: escaped character */
+    {
+        switch (c)
+        {
+        case '\"':
+        case '\\':
+        case '/':
+        case 'b':
+        case 'f':
+        case 'n':
+        case 'r':
+        case 't':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        break;
+
+        case 'u':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 4)
+                {
+                    if (rcs_catc ((jsps->temp), 'u') != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+            jsps->state = 3;    /* parse string: escaped unicode 1; */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state3:            /* parse string: escaped unicode 1 */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'e':
+        case 'f':
+        case 'A':
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'E':
+        case 'F':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 3)
+                {
+                    if (rcs_catc ((jsps->temp), 'u') != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 4;    /* parse string. escaped unicode 2 */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+        }
+        return JSON_OK;
+    }
+
+state4:            /* parse string: escaped unicode 2 */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'e':
+        case 'f':
+        case 'A':
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'E':
+        case 'F':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 2)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 5;    /* parse string. escaped unicode 3 */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+        }
+        return JSON_OK;
+    }
+
+state5:            /* parse string: escaped unicode 3 */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'e':
+        case 'f':
+        case 'A':
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'E':
+        case 'F':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH - 1)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 6;    /* parse string. escaped unicode 4 */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+        }
+        return JSON_OK;
+    }
+
+state6:            /* parse string: escaped unicode 4 */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+        case 'a':
+        case 'b':
+        case 'c':
+        case 'd':
+        case 'e':
+        case 'f':
+        case 'A':
+        case 'B':
+        case 'C':
+        case 'D':
+        case 'E':
+        case 'F':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 1;    /* parse string */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+        }
+        return JSON_OK;
+    }
+
+state7:            /* parse true: tr */
+    {
+        if (c != 'r')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 8;    /* parse true: tru */
+        return JSON_OK;
+    }
+
+state8:            /* parse true: tru */
+    {
+        if (c != 'u')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 9;    /* parse true: true */
+        return JSON_OK;
+    }
+
+state9:            /* parse true: true */
+    {
+        if (c != 'e')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 0;    /* back to general state. */
+        if (jsf->new_true != NULL)
+            jsf->new_true ();
+        return JSON_OK;
+    }
+
+state10:            /* parse false: fa */
+    {
+        if (c != 'a')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 11;    /* parse true: fal */
+        return JSON_OK;
+    }
+
+state11:            /* parse false: fal */
+    {
+        if (c != 'l')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 12;    /* parse true: fals */
+        return JSON_OK;
+    }
+
+state12:            /* parse false: fals */
+    {
+        if (c != 's')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 13;    /* parse true: false */
+        return JSON_OK;
+    }
+
+state13:            /* parse false: false */
+    {
+        if (c != 'e')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 0;    /* general state. everything goes. */
+        if (jsf->new_false != NULL)
+            jsf->new_false ();
+        return JSON_OK;
+    }
+
+state14:            /* parse null: nu */
+    {
+        if (c != 'u')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 15;    /* parse null: nul */
+        return JSON_OK;
+    }
+
+state15:            /* parse null: nul */
+    {
+        if (c != 'l')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 16;    /* parse null: null */
+        return JSON_OK;
+    }
+
+state16:            /* parse null: null */
+    {
+        if (c != 'l')
+        {
+            return JSON_ILLEGAL_CHARACTER;
+        }
+
+        jsps->state = 0;    /* general state. everything goes. */
+        if (jsf->new_null != NULL)
+            jsf->new_null ();
+        return JSON_OK;
+    }
+
+state17:            /* parse number: 0 */
+    {
+        switch (c)
+        {
+        case '.':
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '.') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            jsps->state = 18;    /* parse number: fraccional part */
+            break;
+
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+        if (jsf->new_number != NULL)
+        {
+            jsf->new_number ((jsps->temp)->text);
+        }
+        rcs_free (& jsps->temp);
+
+        jsps->state = 0;
+        break;
+
+        case '}':
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ']':
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_array ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ',':
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->label_value_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+
+        return JSON_OK;
+    }
+
+state18:            /* parse number: start fraccional part */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 19;    /* parse number: fractional part */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state19:            /* parse number: fraccional part */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+/*                      jsps->state = 19;       // parse number: fractional part*/
+        break;
+
+        case 'e':
+        case 'E':
+            if (rcs_catc ((jsps->temp), c) != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+        jsps->state = 20;    /* parse number: start exponent part */
+        break;
+
+
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+        if (jsf->new_number != NULL)
+        {
+            jsf->new_number ((jsps->temp)->text);
+        }
+        rcs_free (& jsps->temp);
+
+        jsps->state = 0;
+        break;
+
+        case '}':
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ']':
+            if (jsf->new_number != NULL)
+            {
+                if ((jsps->temp) == NULL)
+                    return JSON_MEMORY;
+                jsf->new_number ((jsps->temp)->text);
+                rcs_free (& jsps->temp);
+            }
+            else
+            {
+                rcs_free (& jsps->temp);
+                jsps->temp = NULL;
+            }
+            if (jsf->open_object != NULL)
+                jsf->close_array ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ',':
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->label_value_separator != NULL)
+                jsf->label_value_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state20:            /* parse number: start exponent part */
+    {
+        switch (c)
+        {
+        case '+':
+        case '-':
+            jsps->string_length_limit_reached = 0;
+        if (rcs_catc ((jsps->temp), c) != RS_OK)
+        {
+            return JSON_MEMORY;
+        }
+
+        jsps->state = 22;    /* parse number: exponent sign part */
+        break;
+
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 21;    /* parse number: exponent part */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state21:            /* parse number: exponent part */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+                {
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+/*                              jsps->state = 21;       // parse number: exponent part*/
+        break;
+
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+        if (jsf->new_number != NULL)
+        {
+            jsf->new_number ((jsps->temp)->text);
+        }
+        rcs_free (& jsps->temp);
+
+        jsps->state = 0;
+        break;
+
+        case '}':
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object */
+            break;
+
+        case ']':
+            if (jsf->new_number != NULL)
+            {
+                if ((jsps->temp) == NULL)
+                    return JSON_MEMORY;
+                jsf->new_number ((jsps->temp)->text);
+                free (jsps->temp);
+                jsps->temp = NULL;
+            }
+            else
+            {
+                free ((jsps->temp));
+                jsps->temp = NULL;
+            }
+            if (jsf->open_object != NULL)
+                jsf->close_array ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ',':
+            if (jsf->new_number != NULL)
+            {
+                if ((jsps->temp) == NULL)
+                    return JSON_MEMORY;
+                jsf->new_number ((jsps->temp)->text);
+                free ((jsps->temp));
+                jsps->temp = NULL;
+            }
+            else
+            {
+                free (jsps->temp);
+                jsps->temp = NULL;
+            }
+            if (jsf->label_value_separator != NULL)
+                jsf->label_value_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state22:            /* parse number: start exponent part */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH)
+                {
+                    rcs_catc ((jsps->temp), c);
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+        jsps->state = 21;    /* parse number: exponent part */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state23:            /* parse number: start negative */
+    {
+        switch (c)
+        {
+        case '0':
+            rcs_catc ((jsps->temp), c);
+            jsps->state = 17;    /* parse number: 0 */
+            break;
+
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+                {
+                    if ((jsps->temp = rcs_create (5)) == NULL)
+                    {
+                        return JSON_MEMORY;
+                    }
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                    else
+                    {
+                        jsps->string_length_limit_reached = 1;
+                    }
+                }
+            }
+        jsps->state = 24;    /* parse number: start decimal part */
+        break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state24:            /* parse number: decimal part */
+    {
+        switch (c)
+        {
+        case '0':
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            if (!jsps->string_length_limit_reached)
+            {
+                if (rcs_length ((jsps->temp)) < JSON_MAX_STRING_LENGTH / 2)
+                {
+                    if ((jsps->temp = rcs_create (5)) == NULL)
+                    {
+                        return JSON_MEMORY;
+                    }
+                    if (rcs_catc ((jsps->temp), c) != RS_OK)
+                    {
+                        return JSON_MEMORY;
+                    }
+                }
+                else
+                {
+                    jsps->string_length_limit_reached = 1;
+                }
+            }
+/*                              jsps->state = 24;       // parse number: decimal part*/
+        break;
+
+        case '.':
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '.') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+
+            jsps->state = 18;    /* parse number: start exponent part */
+            break;
+
+        case 'e':
+        case 'E':
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+        if (rcs_catc ((jsps->temp), c) != RS_OK)
+        {
+            return JSON_MEMORY;
+        }
+
+        jsps->string_length_limit_reached = 0;    /* reset to accept the exponential part */
+        jsps->state = 20;    /* parse number: start exponent part */
+        break;
+
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+        if (jsf->new_number != NULL)
+        {
+            jsf->new_number ((jsps->temp)->text);
+        }
+        rcs_free (& jsps->temp);
+
+        jsps->state = 0;
+        break;
+
+        case '}':
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ']':
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->open_object != NULL)
+                jsf->close_array ();
+            jsps->state = 26;    /* close object/array */
+            break;
+
+        case ',':
+            if ((jsps->temp) == NULL)
+                return JSON_MEMORY;
+            if (jsf->new_number != NULL)
+            {
+                jsf->new_number ((jsps->temp)->text);
+            }
+            rcs_free (& jsps->temp);
+
+            if (jsf->label_value_separator != NULL)
+                jsf->label_value_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state25:            /* open object */
+    {
+        switch (c)
+        {
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            break;
+
+        case '\"':
+            jsps->temp = NULL;
+            jsps->state = 1;
+            break;
+
+        case '}':
+            if (jsf->close_object != NULL)
+                jsf->close_object ();
+            jsps->state = 26;    /* close object */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state26:            /* close object/array */
+    {
+        switch (c)
+        {
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            break;
+
+        case '}':
+            if (jsf->close_object != NULL)
+                jsf->close_object ();
+/*                      jsp->state = 26;        // close object*/
+            break;
+
+        case ']':
+            if (jsf->close_array != NULL)
+                jsf->close_array ();
+/*                      jsps->state = 26;       // close object/array*/
+            break;
+
+        case ',':
+            if (jsf->sibling_separator != NULL)
+                jsf->sibling_separator ();
+            jsps->state = 27;    /* sibling followup */
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+state27:            /* sibling followup */
+    {
+        switch (c)
+        {
+        case '\x20':
+        case '\x09':
+        case '\x0A':
+        case '\x0D':    /* JSON insignificant white spaces */
+            break;
+
+        case '\"':
+            jsps->state = 1;
+            jsps->temp = NULL;
+            break;
+
+        case '{':
+            if (jsf->open_object != NULL)
+                jsf->open_object ();
+            jsps->state = 25;    /*open object */
+            break;
+
+        case '[':
+            if (jsf->open_array != NULL)
+                jsf->open_array ();
+/*                      jsps->state = 0;        // redundant*/
+            break;
+
+        case 't':
+            jsps->state = 7;    /* parse true: tr */
+            break;
+
+        case 'f':
+            jsps->state = 10;    /* parse false: fa */
+            break;
+
+        case 'n':
+            jsps->state = 14;    /* parse null: nu */
+            break;
+
+        case '0':
+            jsps->state = 17;    /* parse number: 0 */
+            if ((jsps->temp = rcs_create (5)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '0') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            break;
+
+        case '1':
+        case '2':
+        case '3':
+        case '4':
+        case '5':
+        case '6':
+        case '7':
+        case '8':
+        case '9':
+            jsps->state = 24;    /* parse number: decimal */
+        if ((jsps->temp = rcs_create (5)) == NULL)
+        {
+            return JSON_MEMORY;
+        }
+        if (rcs_catc ((jsps->temp), c) != RS_OK)
+        {
+            return JSON_MEMORY;
+        }
+        break;
+
+        case '-':
+            jsps->state = 23;    /* number: */
+            if ((jsps->temp = rcs_create (RSTRING_DEFAULT)) == NULL)
+            {
+                return JSON_MEMORY;
+            }
+            if (rcs_catc ((jsps->temp), '-') != RS_OK)
+            {
+                return JSON_MEMORY;
+            }
+            break;
+
+        default:
+            return JSON_ILLEGAL_CHARACTER;
+            break;
+        }
+        return JSON_OK;
+    }
+
+    return JSON_UNKNOWN_PROBLEM;
+}
+
+
+enum json_error
+json_find_first_label (const json_t * object, const char *text_label, json_t **value)
+{
+    json_t *cursor;
+
+    for (cursor = object->child; cursor != NULL; cursor = cursor->next)
+    {
+        if (strcmp (cursor->text, text_label) == 0) {
+            *value = cursor;
+            return JSON_OK;
+        }
+    }
+    *value = NULL;
+    return JSON_UNKNOWN_PROBLEM;
+}
+
diff --git a/src/util/json.h b/src/util/json.h
new file mode 100644
index 0000000..c093816
--- /dev/null
+++ b/src/util/json.h
@@ -0,0 +1,311 @@
+/***************************************************************************
+ *   Copyright (C) 2007 by Rui Maciel   *
+ *   rui.maciel at gmail.com   *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Library General Public License as       *
+ *   published by the Free Software Foundation; either version 2 of the    *
+ *   License, or (at your option) any later version.                       *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU Library General Public     *
+ *   License along with this program; if not, write to the                 *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+/** @file json.h A small library that helps deal with JSON-encoded information
+    \ingroup JSON
+
+    \note error handling is only in a very rudimentary form.
+    \author Rui Maciel	rui_maciel at users.sourceforge.net
+    \version v1.1
+*/
+
+#include <stdint.h>
+#include <stdio.h>
+
+#ifndef JSON_H
+#define JSON_H
+
+#define JSON_MAX_STRING_LENGTH SIZE_MAX-1
+
+struct rui_cstring
+{
+    char *text;		/*<! char c-string */
+    size_t length;		/*<! put in place to avoid strlen() calls */
+    size_t max;		/*<! usable memory allocated to text minus the space for the nul character */
+};
+
+typedef struct rui_cstring rcstring;
+
+/**
+   The descriptions of the json_value node type
+**/
+    enum json_value_type
+    { JSON_STRING = 0, JSON_NUMBER, JSON_OBJECT, JSON_ARRAY, JSON_TRUE, JSON_FALSE, JSON_NULL };
+
+/**
+   The error messages produced by the JSON parsers
+**/
+    enum json_error
+    {
+        JSON_OK = 1,	/*!< everything went smoothly */
+        JSON_INCOMPLETE_DOCUMENT,	/*!< the parsed document didn't ended */
+        JSON_WAITING_FOR_EOF,	/*!< A complete JSON document tree was already finished but needs to get to EOF. Other characters beyond whitespaces produce errors */
+        JSON_MALFORMED_DOCUMENT,	/* the JSON document which was fed to this parser is malformed */
+        JSON_INCOMPATIBLE_TYPE,	/*!< the currently parsed type does not belong here */
+        JSON_MEMORY,	/*!< an error occurred when allocating memory */
+        JSON_ILLEGAL_CHARACTER,	/*!< the currently parsed character does not belong here */
+        JSON_BAD_TREE_STRUCTURE,	/*!< the document tree structure is malformed */
+        JSON_MAXIMUM_LENGTH,	/*!< the parsed string reached the maximum allowed size */
+        JSON_UNKNOWN_PROBLEM	/*!< some random, unaccounted problem occurred */
+    };
+
+
+/**
+   The JSON document tree node, which is a basic JSON type
+**/
+    typedef struct json_value
+    {
+        enum json_value_type type;	/*!< the type of node */
+        char *text;	/*!< The text stored by the node. It stores UTF-8 strings and is used exclusively by the JSON_STRING and JSON_NUMBER node types */
+
+        /* FIFO queue data */
+        struct json_value *next;	/*!< The pointer pointing to the next element in the FIFO sibling list */
+        struct json_value *previous;	/*!< The pointer pointing to the previous element in the FIFO sibling list */
+        struct json_value *parent;	/*!< The pointer pointing to the parent node in the document tree */
+        struct json_value *child;	/*!< The pointer pointing to the first child node in the document tree */
+        struct json_value *child_end;	/*!< The pointer pointing to the last child node in the document tree */
+    } json_t;
+
+
+/**
+   The structure holding all information needed to resume parsing
+**/
+    struct json_parsing_info
+    {
+        unsigned int state;	/*!< the state where the parsing was left on the last parser run */
+        unsigned int lex_state;
+        rcstring *lex_text;
+        const char *p;
+        int string_length_limit_reached;	/*!< flag informing if the string limit length defined by JSON_MAX_STRING_LENGTH was reached */
+        json_t *cursor;	/*!< pointers to nodes belonging to the document tree which aid the document parsing */
+    };
+
+
+/**
+   The structure which holds the pointers to the functions that will be called by the saxy parser whenever their evens are triggered
+**/
+    struct json_saxy_functions
+    {
+        int (*open_object) (void);
+        int (*close_object) (void);
+        int (*open_array) (void);
+        int (*close_array) (void);
+        int (*new_string) (char *text);
+        int (*new_number) (char *text);
+        int (*new_true) (void);
+        int (*new_false) (void);
+        int (*new_null) (void);
+        int (*label_value_separator) (void);
+        int (*sibling_separator) (void);
+    };
+
+
+/**
+   The structure holding the information needed for json_saxy_parse to resume parsing
+**/
+    struct json_saxy_parser_status
+    {
+        unsigned int state;	/*!< current parser state */
+        int string_length_limit_reached;	/*!< flag informing if the string limit length defined by JSON_MAX_STRING_LENGTH was reached */
+        rcstring *temp;	/*!< temporary string which will be used to build up parsed strings between parser runs. */
+    };
+
+
+/** 
+    Buils a json_t document by parsing an open file
+    @param file a pointer to an object controlling a stream, returned by fopen()
+    @param document a reference to a json_t pointer, set to NULL, which will store the parsed document
+    @return a json_error error code according to how the parsing operation went.
+**/
+    enum json_error json_stream_parse (FILE * file, json_t ** document);
+
+
+/**
+   Creates a new JSON value and defines it's type
+   @param type the value's type
+   @return a pointer to the newly created value structure
+**/
+    json_t *json_new_value (const enum json_value_type type);
+
+
+/**
+   Creates a new JSON string and defines it's text
+   @param text the value's text
+   @return a pointer to the newly created JSON string value
+**/
+    json_t *json_new_string (const char *text);
+
+
+/**
+   Creates a new JSON number and defines it's text. The user is responsible for the number string's correctness
+   @param text the value's number
+   @return a pointer to the newly created JSON string value
+**/
+    json_t *json_new_number (const char *text);
+
+
+/**
+   Creates a new JSON object
+   @return a pointer to the newly created JSON object value
+**/
+    json_t *json_new_object (void);
+
+
+/**
+   Creates a new JSON array
+   @return a pointer to the newly created JSON array value
+**/
+    json_t *json_new_array (void);
+
+
+/**
+   Creates a new JSON null
+   @return a pointer to the newly created JSON null value
+**/
+    json_t *json_new_null (void);
+
+
+/**
+   Creates a new JSON true
+   @return a pointer to the newly created JSON true value
+**/
+    json_t *json_new_true (void);
+
+
+/**
+   Creates a new JSON false
+   @return a pointer to the newly created JSON false value
+**/
+    json_t *json_new_false (void);
+
+
+/**
+   Frees the memory appointed to the value fed as the parameter, as well as all the child nodes
+   @param value the root node of the tree being freed
+**/
+    void json_free_value (json_t ** value);
+
+
+/**
+   Inserts a child node into a parent node, as well as performs some document tree integrity checks.
+   @param parent the parent node
+   @param child the node being added as a child to parent
+   @return the error code corresponding to the operation result
+**/
+    enum json_error json_insert_child (json_t * parent, json_t * child);
+
+
+/**
+   Inserts a label:value pair into a parent node, as well as performs some document tree integrity checks.
+   @param parent the parent node
+   @param text_label a char string which serves as the label in the label:value pair
+   @param value the value in the label:value pair
+   @return the error code corresponding to the operation result
+**/
+    enum json_error json_insert_pair_into_object (json_t * parent, const char *text_label, json_t * value);
+
+
+/**
+   Produces a JSON markup text document from a document tree
+   @param root The document's root node
+   @param text a pointer to a char string that will hold the JSON document text.
+   @return  a json_error code describing how the operation went
+**/
+    enum json_error json_tree_to_string (json_t * root, char **text);
+
+
+/**
+   Produces a JSON markup text document from a json_t document tree to a text stream 
+   @param file a opened file stream
+   @param root The document's root node
+   @return  a json_error code describing how the operation went
+**/
+    enum json_error json_stream_output (FILE * file, json_t * root);
+
+
+/**
+   Strips all JSON white spaces from the text string
+   @param text a char string holding a JSON document or document snippet 
+**/
+    void json_strip_white_spaces (char *text);
+
+
+/**
+   Formats a JSON markup text contained in the given string
+   @param text a JSON formatted document
+   @return a pointer to a char string holding the formated document
+**/
+    char *json_format_string (const char *text);
+
+
+/**
+   Outputs a new UTF8 c-string which replaces all characters that must be escaped with their respective escaped versions
+   @param text an UTF8 char text string
+   @return an UTF-8 c-string holding the same text string but with escaped characters
+**/
+    char *json_escape (char *text);
+
+
+/**
+   This function takes care of the tedious task of initializing any instance of 
+   struct json_parsing_info
+   @param jpi a pointer to a struct json_parsing_info instance
+**/
+    void json_jpi_init (struct json_parsing_info *jpi);
+
+
+/**
+   Produces a document tree sequentially from a JSON markup text fragment
+   @param info the information necessary to resume parsing any incomplete document
+   @param buffer a null-terminated c-string containing a JSON document fragment
+   @return a code describing how the operation ended up
+**/
+    enum json_error json_parse_fragment (struct json_parsing_info *info, const char *buffer);
+
+
+/**
+   Produces a document tree from a JSON markup text string that contains a complete document
+   @param root a reference to a pointer to a json_t type. The function allocates memory to the passed pointer and sets up the value
+   @param text a c-string containing a complete JSON text document
+   @return a pointer to the new document tree or NULL if some error occurred
+**/
+    enum json_error json_parse_document (json_t ** root, const char *text);
+
+
+/**
+   Function to perform a SAX-like parsing of any JSON document or document fragment that is passed to it
+   @param jsps a structure holding the status information of the current parser
+   @param jsf a structure holding the function pointers to the event functions
+   @param c the character to be parsed
+   @return a json_error code informing how the parsing went
+**/
+    enum json_error json_saxy_parse (struct json_saxy_parser_status *jsps, struct json_saxy_functions *jsf, char c);
+
+
+/**
+   Searches through the object's children for a label holding the text text_label
+   @param object a json_value of type JSON_OBJECT
+   @param text_label the c-string to search for through the object's child labels
+   @return a pointer to the first label holding a text equal to text_label or NULL if there is no such label or if object has no children
+**/
+    enum json_error json_find_first_label (const json_t * object, const char *text_label, json_t **value);
+
+
+#endif
-- 
1.6.2.5




More information about the libvir-list mailing list