diff options
author | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-10-27 18:21:26 +0000 |
---|---|---|
committer | patacongo <patacongo@42af7a65-404d-4744-a932-0658087f49c3> | 2012-10-27 18:21:26 +0000 |
commit | 42000d013c9e817baf40d6445ac015991bb4a50b (patch) | |
tree | 406de17abc4dcaf0ee6ea4ad85bc4c45ea1d4e85 /apps | |
parent | b3ee6803286f2d85eb1893780b4c20265d6b8f67 (diff) | |
download | nuttx-42000d013c9e817baf40d6445ac015991bb4a50b.tar.gz nuttx-42000d013c9e817baf40d6445ac015991bb4a50b.tar.bz2 nuttx-42000d013c9e817baf40d6445ac015991bb4a50b.zip |
Add port of cJSON from Darcy Gong
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5267 42af7a65-404d-4744-a932-0658087f49c3
Diffstat (limited to 'apps')
-rw-r--r-- | apps/ChangeLog.txt | 3 | ||||
-rw-r--r-- | apps/examples/Kconfig | 156 | ||||
-rw-r--r-- | apps/examples/Make.defs | 4 | ||||
-rw-r--r-- | apps/examples/Makefile | 6 | ||||
-rw-r--r-- | apps/examples/README.txt | 13 | ||||
-rw-r--r-- | apps/examples/json/Kconfig | 14 | ||||
-rw-r--r-- | apps/examples/json/Makefile | 103 | ||||
-rw-r--r-- | apps/examples/json/README | 258 | ||||
-rw-r--r-- | apps/examples/json/json_main.c | 325 | ||||
-rw-r--r-- | apps/include/netutils/cJSON.h | 206 | ||||
-rw-r--r-- | apps/netutils/Kconfig | 42 | ||||
-rw-r--r-- | apps/netutils/Make.defs | 4 | ||||
-rw-r--r-- | apps/netutils/Makefile | 3 | ||||
-rw-r--r-- | apps/netutils/README.txt | 6 | ||||
-rw-r--r-- | apps/netutils/json/Kconfig | 18 | ||||
-rw-r--r-- | apps/netutils/json/Makefile | 91 | ||||
-rw-r--r-- | apps/netutils/json/README | 258 | ||||
-rw-r--r-- | apps/netutils/json/cJSON.c | 1576 |
18 files changed, 2886 insertions, 200 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt index 14d107b3f..b75637ad2 100644 --- a/apps/ChangeLog.txt +++ b/apps/ChangeLog.txt @@ -384,4 +384,7 @@ by one (Mike Smith). * apps/examples/elf: Test example for the ELF loader. * apps/examples/elf: The ELF module test example appears fully functional. + * apps/netutils/json: Add a snapshot of the cJSON project. Contributed by + Darcy Gong. + * apps/examples/json: Test example for cJSON from Darcy Gong diff --git a/apps/examples/Kconfig b/apps/examples/Kconfig index a37308eaa..f851ad8bd 100644 --- a/apps/examples/Kconfig +++ b/apps/examples/Kconfig @@ -3,210 +3,56 @@ # see misc/tools/kconfig-language.txt. # -menu "ADC Example" source "$APPSDIR/examples/adc/Kconfig" -endmenu - -menu "Buttons Example" source "$APPSDIR/examples/buttons/Kconfig" -endmenu - -menu "CAN Example" source "$APPSDIR/examples/can/Kconfig" -endmenu - -menu "USB CDC/ACM Class Driver Example" source "$APPSDIR/examples/cdcacm/Kconfig" -endmenu - -menu "USB composite Class Driver Example" source "$APPSDIR/examples/composite/Kconfig" -endmenu - -menu "DHCP Server Example" source "$APPSDIR/examples/dhcpd/Kconfig" -endmenu - -menu "ELF Loader Example" source "$APPSDIR/examples/elf/Kconfig" -endmenu - -menu "FTP Client Example" source "$APPSDIR/examples/ftpc/Kconfig" -endmenu - -menu "FTP Server Example" source "$APPSDIR/examples/ftpd/Kconfig" -endmenu - -menu "\"Hello, World!\" Example" source "$APPSDIR/examples/hello/Kconfig" -endmenu - -menu "\"Hello, World!\" C++ Example" source "$APPSDIR/examples/helloxx/Kconfig" -endmenu - -menu "USB HID Keyboard Example" +source "$APPSDIR/examples/json/Kconfig" source "$APPSDIR/examples/hidkbd/Kconfig" -endmenu - -menu "IGMP Example" source "$APPSDIR/examples/igmp/Kconfig" -endmenu - -menu "LCD Read/Write Example" source "$APPSDIR/examples/lcdrw/Kconfig" -endmenu - -menu "Memory Management Example" source "$APPSDIR/examples/mm/Kconfig" -endmenu - -menu "File System Mount Example" source "$APPSDIR/examples/mount/Kconfig" -endmenu - -menu "FreeModBus Example" source "$APPSDIR/examples/modbus/Kconfig" -endmenu - -menu "Network Test Example" source "$APPSDIR/examples/nettest/Kconfig" -endmenu - -menu "NuttShell (NSH) Example" source "$APPSDIR/examples/nsh/Kconfig" -endmenu - -menu "NULL Example" source "$APPSDIR/examples/null/Kconfig" -endmenu - -menu "NX Graphics Example" source "$APPSDIR/examples/nx/Kconfig" -endmenu - -menu "NxConsole Example" source "$APPSDIR/examples/nxconsole/Kconfig" -endmenu - -menu "NXFFS File System Example" source "$APPSDIR/examples/nxffs/Kconfig" -endmenu - -menu "NXFLAT Example" source "$APPSDIR/examples/nxflat/Kconfig" -endmenu - -menu "NX Graphics \"Hello, World!\" Example" source "$APPSDIR/examples/nxhello/Kconfig" -endmenu - -menu "NX Graphics image Example" source "$APPSDIR/examples/nximage/Kconfig" -endmenu - -menu "NX Graphics lines Example" source "$APPSDIR/examples/nxlines/Kconfig" -endmenu - -menu "NX Graphics Text Example" source "$APPSDIR/examples/nxtext/Kconfig" -endmenu - -menu "OS Test Example" source "$APPSDIR/examples/ostest/Kconfig" -endmenu - -menu "Pascal \"Hello, World!\"example" source "$APPSDIR/examples/pashello/Kconfig" -endmenu - -menu "Pipe Example" source "$APPSDIR/examples/pipe/Kconfig" -endmenu - -menu "Poll Example" source "$APPSDIR/examples/poll/Kconfig" -endmenu - -menu "Pulse Width Modulation (PWM) Example" source "$APPSDIR/examples/pwm/Kconfig" -endmenu - -menu "Quadrature Encoder Example" source "$APPSDIR/examples/qencoder/Kconfig" -endmenu - -menu "RGMP Example" source "$APPSDIR/examples/rgmp/Kconfig" -endmenu - -menu "ROMFS Example" source "$APPSDIR/examples/romfs/Kconfig" -endmenu - -menu "sendmail Example" source "$APPSDIR/examples/sendmail/Kconfig" -endmenu - -menu "Serial Loopback Example" source "$APPSDIR/examples/serloop/Kconfig" -endmenu - -menu "Telnet Daemon Example" source "$APPSDIR/examples/telnetd/Kconfig" -endmenu - -menu "THTTPD Web Server Example" source "$APPSDIR/examples/thttpd/Kconfig" -endmenu - -menu "TIFF Generation Example" source "$APPSDIR/examples/tiff/Kconfig" -endmenu - -menu "Touchscreen Example" source "$APPSDIR/examples/touchscreen/Kconfig" -endmenu - -menu "UDP Example" source "$APPSDIR/examples/udp/Kconfig" -endmenu - -menu "UDP Discovery Daemon Example" source "$APPSDIR/examples/discover/Kconfig" -endmenu - -menu "uIP Web Server Example" source "$APPSDIR/examples/uip/Kconfig" -endmenu - -menu "USB Serial Test Example" source "$APPSDIR/examples/usbserial/Kconfig" -endmenu - -menu "USB Mass Storage Class Example" source "$APPSDIR/examples/usbstorage/Kconfig" -endmenu - -menu "USB Serial Terminal Example" source "$APPSDIR/examples/usbterm/Kconfig" -endmenu - -menu "Watchdog timer Example" source "$APPSDIR/examples/watchdog/Kconfig" -endmenu - -menu "wget Example" source "$APPSDIR/examples/wget/Kconfig" -endmenu - -menu "WLAN Example" source "$APPSDIR/examples/wlan/Kconfig" -endmenu - -menu "XML RPC Example" source "$APPSDIR/examples/xmlrpc/Kconfig" -endmenu diff --git a/apps/examples/Make.defs b/apps/examples/Make.defs index 0733d9419..ad5653c82 100644 --- a/apps/examples/Make.defs +++ b/apps/examples/Make.defs @@ -90,6 +90,10 @@ ifeq ($(CONFIG_EXAMPLES_IGMP),y) CONFIGURED_APPS += examples/igmp endif +ifeq ($(CONFIG_EXAMPLES_JSON),y) +CONFIGURED_APPS += examples/json +endif + ifeq ($(CONFIG_EXAMPLES_LCDRW),y) CONFIGURED_APPS += examples/lcdrw endif diff --git a/apps/examples/Makefile b/apps/examples/Makefile index ef59e6822..0ebc300e4 100644 --- a/apps/examples/Makefile +++ b/apps/examples/Makefile @@ -38,7 +38,7 @@ # Sub-directories SUBDIRS = adc buttons can cdcacm composite dhcpd discover elf ftpc ftpd hello -SUBDIRS += helloxx hidkbd igmp lcdrw mm modbus mount nettest nsh null nx +SUBDIRS += helloxx hidkbd igmp json lcdrw mm modbus mount nettest nsh null nx SUBDIRS += nxconsole nxffs nxflat nxhello nximage nxlines nxtext ostest SUBDIRS += pashello pipe poll pwm qencoder rgmp romfs serloop telnetd SUBDIRS += thttpd tiff touchscreen udp uip usbserial sendmail usbstorage @@ -57,8 +57,8 @@ SUBDIRS += usbterm watchdog wget wlan CNTXTDIRS = pwm ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) -CNTXTDIRS += adc can cdcacm composite discover ftpd dhcpd modbus nettest -CNTXTDIRS += qencoder telnetd watchdog +CNTXTDIRS += adc can cdcacm composite dhcpd discover ftpd json modbus +CNTXTDIRS += nettest qencoder telnetd watchdog endif ifeq ($(CONFIG_EXAMPLES_HELLO_BUILTIN),y) diff --git a/apps/examples/README.txt b/apps/examples/README.txt index 70dffc989..abf95e208 100644 --- a/apps/examples/README.txt +++ b/apps/examples/README.txt @@ -542,6 +542,19 @@ examples/igmp CONFIGURED_APPS += uiplib +examples/json +^^^^^^^^^^^^^ + + This example exercises the cJSON implementation at apps/netutils/json. + This example contains logic taken from the cJSON project: + + http://sourceforge.net/projects/cjson/ + + The example corresponds to SVN revision r42 (with lots of changes for + NuttX coding standards). As of r42, the SVN repository was last updated + on 2011-10-10 so I presume that the code is stable and there is no risk + of maintaining duplicate logic in the NuttX repository. + examples/lcdrw ^^^^^^^^^^^^^^ diff --git a/apps/examples/json/Kconfig b/apps/examples/json/Kconfig new file mode 100644 index 000000000..6d948f0d7 --- /dev/null +++ b/apps/examples/json/Kconfig @@ -0,0 +1,14 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config EXAMPLES_JSON + bool "JSON example" + default n + select NETUTILS_JSON + ---help--- + An example for the netutils/json library. + +if EXAMPLES_JSON +endif diff --git a/apps/examples/json/Makefile b/apps/examples/json/Makefile new file mode 100644 index 000000000..c2fd26bff --- /dev/null +++ b/apps/examples/json/Makefile @@ -0,0 +1,103 @@ +############################################################################ +# apps/examples/json/Makefile +# +# Copyright (C) 2012 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt <gnutt@nuttx.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +# cJSON built-in application info + +APPNAME = json +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = 2048 + +ASRCS = +CSRCS = json_main.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: clean depend distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +.context: +ifeq ($(CONFIG_NSH_BUILTIN_APPS),y) + $(call REGISTER,$(APPNAME),$(PRIORITY),$(STACKSIZE),$(APPNAME)_main) + @touch $@ +endif + +context: .context + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/examples/json/README b/apps/examples/json/README new file mode 100644 index 000000000..de9a0b2ed --- /dev/null +++ b/apps/examples/json/README @@ -0,0 +1,258 @@ +apps/examples/json/README.txt +============================= + +This directory contains logic taken from the cJSON project: + +http://sourceforge.net/projects/cjson/ + +This corresponds to SVN revision r42 (with lots of changes for NuttX coding +standards). As of r42, the SVN repository was last updated on 2011-10-10 +so I presume that the code is stable and there is no risk of maintaining +duplicate logic in the NuttX repository. + +Contents +======== + + o License + o Welcome to cJSON + +License +======= + + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Welcome to cJSON +================ + +cJSON aims to be the dumbest possible parser that you can get your job done with. +It's a single file of C, and a single header file. + +JSON is described best here: http://www.json.org/ +It's like XML, but fat-free. You use it to move data around, store things, or just +generally represent your program's state. + +First up, how do I build? +Add cJSON.c to your project, and put cJSON.h somewhere in the header search path. +For example, to build the test app: + +gcc cJSON.c test.c -o test -lm +./test + +As a library, cJSON exists to take away as much legwork as it can, but not get in your way. +As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it +in one of two modes: Auto and Manual. Let's have a quick run-through. + +I lifted some JSON from this page: http://www.json.org/fatfree.html +That page inspired me to write cJSON, which is a parser that tries to share the same +philosophy as JSON itself. Simple, dumb, out of the way. + +Some JSON: +{ + "name": "Jack (\"Bee\") Nimble", + "format": { + "type": "rect", + "width": 1920, + "height": 1080, + "interlace": false, + "frame rate": 24 + } +} + +Assume that you got this from a file, a webserver, or magic JSON elves, whatever, +you have a char * to it. Everything is a cJSON struct. +Get it parsed: + cJSON *root = cJSON_Parse(my_json_string); + +This is an object. We're in C. We don't have objects. But we do have structs. +What's the framerate? + + cJSON *format = cJSON_GetObjectItem(root,"format"); + int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint; + +Want to change the framerate? + cJSON_GetObjectItem(format,"frame rate")->valueint=25; + +Back to disk? + char *rendered=cJSON_Print(root); + +Finished? Delete the root (this takes care of everything else). + cJSON_Delete(root); + +That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers +before you dereference them. If you want to see how you'd build this struct in code? + cJSON *root,*fmt; + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject()); + cJSON_AddStringToObject(fmt,"type", "rect"); + cJSON_AddNumberToObject(fmt,"width", 1920); + cJSON_AddNumberToObject(fmt,"height", 1080); + cJSON_AddFalseToObject (fmt,"interlace"); + cJSON_AddNumberToObject(fmt,"frame rate", 24); + +Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup. +Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and +a few from elsewhere. + +What about manual mode? First up you need some detail. +Let's cover how the cJSON objects represent the JSON data. +cJSON doesn't distinguish arrays from objects in handling; just type. +Each cJSON has, potentially, a child, siblings, value, a name. + +The root object has: Object Type and a Child +The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: +Sibling has type Object, name "format", and a child. +That child has type String, name "type", value "rect", and a sibling: +Sibling has type Number, name "width", value 1920, and a sibling: +Sibling has type Number, name "height", value 1080, and a sibling: +Sibling hs type False, name "interlace", and a sibling: +Sibling has type Number, name "frame rate", value 24 + +Here's the structure: +typedef struct cJSON { + struct cJSON *next,*prev; + struct cJSON *child; + + int type; + + char *valuestring; + int valueint; + double valuedouble; + + char *string; +} cJSON; + +By default all values are 0 unless set by virtue of being meaningful. + +next/prev is a doubly linked list of siblings. next takes you to your sibling, +prev takes you back from your sibling to you. +Only objects and arrays have a "child", and it's the head of the doubly linked list. +A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. +The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in +cJSON.h + +A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read +valuedouble. + +Any entry which is in the linked list which is the child of an object will have a "string" +which is the "name" of the entry. When I said "name" in the above example, that's "string". +"string" is the JSON name for the 'variable name' if you will. + +Now you can trivially walk the lists, recursively, and parse as you please. +You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take +the root object, and traverse the structure (which is, formally, an N-tree), +and tokenise as you please. If you wanted to build a callback style parser, this is how +you'd do it (just an example, since these things are very specific): + +void parse_and_callback(cJSON *item,const char *prefix) +{ + while (item) + { + char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2); + sprintf(newprefix,"%s/%s",prefix,item->name); + int dorecurse=callback(newprefix, item->type, item); + if (item->child && dorecurse) parse_and_callback(item->child,newprefix); + item=item->next; + free(newprefix); + } +} + +The prefix process will build you a separated list, to simplify your callback handling. +The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or +let you invoke it per-item. For the item above, your callback might look like this: + +int callback(const char *name,int type,cJSON *item) +{ + if (!strcmp(name,"name")) { /* populate name */ } + else if (!strcmp(name,"format/type") { /* handle "rect" */ } + else if (!strcmp(name,"format/width") { /* 800 */ } + else if (!strcmp(name,"format/height") { /* 600 */ } + else if (!strcmp(name,"format/interlace") { /* false */ } + else if (!strcmp(name,"format/frame rate") { /* 24 */ } + return 1; +} + +Alternatively, you might like to parse iteratively. +You'd use: + +void parse_object(cJSON *item) +{ + int i; for (i=0;i<cJSON_GetArraySize(item);i++) + { + cJSON *subitem=cJSON_GetArrayItem(item,i); + // handle subitem. + } +} + +Or, for PROPER manual mode: + +void parse_object(cJSON *item) +{ + cJSON *subitem=item->child; + while (subitem) + { + // handle subitem + if (subitem->child) parse_object(subitem->child); + + subitem=subitem->next; + } +} + +Of course, this should look familiar, since this is just a stripped-down version +of the callback-parser. + +This should cover most uses you'll find for parsing. The rest should be possible +to infer.. and if in doubt, read the source! There's not a lot of it! ;) + +In terms of constructing JSON data, the example code above is the right way to do it. +You can, of course, hand your sub-objects to other functions to populate. +Also, if you find a use for it, you can manually build the objects. +For instance, suppose you wanted to build an array of objects? + +cJSON *objects[24]; + +cJSON *Create_array_of_anything(cJSON **items,int num) +{ + int i;cJSON *prev, *root=cJSON_CreateArray(); + for (i=0;i<24;i++) + { + if (!i) root->child=objects[i]; + else prev->next=objects[i], objects[i]->prev=prev; + prev=objects[i]; + } + return root; +} + +and simply: Create_array_of_anything(objects,24); + +cJSON doesn't make any assumptions about what order you create things in. +You can attach the objects, as above, and later add children to each +of those objects. + +As soon as you call cJSON_Print, it renders the structure to text. + +The test.c code shows how to handle a bunch of typical cases. If you uncomment +the code, it'll load, parse and print a bunch of test files, also from json.org, +which are more complex than I'd care to try and stash into a const char array[]. + +Enjoy cJSON! + +- Dave Gamble, Aug 2009 diff --git a/apps/examples/json/json_main.c b/apps/examples/json/json_main.c new file mode 100644 index 000000000..443be387b --- /dev/null +++ b/apps/examples/json/json_main.c @@ -0,0 +1,325 @@ +/**************************************************************************** + * apps/examples/json/json_main.c + * + * This file is a part of NuttX: + * + * Copyright (C) 2012 Gregory Nutt. All rights reserved. + * Ported by: Darcy Gong + * + * And derives from the cJSON Project which has an MIT license: + * + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <apps/netutils/cJSON.h> + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Used by some code below as an example datatype. */ + +struct record +{ + const char *precision; + double lat; + double lon; + const char *address; + const char *city; + const char *state; + const char *zip; + const char *country; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: doit + * + * Description: + * Parse text to JSON, then render back to text, and print! + * + ****************************************************************************/ + +static void doit(const char *text) +{ + char *out; + cJSON *json; + json = cJSON_Parse(text); + if (!json) + { + printf("Error before: [%s]\n", cJSON_GetErrorPtr()); + } + else + { + out = cJSON_Print(json); + cJSON_Delete(json); + printf("%s\n", out); + free(out); + } +} + +/**************************************************************************** + * Name: dofile + * + * Description: + * Read a file, parse, render back, etc. + * + ****************************************************************************/ + +#if 0 /* Not used */ +static void dofile(char *filename) +{ + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + long len = ftell(f); + fseek(f, 0, SEEK_SET); + char *data = malloc(len + 1); + fread(data, 1, len, f); + fclose(f); + doit(data); + free(data); +} +#endif + +/**************************************************************************** + * Name: create_objects + * + * Description: + * Create a bunch of objects as demonstration. + * + ****************************************************************************/ + +static void create_objects(void) +{ + cJSON *root; + cJSON *fmt; + cJSON *img; + cJSON *thm; + cJSON *fld; + char *out; + int i; + + /* Our "days of the week" array */ + + static const char *strings[7] = + { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; + + /* Our matrix: */ + + static const int numbers[3][3] = { {0, -1, 0}, {1, 0, 0}, {0, 0, 1} }; + + /* Our "gallery" item: */ + + static const int ids[4] = { 116, 943, 234, 38793 }; + + /* Our array of "records": */ + + static const struct record fields[2] = + { + {"zip", 37.7668, -1.223959e+2, "", "SAN FRANCISCO", "CA", "94107", "US"}, + {"zip", 37.371991, -1.22026e+2, "", "SUNNYVALE", "CA", "94085", "US"} + }; + + /* Here we construct some JSON standards, from the JSON site. */ + /* Our "Video" datatype: */ + + root = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt = cJSON_CreateObject()); + cJSON_AddStringToObject(fmt, "type", "rect"); + cJSON_AddNumberToObject(fmt, "width", 1920); + cJSON_AddNumberToObject(fmt, "height", 1080); + cJSON_AddFalseToObject(fmt, "interlace"); + cJSON_AddNumberToObject(fmt, "frame rate", 24); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + /* Print to text, Delete the cJSON, print it, release the string. */ + + root = cJSON_CreateStringArray(strings, 7); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateArray(); + for (i = 0; i < 3; i++) + { + cJSON_AddItemToArray(root, cJSON_CreateIntArray(numbers[i], 3)); + } + +/*cJSON_ReplaceItemInArray(root,1,cJSON_CreateString("Replacement")); */ + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateObject(); + cJSON_AddItemToObject(root, "Image", img = cJSON_CreateObject()); + cJSON_AddNumberToObject(img, "Width", 800); + cJSON_AddNumberToObject(img, "Height", 600); + cJSON_AddStringToObject(img, "Title", "View from 15th Floor"); + cJSON_AddItemToObject(img, "Thumbnail", thm = cJSON_CreateObject()); + cJSON_AddStringToObject(thm, "Url", "http:/*www.example.com/image/481989943"); + cJSON_AddNumberToObject(thm, "Height", 125); + cJSON_AddStringToObject(thm, "Width", "100"); + cJSON_AddItemToObject(img, "IDs", cJSON_CreateIntArray(ids, 4)); + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); + + root = cJSON_CreateArray(); + for (i = 0; i < 2; i++) + { + cJSON_AddItemToArray(root, fld = cJSON_CreateObject()); + cJSON_AddStringToObject(fld, "precision", fields[i].precision); + cJSON_AddNumberToObject(fld, "Latitude", fields[i].lat); + cJSON_AddNumberToObject(fld, "Longitude", fields[i].lon); + cJSON_AddStringToObject(fld, "Address", fields[i].address); + cJSON_AddStringToObject(fld, "City", fields[i].city); + cJSON_AddStringToObject(fld, "State", fields[i].state); + cJSON_AddStringToObject(fld, "Zip", fields[i].zip); + cJSON_AddStringToObject(fld, "Country", fields[i].country); + } + +/*cJSON_ReplaceItemInObject(cJSON_GetArrayItem(root,1),"City",cJSON_CreateIntArray(ids,4)); */ + out = cJSON_Print(root); + cJSON_Delete(root); + printf("%s\n", out); + free(out); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ +/**************************************************************************** + * Name: + * + * Description: + * + * + ****************************************************************************/ + +int json_main(int argc, const char *argv[]) +{ + /* a bunch of json: */ + + static const char text1[] = + "{\n" + " \"name\": \"Jack (\\\"Bee\\\") Nimble\",\n" + " \"format\": {\n" + " \"type\": \"rect\",\n" + " \"width\": 1920,\n" + " \"height\": 1080,\n" + " \"interlace\": false,\n" + " \"frame rate\": 24\n" + " }\n" + "}"; + + static const char text2[] = + "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]"; + + static const char text3[] = + "[\n" + " [0, -1, 0],\n" + " [1, 0, 0],\n" + " [0, 0, 1]\n" + "]\n"; + + static const char text4[] = + "{\n" + " \"Image\": {\n" + " \"Width\": 800,\n" + " \"Height\": 600,\n" + " \"Title\": \"View from 15th Floor\",\n" + " \"Thumbnail\": {\n" + " \"Url\": \"http:/*www.example.com/image/481989943\",\n" + " \"Height\": 125,\n" + " \"Width\": \"100\"\n" + " },\n" + " \"IDs\": [116, 943, 234, 38793]\n" + " }\n" + "}"; + + static const char text5[] = + "[\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.7668,\n" + " \"Longitude\": -122.3959,\n" + " \"Address\": \"\",\n" + " \"City\": \"SAN FRANCISCO\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94107\",\n" + " \"Country\": \"US\"\n" + " },\n" + " {\n" + " \"precision\": \"zip\",\n" + " \"Latitude\": 37.371991,\n" + " \"Longitude\": -122.026020,\n" + " \"Address\": \"\",\n" + " \"City\": \"SUNNYVALE\",\n" + " \"State\": \"CA\",\n" + " \"Zip\": \"94085\",\n" + " \"Country\": \"US\"\n" + " }\n" + "]"; + + /* Process each json textblock by parsing, then rebuilding: */ doit(text1); + + doit(text2); + doit(text3); + doit(text4); + doit(text5); + + /* Parse standard testfiles: */ + +#if 0 /* Not yet */ + dofile("../../tests/test1"); + dofile("../../tests/test2"); + dofile("../../tests/test3"); + dofile("../../tests/test4"); + dofile("../../tests/test5"); +#endif + + /* Now some samplecode for building objects concisely: */ + + create_objects(); + return 0; +} diff --git a/apps/include/netutils/cJSON.h b/apps/include/netutils/cJSON.h new file mode 100644 index 000000000..3526782bd --- /dev/null +++ b/apps/include/netutils/cJSON.h @@ -0,0 +1,206 @@ +/**************************************************************************** + * apps/netutils/json/cJSON.c + * + * This file is a part of NuttX: + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Ported by: Darcy Gong + * + * And derives from the cJSON Project which has an MIT license: + * + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + ****************************************************************************/ + +#ifndef __APPS_INCLUDE_NETUTILS_JSON_H +#define __APPS_INCLUDE_NETUTILS_JSON_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define cJSON_False 0 +#define cJSON_True 1 +#define cJSON_NULL 2 +#define cJSON_Number 3 +#define cJSON_String 4 +#define cJSON_Array 5 +#define cJSON_Object 6 + +#define cJSON_IsReference 256 + +#define cJSON_AddNullToObject(object,name) \ + cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) \ + cJSON_AddItemToObject(object, name, cJSON_CreateTrue())cd +#define cJSON_AddFalseToObject(object,name) \ + cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddNumberToObject(object,name,n) \ + cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) \ + cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* The cJSON structure: */ + +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use + * GetArraySize/GetArrayItem/GetObjectItem + */ + + struct cJSON *next,*prev; + + /* An array or object item will have a child pointer pointing to a chain + * of the items in the array/object. + */ + + struct cJSON *child; + + int type; /* The type of the item, as above. */ + char *valuestring; /* The item's string, if type==cJSON_String */ + int valueint; /* The item's number, if type==cJSON_Number */ + double valuedouble; /* The item's number, if type==cJSON_Number */ + + /* The item's name string, if this item is the child of, or is in the list + * of subitems of an object. + */ + + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/* Supply malloc, realloc and free functions to cJSON */ + +void cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Supply a block of JSON, and this returns a cJSON object you can + * interrogate. Call cJSON_Delete when finished. + */ + +cJSON *cJSON_Parse(const char *value); + +/* Render a cJSON entity to text for transfer/storage. Free the char* when + * finished. + */ + +char *cJSON_Print(cJSON *item); + +/* Render a cJSON entity to text for transfer/storage without any + * formatting. Free the char* when finished. + */ + +char *cJSON_PrintUnformatted(cJSON *item); + +/* Delete a cJSON entity and all subentities. */ + +void cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ + +int cJSON_GetArraySize(cJSON *array); + +/* Retrieve item number "item" from array "array". Returns NULL if + * unsuccessful. + */ + +cJSON *cJSON_GetArrayItem(cJSON *array, int item); + +/* Get item "string" from object. Case insensitive. */ + +cJSON *cJSON_GetObjectItem(cJSON *object, const char *string); + +/* For analysing failed parses. This returns a pointer to the parse error. + * You'll probably need to look a few chars back to make sense of it. + * Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. + */ + +const char *cJSON_GetErrorPtr(); + +/* These calls create a cJSON item of the appropriate type. */ + +cJSON *cJSON_CreateNull(); +cJSON *cJSON_CreateTrue(); +cJSON *cJSON_CreateFalse(); +cJSON *cJSON_CreateBool(int b); +cJSON *cJSON_CreateNumber(double num); +cJSON *cJSON_CreateString(const char *string); +cJSON *cJSON_CreateArray(); +cJSON *cJSON_CreateObject(); + +/* These utilities create an Array of count items. */ + +cJSON *cJSON_CreateIntArray(const int *numbers, int count); +cJSON *cJSON_CreateFloatArray(const float *numbers, int count); +cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); +cJSON *cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ + +void cJSON_AddItemToArray(cJSON *array, cJSON *item); +void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); + +/* Append reference to item to the specified array/object. Use this when you + * want to add an existing cJSON to a new cJSON, but don't want to corrupt + * your existing cJSON. + */ + +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ + +cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); +void cJSON_DeleteItemFromArray(cJSON *array, int which); +cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); +void cJSON_DeleteItemFromObject(cJSON *object, const char *string); + +/* Update array items. */ + +void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem); + +#ifdef __cplusplus +} +#endif + +#endif /* __APPS_INCLUDE_NETUTILS_JSON_H */ diff --git a/apps/netutils/Kconfig b/apps/netutils/Kconfig index 4141e5b03..d59afb28e 100644 --- a/apps/netutils/Kconfig +++ b/apps/netutils/Kconfig @@ -5,58 +5,18 @@ comment "Networking Utilities" -menu "DHCP client" source "$APPSDIR/netutils/dhcpc/Kconfig" -endmenu - -menu "DHCP server" source "$APPSDIR/netutils/dhcpd/Kconfig" -endmenu - -menu "FTP client" source "$APPSDIR/netutils/ftpc/Kconfig" -endmenu - -menu "FTP server" source "$APPSDIR/netutils/ftpd/Kconfig" -endmenu - -menu "Name resolution" +source "$APPSDIR/netutils/json/Kconfig" source "$APPSDIR/netutils/resolv/Kconfig" -endmenu - -menu "SMTP" source "$APPSDIR/netutils/smtp/Kconfig" -endmenu - -menu "TFTP client" source "$APPSDIR/netutils/telnetd/Kconfig" -endmenu - -menu "TFTP client" source "$APPSDIR/netutils/tftpc/Kconfig" -endmenu - -menu "THTTPD web server" source "$APPSDIR/netutils/thttpd/Kconfig" -endmenu - -menu "uIP support library" source "$APPSDIR/netutils/uiplib/Kconfig" -endmenu - -menu "uIP web client" source "$APPSDIR/netutils/webclient/Kconfig" -endmenu - -menu "uIP web server" source "$APPSDIR/netutils/webserver/Kconfig" -endmenu - -menu "UDP Discovery Utility" source "$APPSDIR/netutils/discover/Kconfig" -endmenu - -menu "XML-RPC library" source "$APPSDIR/netutils/xmlrpc/Kconfig" -endmenu diff --git a/apps/netutils/Make.defs b/apps/netutils/Make.defs index ae72ab0fd..9fb25b80f 100644 --- a/apps/netutils/Make.defs +++ b/apps/netutils/Make.defs @@ -50,6 +50,10 @@ ifeq ($(CONFIG_NETUTILS_FTPD),y) CONFIGURED_APPS += netutils/ftpd endif +ifeq ($(CONFIG_NETUTILS_JSON),y) +CONFIGURED_APPS += netutils/json +endif + ifeq ($(CONFIG_NETUTILS_RESOLV),y) CONFIGURED_APPS += netutils/resolv endif diff --git a/apps/netutils/Makefile b/apps/netutils/Makefile index 058b0f629..e60771d32 100644 --- a/apps/netutils/Makefile +++ b/apps/netutils/Makefile @@ -37,8 +37,9 @@ # Sub-directories +SUBDIRS = json ifeq ($(CONFIG_NET),y) -SUBDIRS = uiplib dhcpc dhcpd discover ftpc ftpd resolv smtp telnetd +SUBDIRS += uiplib dhcpc dhcpd discover ftpc ftpd resolv smtp telnetd SUBDIRS += webclient webserver tftpc thttpd xmlrpc endif diff --git a/apps/netutils/README.txt b/apps/netutils/README.txt index e97bf5a61..eaf508d48 100644 --- a/apps/netutils/README.txt +++ b/apps/netutils/README.txt @@ -47,6 +47,12 @@ highly influenced by uIP) include: device class so that groups of devices can be discovered. It is also possible to address all classes with a kind of broadcast discover. (Contributed by Max Holtzberg). + json - cJSON is an ultra-lightweight, portable, single-file, + simple-as-can-be ANSI-C compliant JSON parser, under MIT + license. Embeddable Lightweight XML-RPC Server discussed at + http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364. + This code was taken from http://sourceforge.net/projects/cjson/ + and adapted for NuttX by Darcy Gong. tftpc - TFTP client. See apps/include/netutils/tftp.h for interface information. telnetd - TELNET server. This is the Telnet logic adapted from diff --git a/apps/netutils/json/Kconfig b/apps/netutils/json/Kconfig new file mode 100644 index 000000000..741ae5660 --- /dev/null +++ b/apps/netutils/json/Kconfig @@ -0,0 +1,18 @@ +# +# For a description of the syntax of this configuration file, +# see misc/tools/kconfig-language.txt. +# + +config NETUTILS_JSON + bool "cJSON library" + default n + ---help--- + Enables the cJSON library. cJSON is an ultra-lightweight, portable, + single-file, simple-as-can-be ANSI-C compliant JSON parser, under MIT + license. Embeddable Lightweight XML-RPC Server discussed at + http://www.drdobbs.com/web-development/an-embeddable-lightweight-xml-rpc-server/184405364. + This code was taken from http://sourceforge.net/projects/cjson/ and + adapted for NuttX by Darcy Gong. + +if NETUTILS_JSON +endif diff --git a/apps/netutils/json/Makefile b/apps/netutils/json/Makefile new file mode 100644 index 000000000..1426a1a51 --- /dev/null +++ b/apps/netutils/json/Makefile @@ -0,0 +1,91 @@ +############################################################################ +# apps/netutils/json/Makefile +# +# Copyright (C) 2012 Gregory Nutt. All rights reserved. +# Author: Gregory Nutt <gnutt@nuttx.org> +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/.config +-include $(TOPDIR)/Make.defs +include $(APPDIR)/Make.defs + +ASRCS = +CSRCS = cJSON.c + +AOBJS = $(ASRCS:.S=$(OBJEXT)) +COBJS = $(CSRCS:.c=$(OBJEXT)) + +SRCS = $(ASRCS) $(CSRCS) +OBJS = $(AOBJS) $(COBJS) + +ifeq ($(WINTOOL),y) + BIN = "${shell cygpath -w $(APPDIR)/libapps$(LIBEXT)}" +else + BIN = "$(APPDIR)/libapps$(LIBEXT)" +endif + +ROOTDEPPATH = --dep-path . + +# Common build + +VPATH = + +all: .built +.PHONY: context depend clean distclean + +$(AOBJS): %$(OBJEXT): %.S + $(call ASSEMBLE, $<, $@) + +$(COBJS): %$(OBJEXT): %.c + $(call COMPILE, $<, $@) + +.built: $(OBJS) + @( for obj in $(OBJS) ; do \ + $(call ARCHIVE, $(BIN), $${obj}); \ + done ; ) + @touch .built + +context: + +.depend: Makefile $(SRCS) + @$(MKDEP) $(ROOTDEPPATH) $(CC) -- $(CFLAGS) -- $(SRCS) >Make.dep + @touch $@ + +depend: .depend + +clean: + @rm -f *.o *~ .*.swp .built + $(call CLEAN) + +distclean: clean + @rm -f Make.dep .depend + +-include Make.dep diff --git a/apps/netutils/json/README b/apps/netutils/json/README new file mode 100644 index 000000000..6d60da934 --- /dev/null +++ b/apps/netutils/json/README @@ -0,0 +1,258 @@ +apps/netutils/json/README.txt +============================= + +This directory contains logic taken from the cJSON project: + +http://sourceforge.net/projects/cjson/ + +This corresponds to SVN revision r42 (with lots of changes for NuttX coding +standards). As of r42, the SVN repository was last updated on 2011-10-10 +so I presume that the code is stable and there is no risk of maintaining +duplicate logic in the NuttX repository. + +Contents +======== + + o License + o Welcome to cJSON + +License +======= + + Copyright (c) 2009 Dave Gamble + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +Welcome to cJSON +================ + +cJSON aims to be the dumbest possible parser that you can get your job done with. +It's a single file of C, and a single header file. + +JSON is described best here: http://www.json.org/ +It's like XML, but fat-free. You use it to move data around, store things, or just +generally represent your program's state. + +First up, how do I build? +Add cJSON.c to your project, and put cJSON.h somewhere in the header search path. +For example, to build the test app: + +gcc cJSON.c test.c -o test -lm +./test + +As a library, cJSON exists to take away as much legwork as it can, but not get in your way. +As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it +in one of two modes: Auto and Manual. Let's have a quick run-through. + +I lifted some JSON from this page: http://www.json.org/fatfree.html +That page inspired me to write cJSON, which is a parser that tries to share the same +philosophy as JSON itself. Simple, dumb, out of the way. + +Some JSON: +{ + "name": "Jack (\"Bee\") Nimble", + "format": { + "type": "rect", + "width": 1920, + "height": 1080, + "interlace": false, + "frame rate": 24 + } +} + +Assume that you got this from a file, a webserver, or magic JSON elves, whatever, +you have a char * to it. Everything is a cJSON struct. +Get it parsed: + cJSON *root = cJSON_Parse(my_json_string); + +This is an object. We're in C. We don't have objects. But we do have structs. +What's the framerate? + + cJSON *format = cJSON_GetObjectItem(root,"format"); + int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint; + +Want to change the framerate? + cJSON_GetObjectItem(format,"frame rate")->valueint=25; + +Back to disk? + char *rendered=cJSON_Print(root); + +Finished? Delete the root (this takes care of everything else). + cJSON_Delete(root); + +That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers +before you dereference them. If you want to see how you'd build this struct in code? + cJSON *root,*fmt; + root=cJSON_CreateObject(); + cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble")); + cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject()); + cJSON_AddStringToObject(fmt,"type", "rect"); + cJSON_AddNumberToObject(fmt,"width", 1920); + cJSON_AddNumberToObject(fmt,"height", 1080); + cJSON_AddFalseToObject (fmt,"interlace"); + cJSON_AddNumberToObject(fmt,"frame rate", 24); + +Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup. +Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and +a few from elsewhere. + +What about manual mode? First up you need some detail. +Let's cover how the cJSON objects represent the JSON data. +cJSON doesn't distinguish arrays from objects in handling; just type. +Each cJSON has, potentially, a child, siblings, value, a name. + +The root object has: Object Type and a Child +The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling: +Sibling has type Object, name "format", and a child. +That child has type String, name "type", value "rect", and a sibling: +Sibling has type Number, name "width", value 1920, and a sibling: +Sibling has type Number, name "height", value 1080, and a sibling: +Sibling hs type False, name "interlace", and a sibling: +Sibling has type Number, name "frame rate", value 24 + +Here's the structure: +typedef struct cJSON { + struct cJSON *next,*prev; + struct cJSON *child; + + int type; + + char *valuestring; + int valueint; + double valuedouble; + + char *string; +} cJSON; + +By default all values are 0 unless set by virtue of being meaningful. + +next/prev is a doubly linked list of siblings. next takes you to your sibling, +prev takes you back from your sibling to you. +Only objects and arrays have a "child", and it's the head of the doubly linked list. +A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0. +The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in +cJSON.h + +A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read +valuedouble. + +Any entry which is in the linked list which is the child of an object will have a "string" +which is the "name" of the entry. When I said "name" in the above example, that's "string". +"string" is the JSON name for the 'variable name' if you will. + +Now you can trivially walk the lists, recursively, and parse as you please. +You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take +the root object, and traverse the structure (which is, formally, an N-tree), +and tokenise as you please. If you wanted to build a callback style parser, this is how +you'd do it (just an example, since these things are very specific): + +void parse_and_callback(cJSON *item,const char *prefix) +{ + while (item) + { + char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2); + sprintf(newprefix,"%s/%s",prefix,item->name); + int dorecurse=callback(newprefix, item->type, item); + if (item->child && dorecurse) parse_and_callback(item->child,newprefix); + item=item->next; + free(newprefix); + } +} + +The prefix process will build you a separated list, to simplify your callback handling. +The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or +let you invoke it per-item. For the item above, your callback might look like this: + +int callback(const char *name,int type,cJSON *item) +{ + if (!strcmp(name,"name")) { /* populate name */ } + else if (!strcmp(name,"format/type") { /* handle "rect" */ } + else if (!strcmp(name,"format/width") { /* 800 */ } + else if (!strcmp(name,"format/height") { /* 600 */ } + else if (!strcmp(name,"format/interlace") { /* false */ } + else if (!strcmp(name,"format/frame rate") { /* 24 */ } + return 1; +} + +Alternatively, you might like to parse iteratively. +You'd use: + +void parse_object(cJSON *item) +{ + int i; for (i=0;i<cJSON_GetArraySize(item);i++) + { + cJSON *subitem=cJSON_GetArrayItem(item,i); + // handle subitem. + } +} + +Or, for PROPER manual mode: + +void parse_object(cJSON *item) +{ + cJSON *subitem=item->child; + while (subitem) + { + // handle subitem + if (subitem->child) parse_object(subitem->child); + + subitem=subitem->next; + } +} + +Of course, this should look familiar, since this is just a stripped-down version +of the callback-parser. + +This should cover most uses you'll find for parsing. The rest should be possible +to infer.. and if in doubt, read the source! There's not a lot of it! ;) + +In terms of constructing JSON data, the example code above is the right way to do it. +You can, of course, hand your sub-objects to other functions to populate. +Also, if you find a use for it, you can manually build the objects. +For instance, suppose you wanted to build an array of objects? + +cJSON *objects[24]; + +cJSON *Create_array_of_anything(cJSON **items,int num) +{ + int i;cJSON *prev, *root=cJSON_CreateArray(); + for (i=0;i<24;i++) + { + if (!i) root->child=objects[i]; + else prev->next=objects[i], objects[i]->prev=prev; + prev=objects[i]; + } + return root; +} + +and simply: Create_array_of_anything(objects,24); + +cJSON doesn't make any assumptions about what order you create things in. +You can attach the objects, as above, and later add children to each +of those objects. + +As soon as you call cJSON_Print, it renders the structure to text. + +The test.c code shows how to handle a bunch of typical cases. If you uncomment +the code, it'll load, parse and print a bunch of test files, also from json.org, +which are more complex than I'd care to try and stash into a const char array[]. + +Enjoy cJSON! + +- Dave Gamble, Aug 2009 diff --git a/apps/netutils/json/cJSON.c b/apps/netutils/json/cJSON.c new file mode 100644 index 000000000..12eaab243 --- /dev/null +++ b/apps/netutils/json/cJSON.c @@ -0,0 +1,1576 @@ +/**************************************************************************** + * apps/netutils/json/cJSON.c + * + * This file is a part of NuttX: + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Ported by: Darcy Gong + * + * And derives from the cJSON Project which has an MIT license: + * + * Copyright (c) 2009 Dave Gamble + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <apps/math.h> +#include <stdlib.h> +#include <float.h> +#include <limits.h> +#include <ctype.h> +#include <unistd.h> + +#include <apps/netutils/cJSON.h> + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const char *ep; + +static const unsigned char firstByteMark[7] = + { 0x00, 0x00, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc }; + +static void *(*cJSON_malloc)(size_t sz) = malloc; +static void (*cJSON_free)(void *ptr) = free; + +/**************************************************************************** + * Private Prototypes + ****************************************************************************/ + +static const char *parse_value(cJSON *item, const char *value); +static char *print_value(cJSON *item, int depth, int fmt); +static const char *parse_array(cJSON *item, const char *value); +static char *print_array(cJSON *item, int depth, int fmt); +static const char *parse_object(cJSON *item, const char *value); +static char *print_object(cJSON *item, int depth, int fmt); + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static char *cJSON_strdup(const char *str) +{ + size_t len; + char *copy; + + len = strlen(str) + 1; + if (!(copy = (char *)cJSON_malloc(len))) + { + return 0; + } + + memcpy(copy, str, len); + return copy; +} + +/* Internal constructor. */ + +static cJSON *cJSON_New_Item(void) +{ + cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON)); + if (node) + { + memset(node, 0, sizeof(cJSON)); + } + + return node; +} + +static int cJSON_strcasecmp(const char *s1, const char *s2) +{ + if (!s1) + { + return (s1 == s2) ? 0 : 1; + } + + if (!s2) + { + return 1; + } + + for (; tolower(*s1) == tolower(*s2); ++s1, ++s2) + { + if (*s1 == 0) + { + return 0; + } + } + + return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); +} + +/* Parse the input text to generate a number, and populate the result into item. */ + +static const char *parse_number(cJSON *item, const char *num) +{ + double n = 0, sign = 1, scale = 0; + int subscale = 0, signsubscale = 1; + + /* Could use sscanf for this? */ + /* Has sign? */ + + if (*num == '-') + { + sign = -1, num++; + } + + /* is zero */ + + if (*num == '0') + { + num++; + } + + /* Number? */ + + if (*num >= '1' && *num <= '9') + { + do + { + n = (n * 10.0) + (*num++ - '0'); + } + while (*num >= '0' && *num <= '9'); + } + + /* Fractional part? */ + + if (*num == '.' && num[1] >= '0' && num[1] <= '9') + { + num++; + do + { + n = (n * 10.0) + (*num++ - '0'), scale--; + } + while (*num >= '0' && *num <= '9'); + } + + /* Exponent? */ + + if (*num == 'e' || *num == 'E') + { + num++; + if (*num == '+') + { + num++; + } + + /* With sign? */ + + else if (*num == '-') + { + signsubscale = -1; + num++; + } + + /* Number? */ + + while (*num >= '0' && *num <= '9') + { + subscale = (subscale * 10) + (*num++ - '0'); + } + } + + /* number = +/- number.fraction * 10^+/-exponent */ + + n = sign * n * pow(10.0, (scale + subscale * signsubscale)); + item->valuedouble = n; + item->valueint = (int)n; + item->type = cJSON_Number; + return num; +} + +/* Render the number nicely from the given item into a string. */ + +static char *print_number(cJSON *item) +{ + char *str; + double d = item->valuedouble; + + if (fabs(((double)item->valueint) - d) <= DBL_EPSILON) /* && d<=INT_MAX && d>=INT_MIN) */ + { + /* 2^64+1 can be represented in 21 chars. */ + + str = (char *)cJSON_malloc(21); + if (str) + { + sprintf(str, "%d", item->valueint); + } + } + else + { + /* This is a nice tradeoff. */ + + str = (char *)cJSON_malloc(64); + if (str) + { + if (fabs(floor(d) - d) <= DBL_EPSILON) + { + sprintf(str, "%d", item->valueint); + } + else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9) + { + sprintf(str, "%e", d); + } + else + { + sprintf(str, " %f", d); + } + } + } + + return str; +} + +/* Parse the input text into an unescaped cstring, and populate item. */ + +static const char *parse_string(cJSON *item, const char *str) +{ + const char *ptr = str + 1; + char *ptr2; + char *out; + int len = 0; + unsigned uc; + unsigned uc2; + + if (*str != '\"') + { + /* not a string! */ + + ep = str; + return 0; + } + + while (*ptr != '\"' && *ptr && ++len) + { + /* Skip escaped quotes. */ + + if (*ptr++ == '\\') + { + ptr++; + } + } + + /* This is how long we need for the string, roughly. */ + + out = (char *)cJSON_malloc(len + 1); + if (!out) + { + return 0; + } + + ptr = str + 1; + ptr2 = out; + while (*ptr != '\"' && *ptr) + { + if (*ptr != '\\') + { + *ptr2++ = *ptr++; + } + else + { + ptr++; + switch (*ptr) + { + case 'b': + *ptr2++ = '\b'; + break; + + case 'f': + *ptr2++ = '\f'; + break; + + case 'n': + *ptr2++ = '\n'; + break; + + case 'r': + *ptr2++ = '\r'; + break; + + case 't': + *ptr2++ = '\t'; + break; + + case 'u': + /* Transcode utf16 to utf8. */ + /* Get the unicode char. */ + + sscanf(ptr + 1, "%4x", &uc); + ptr += 4; + + /* Check for invalid. */ + + if ((uc >= 0xdc00 && uc <= 0xdfff) || uc == 0) + { + break; + } + + if (uc >= 0xd800 && uc <= 0xdbff) /* UTF16 surrogate pairs. */ + { + /* missing second-half of surrogate. */ + + if (ptr[1] != '\\' || ptr[2] != 'u') + { + break; + } + + sscanf(ptr + 3, "%4x", &uc2); + ptr += 6; + if (uc2 < 0xdc00 || uc2 > 0xdfff) + { + /* Invalid second-half of surrogate. */ + + break; + } + + uc = 0x10000 | ((uc & 0x3ff) << 10) | (uc2 & 0x3ff); + } + + len = 4; + if (uc < 0x80) + { + len = 1; + } + else if (uc < 0x800) + { + len = 2; + } + else if (uc < 0x10000) + { + len = 3; + } + + ptr2 += len; + + switch (len) + { + case 4: + *--ptr2 = ((uc | 0x80) & 0xbf); + uc >>= 6; + case 3: + *--ptr2 = ((uc | 0x80) & 0xbf); + uc >>= 6; + case 2: + *--ptr2 = ((uc | 0x80) & 0xbf); + uc >>= 6; + case 1: + *--ptr2 = (uc | firstByteMark[len]); + break; + } + + ptr2 += len; + break; + + default: + *ptr2++ = *ptr; + break; + } + ptr++; + } + } + + *ptr2 = 0; + if (*ptr == '\"') + { + ptr++; + } + + item->valuestring = out; + item->type = cJSON_String; + return ptr; +} + +/* Render the cstring provided to an escaped version that can be printed. */ + +static char *print_string_ptr(const char *str) +{ + const char *ptr; + char *ptr2, *out; + int len = 0; + unsigned char token; + + if (!str) + { + return cJSON_strdup(""); + } + + ptr = str; + while ((token = *ptr) && ++len) + { + if (strchr("\"\\\b\f\n\r\t", token)) + { + len++; + } + else if (token < 32) + { + len += 5; + } + + ptr++; + } + + out = (char *)cJSON_malloc(len + 3); + if (!out) + { + return 0; + } + + ptr2 = out; + ptr = str; + *ptr2++ = '\"'; + while (*ptr) + { + if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\') + { + *ptr2++ = *ptr++; + } + else + { + *ptr2++ = '\\'; + switch (token = *ptr++) + { + case '\\': + *ptr2++ = '\\'; + break; + + case '\"': + *ptr2++ = '\"'; + break; + + case '\b': + *ptr2++ = 'b'; + break; + + case '\f': + *ptr2++ = 'f'; + break; + + case '\n': + *ptr2++ = 'n'; + break; + + case '\r': + *ptr2++ = 'r'; + break; + + case '\t': + *ptr2++ = 't'; + break; + + default: + /* Escape and print */ + + sprintf(ptr2, "u%04x", token); + ptr2 += 5; + break; + } + } + } + + *ptr2++ = '\"'; + *ptr2++ = 0; + return out; +} + +/* Invote print_string_ptr (which is useful) on an item. */ + +static char *print_string(cJSON *item) +{ + return print_string_ptr(item->valuestring); +} + +/* Utility to jump whitespace and cr/lf */ + +static const char *skip(const char *in) +{ + while (in && *in && (unsigned char)*in <= 32) + { + in++; + } + + return in; +} + +/* Parser core - when encountering text, process appropriately. */ + +static const char *parse_value(cJSON *item, const char *value) +{ + if (!value) + { + /* Fail on null. */ + + return 0; + } + + if (!strncmp(value, "null", 4)) + { + item->type = cJSON_NULL; + return value + 4; + } + + if (!strncmp(value, "false", 5)) + { + item->type = cJSON_False; + return value + 5; + } + + if (!strncmp(value, "true", 4)) + { + item->type = cJSON_True; + item->valueint = 1; + return value + 4; + } + + if (*value == '\"') + { + return parse_string(item, value); + } + + if (*value == '-' || (*value >= '0' && *value <= '9')) + { + return parse_number(item, value); + } + + if (*value == '[') + { + return parse_array(item, value); + } + + if (*value == '{') + { + return parse_object(item, value); + } + + /* Failure. */ + + ep = value; + return 0; +} + +/* Render a value to text. */ + +static char *print_value(cJSON *item, int depth, int fmt) +{ + char *out = 0; + if (!item) + { + return 0; + } + + switch ((item->type) & 255) + { + case cJSON_NULL: + out = cJSON_strdup("null"); + break; + + case cJSON_False: + out = cJSON_strdup("false"); + break; + + case cJSON_True: + out = cJSON_strdup("true"); + break; + + case cJSON_Number: + out = print_number(item); + break; + + case cJSON_String: + out = print_string(item); + break; + + case cJSON_Array: + out = print_array(item, depth, fmt); + break; + + case cJSON_Object: + out = print_object(item, depth, fmt); + break; + } + + return out; +} + +/* Build an array from input text. */ + +static const char *parse_array(cJSON *item, const char *value) +{ + cJSON *child; + + if (*value != '[') + { + /* not an array! */ + + ep = value; + return 0; + } + + item->type = cJSON_Array; + value = skip(value + 1); + if (*value == ']') + { + /* Empty array. */ + + return value + 1; + } + + item->child = child = cJSON_New_Item(); + if (!item->child) + { + /* Memory fail */ + + return 0; + } + + /* Skip any spacing, get the value. */ + + value = skip(parse_value(child, skip(value))); + if (!value) + { + return 0; + } + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + { + /* <emory fail */ + + return 0; + } + + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_value(child, skip(value + 1))); + if (!value) + { + /* Memory fail */ + + return 0; + } + } + + if (*value == ']') + { + /* End of array */ + + return value + 1; + } + + /* Malformed */ + + ep = value; + return 0; +} + +/* Render an array to text */ + +static char *print_array(cJSON *item, int depth, int fmt) +{ + char **entries; + char *out = 0; + char *ptr; + char *ret; + int len = 5; + cJSON *child = item->child; + int numentries = 0; + int i = 0; + int fail = 0; + + /* How many entries in the array? */ + + while (child) + { + numentries++, child = child->next; + } + + /* Allocate an array to hold the values for each */ + + entries = (char **)cJSON_malloc(numentries * sizeof(char *)); + if (!entries) + { + return 0; + } + + memset(entries, 0, numentries * sizeof(char *)); + + /* Retrieve all the results: */ + + child = item->child; + while (child && !fail) + { + ret = print_value(child, depth + 1, fmt); + entries[i++] = ret; + if (ret) + { + len += strlen(ret) + 2 + (fmt ? 1 : 0); + } + else + { + fail = 1; + } + + child = child->next; + } + + /* If we didn't fail, try to malloc the output string */ + + if (!fail) + { + out = (char *)cJSON_malloc(len); + } + + /* If that fails, we fail. */ + + if (!out) + { + fail = 1; + } + + /* Handle failure. */ + + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (entries[i]) + { + cJSON_free(entries[i]); + } + } + + cJSON_free(entries); + return 0; + } + + /* Compose the output array. */ + + *out = '['; + ptr = out + 1; + *ptr = 0; + for (i = 0; i < numentries; i++) + { + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + if (fmt) + { + *ptr++ = ' '; + } + + *ptr = 0; + } + cJSON_free(entries[i]); + } + + cJSON_free(entries); + *ptr++ = ']'; + *ptr++ = 0; + return out; +} + +/* Build an object from the text. */ + +static const char *parse_object(cJSON *item, const char *value) +{ + cJSON *child; + if (*value != '{') + { + /* Not an object! */ + + ep = value; + return 0; + } + + item->type = cJSON_Object; + value = skip(value + 1); + if (*value == '}') + { + /* Empty array. */ + + return value + 1; + } + + item->child = child = cJSON_New_Item(); + if (!item->child) + { + return 0; + } + + value = skip(parse_string(child, skip(value))); + if (!value) + { + return 0; + } + + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } + + /* Skip any spacing, get the value. */ + + value = skip(parse_value(child, skip(value + 1))); + if (!value) + { + return 0; + } + + while (*value == ',') + { + cJSON *new_item; + if (!(new_item = cJSON_New_Item())) + { + /* Memory fail */ + + return 0; + } + + child->next = new_item; + new_item->prev = child; + child = new_item; + value = skip(parse_string(child, skip(value + 1))); + if (!value) + { + return 0; + } + + child->string = child->valuestring; + child->valuestring = 0; + if (*value != ':') + { + ep = value; + return 0; + } + + /* Skip any spacing, get the value. */ + + value = skip(parse_value(child, skip(value + 1))); + if (!value) + { + return 0; + } + } + + if (*value == '}') + { + /* End of array */ + + return value + 1; + } + + /* Malformed */ + + ep = value; + return 0; +} + +/* Render an object to text. */ + +static char *print_object(cJSON *item, int depth, int fmt) +{ + char **entries = 0; + char **names = 0; + char *out = 0; + char *ptr; + char *ret; + char *str; + int len = 7; + int i = 0; + int j; + cJSON *child = item->child; + int numentries = 0; + int fail = 0; + + /* Count the number of entries. */ + + while (child) + { + numentries++, child = child->next; + } + + /* Allocate space for the names and the objects */ + + entries = (char **)cJSON_malloc(numentries * sizeof(char *)); + if (!entries) + { + return 0; + } + + names = (char **)cJSON_malloc(numentries * sizeof(char *)); + if (!names) + { + cJSON_free(entries); + return 0; + } + + memset(entries, 0, sizeof(char *) * numentries); + memset(names, 0, sizeof(char *) * numentries); + + /* Collect all the results into our arrays: */ + + child = item->child; + depth++; + if (fmt) + { + len += depth; + } + + while (child) + { + names[i] = str = print_string_ptr(child->string); + entries[i++] = ret = print_value(child, depth, fmt); + if (str && ret) + { + len += strlen(ret) + strlen(str) + 2 + (fmt ? 2 + depth : 0); + } + else + { + fail = 1; + } + + child = child->next; + } + + /* Try to allocate the output string */ + + if (!fail) + { + out = (char *)cJSON_malloc(len); + } + + if (!out) + { + fail = 1; + } + + /* Handle failure */ + + if (fail) + { + for (i = 0; i < numentries; i++) + { + if (names[i]) + { + cJSON_free(names[i]); + } + + if (entries[i]) + { + cJSON_free(entries[i]); + } + } + + cJSON_free(names); + cJSON_free(entries); + return 0; + } + + /* Compose the output: */ + + *out = '{'; + ptr = out + 1; + if (fmt) + { + *ptr++ = '\n'; + } + *ptr = 0; + + for (i = 0; i < numentries; i++) + { + if (fmt) + { + for (j = 0; j < depth; j++) + { + *ptr++ = '\t'; + } + } + + strcpy(ptr, names[i]); + ptr += strlen(names[i]); + *ptr++ = ':'; + if (fmt) + { + *ptr++ = '\t'; + } + + strcpy(ptr, entries[i]); + ptr += strlen(entries[i]); + if (i != numentries - 1) + { + *ptr++ = ','; + } + + if (fmt) + { + *ptr++ = '\n'; + } + + *ptr = 0; + cJSON_free(names[i]); + cJSON_free(entries[i]); + } + + cJSON_free(names); + cJSON_free(entries); + if (fmt) + { + for (i = 0; i < depth - 1; i++) + { + *ptr++ = '\t'; + } + } + + *ptr++ = '}'; + *ptr++ = 0; + return out; +} + +/* Utility for array list handling. */ + +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ + +static cJSON *create_reference(cJSON *item) +{ + cJSON *ref = cJSON_New_Item(); + if (!ref) + { + return 0; + } + + memcpy(ref, item, sizeof(cJSON)); + ref->string = 0; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = 0; + return ref; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +const char *cJSON_GetErrorPtr(void) +{ + return ep; +} + +void cJSON_InitHooks(cJSON_Hooks *hooks) +{ + if (!hooks) + { + /* Reset hooks */ + + cJSON_malloc = malloc; + cJSON_free = free; + return; + } + + cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc; + cJSON_free = (hooks->free_fn) ? hooks->free_fn : free; +} + +/* Delete a cJSON structure. */ + +void cJSON_Delete(cJSON *c) +{ + cJSON *next; + while (c) + { + next = c->next; + if (!(c->type & cJSON_IsReference) && c->child) + { + cJSON_Delete(c->child); + } + + if (!(c->type & cJSON_IsReference) && c->valuestring) + { + cJSON_free(c->valuestring); + } + + if (c->string) + { + cJSON_free(c->string); + } + + cJSON_free(c); + c = next; + } +} + +/* Parse an object - create a new root, and populate. */ + +cJSON *cJSON_Parse(const char *value) +{ + cJSON *c = cJSON_New_Item(); + ep = 0; + if (!c) + { + /* Memory fail */ + + return 0; + } + + if (!parse_value(c, skip(value))) + { + cJSON_Delete(c); + return 0; + } + + return c; +} + +/* Render a cJSON item/entity/structure to text. */ + +char *cJSON_Print(cJSON *item) +{ + return print_value(item, 0, 1); +} + +char *cJSON_PrintUnformatted(cJSON *item) +{ + return print_value(item, 0, 0); +} + +/* Get Array size/item / object item. */ + +int cJSON_GetArraySize(cJSON *array) +{ + cJSON *c = array->child; + int i = 0; + + while (c) + { + i++; + c = c->next; + } + + return i; +} + +cJSON *cJSON_GetArrayItem(cJSON *array, int item) +{ + cJSON *c = array->child; + + while (c && item > 0) + { + item--; + c = c->next; + } + + return c; +} + +cJSON *cJSON_GetObjectItem(cJSON *object, const char *string) +{ + cJSON *c = object->child; + + while (c && cJSON_strcasecmp(c->string, string)) + { + c = c->next; + } + + return c; +} + +/* Add item to array/object. */ +void cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *c = array->child; + + if (!item) + { + return; + } + + if (!c) + { + array->child = item; + } + else + { + while (c && c->next) + { + c = c->next; + } + + suffix_object(c, item); + } +} + +void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + { + return; + } + + if (item->string) + { + cJSON_free(item->string); + } + + item->string = cJSON_strdup(string); + cJSON_AddItemToArray(object, item); +} + +void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item)); +} + +void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item)); +} + +cJSON *cJSON_DetachItemFromArray(cJSON *array, int which) +{ + cJSON *c = array->child; + + while (c && which > 0) + { + c = c->next, which--; + } + + if (!c) + { + return 0; + } + + if (c->prev) + { + c->prev->next = c->next; + } + + if (c->next) + { + c->next->prev = c->prev; + } + + if (c == array->child) + { + array->child = c->next; + } + + c->prev = c->next = 0; + return c; +} + +void cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + int i = 0; + cJSON *c = object->child; + + while (c && cJSON_strcasecmp(c->string, string)) + { + i++; + c = c->next; + } + + if (c) + { + return cJSON_DetachItemFromArray(object, i); + } + + return 0; +} + +void cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +/* Replace array/object items with new ones. */ + +void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *c = array->child; + + while (c && which > 0) + { + c = c->next, which--; + } + + if (!c) + { + return; + } + + newitem->next = c->next; + newitem->prev = c->prev; + if (newitem->next) + { + newitem->next->prev = newitem; + } + + if (c == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + + c->next = c->prev = 0; + cJSON_Delete(c); +} + +void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + int i = 0; + cJSON *c = object->child; + + while (c && cJSON_strcasecmp(c->string, string)) + { + i++; + c = c->next; + } + + if (c) + { + newitem->string = cJSON_strdup(string); + cJSON_ReplaceItemInArray(object, i, newitem); + } +} + +/* Create basic types: */ + +cJSON *cJSON_CreateNull() +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_NULL; + } + + return item; +} + +cJSON *cJSON_CreateTrue() +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_True; + } + + return item; +} + +cJSON *cJSON_CreateFalse() +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_False; + } + + return item; +} + +cJSON *cJSON_CreateBool(int b) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +cJSON *cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Number; + item->valuedouble = num; + item->valueint = (int)num; + } + + return item; +} + +cJSON *cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_String; + item->valuestring = cJSON_strdup(string); + } + + return item; +} + +cJSON *cJSON_CreateArray() +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Array; + } + + return item; +} + +cJSON *cJSON_CreateObject() +{ + cJSON *item = cJSON_New_Item(); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ + +cJSON *cJSON_CreateIntArray(const int *numbers, int count) +{ + cJSON *n = 0; + cJSON *p = 0; + cJSON *a = cJSON_CreateArray(); + int i; + + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + + p = n; + } + + return a; +} + +cJSON *cJSON_CreateFloatArray(const float *numbers, int count) +{ + cJSON *n = 0; + cJSON *p = 0; + cJSON *a = cJSON_CreateArray(); + int i; + + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + + p = n; + } + + return a; +} + +cJSON *cJSON_CreateDoubleArray(const double *numbers, int count) +{ + cJSON *n = 0; + cJSON *p = 0; + cJSON *a = cJSON_CreateArray(); + int i; + + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + + p = n; + } + + return a; +} + +cJSON *cJSON_CreateStringArray(const char **strings, int count) +{ + cJSON *n = 0; + cJSON *p = 0; + cJSON *a = cJSON_CreateArray(); + int i; + + for (i = 0; a && i < count; i++) + { + n = cJSON_CreateString(strings[i]); + if (!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + + p = n; + } + + return a; +} |