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(a)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(a)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(a)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(a)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(a)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