added uncommitted new JSON files to branch
authorerickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 1 Jun 2007 18:27:29 +0000 (18:27 +0000)
committererickson <erickson@9efc2488-bf62-4759-914b-345cdb29e865>
Fri, 1 Jun 2007 18:27:29 +0000 (18:27 +0000)
git-svn-id: svn://svn.open-ils.org/OpenSRF/branches/new-json@931 9efc2488-bf62-4759-914b-345cdb29e865

src/libstack/Makefile.JSON [new file with mode: 0644]
src/libstack/legacy_json.c [new file with mode: 0644]
src/libstack/legacy_json.h [new file with mode: 0644]
src/libstack/osrf_json.h [new file with mode: 0644]
src/libstack/osrf_json_object.c [new file with mode: 0644]
src/libstack/osrf_json_parser.c [new file with mode: 0644]
src/libstack/osrf_json_tools.c [new file with mode: 0644]
src/libstack/osrf_json_utils.h [new file with mode: 0644]

diff --git a/src/libstack/Makefile.JSON b/src/libstack/Makefile.JSON
new file mode 100644 (file)
index 0000000..da15e1f
--- /dev/null
@@ -0,0 +1,46 @@
+# --------------------------------------------------------------
+# This makefile creates a libosrfjson.so library for a standlone
+# JSON parser.
+#
+# usage: make -f Makefile.JSON
+#
+# --------------------------------------------------------------
+
+#PG = -pg # enable profiling support
+#G = -g  # enable debugging
+CFLAGS += $(G) -Wall $(PG) -O2 -fPIC -DJSON_IGNORE_COMMENTS -I ../utils/
+LDLIBS         += 
+LDFLAGS        += -L .  $(PG)
+
+TARGETS =      osrf_hash.o \
+                       osrf_list.o \
+                       osrf_json_parser.o \
+                       osrf_json_object.o \
+                       osrf_json_tools.o \
+                       ../utils/utils.o \
+                       ../utils/md5.o \
+                       ../utils/string_array.o \
+                       ../utils/log.o \
+
+
+all:   libosrfjson.so
+
+osrf_hash.o:   osrf_hash.c osrf_hash.h
+osrf_list.o:   osrf_list.c osrf_list.h
+osrf_json_parser.o:    osrf_json_parser.c osrf_json.h osrf_json_utils.h
+osrf_json_object.o:    osrf_json_object.c osrf_json.h osrf_json_utils.h
+osrf_json_tools.o:     osrf_json_tools.c osrf_json.h osrf_json_utils.h
+../utils/utils.o:      ../utils/utils.c ../utils/utils.h
+../utils/md5.o:        ../utils/md5.c ../utils/md5.h
+../utils/string_array.o:       ../utils/string_array.c ../utils/string_array.h
+../utils/log.o:        ../utils/log.c ../utils/log.h
+
+
+
+libosrfjson.so:        $(TARGETS)      
+       $(CC) -shared -W1 $(LDFLAGS) $(ARGETS) -o $@
+
+clean: 
+       rm -f $(TARGETS) libosrfjson.so
+
+
diff --git a/src/libstack/legacy_json.c b/src/libstack/legacy_json.c
new file mode 100644 (file)
index 0000000..516c282
--- /dev/null
@@ -0,0 +1,851 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+
+#include "legacy_json.h"
+
+/* keep a copy of the length of the current json string so we don't 
+ * have to calculate it in each function
+ */
+int current_strlen; 
+
+
+jsonObject* legacy_jsonParseString( char* string) {
+       return json_parse_string( string );
+}
+
+jsonObject* legacy_jsonParseStringFmt( char* string, ... ) {
+       VA_LIST_TO_STRING(string);
+       return json_parse_string( VA_BUF );
+}
+
+
+jsonObject* json_parse_string(char* string) {
+
+       if(string == NULL) return NULL;
+
+       current_strlen = strlen(string);
+
+       if(current_strlen == 0) 
+               return NULL;
+
+       unsigned long index = 0;
+
+       json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
+       if(index == current_strlen) return NULL;
+
+       jsonObject* obj = jsonNewObject(NULL);
+
+       int status = _json_parse_string(string, &index, obj, current_strlen);
+       if(!status) return obj;
+
+       if(status == -2) {
+               jsonObjectFree(obj);
+               return NULL;
+       }
+
+       return NULL;
+}
+
+
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( !string || !index || *index >= current_strlen) return -2;
+
+       int status = 0; /* return code from parsing routines */
+       char* classname = NULL; /* object class hint */
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       char c = string[*index];
+
+       /* remove any leading comments */
+       if( c == '/' ) { 
+
+               while(1) {
+                       (*index)++; /* move to second comment char */
+                       status = json_eat_comment(string, index, &classname, 1, current_strlen);
+                       if(status) return status;
+
+                       json_eat_ws(string, index, 1, current_strlen);
+                       c = string[*index];
+                       if(c != '/')
+                               break;
+               }
+       }
+
+       json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
+
+       if(*index >= current_strlen)
+               return -2;
+
+       switch(c) {
+                               
+               /* json string */
+               case '"': 
+                       (*index)++;
+                       status = json_parse_json_string(string, index, obj, current_strlen); break;
+
+               /* json array */
+               case '[':
+                       (*index)++;
+                       status = json_parse_json_array(string, index, obj, current_strlen);                     
+                       break;
+
+               /* json object */
+               case '{':
+                       (*index)++;
+                       status = json_parse_json_object(string, index, obj, current_strlen);
+                       break;
+
+               /* NULL */
+               case 'n':
+               case 'N':
+                       status = json_parse_json_null(string, index, obj, current_strlen);
+                       break;
+                       
+
+               /* true, false */
+               case 'f':
+               case 'F':
+               case 't':
+               case 'T':
+                       status = json_parse_json_bool(string, index, obj, current_strlen);
+                       break;
+
+               default:
+                       if(is_number(c) || c == '.' || c == '-') { /* are we a number? */
+                               status = json_parse_json_number(string, index, obj, current_strlen);    
+                               if(status) return status;
+                               break;
+                       }
+
+                       (*index)--;
+                       /* we should never get here */
+                       return json_handle_error(string, index, "_json_parse_string() final switch clause");
+       }       
+
+       if(status) return status;
+
+       json_eat_ws(string, index, 1, current_strlen);
+
+       if( *index < current_strlen ) {
+               /* remove any trailing comments */
+               c = string[*index];
+               if( c == '/' ) { 
+                       (*index)++;
+                       status = json_eat_comment(string, index, NULL, 0, current_strlen);
+                       if(status) return status;
+               }
+       }
+
+       if(classname){
+               jsonObjectSetClass(obj, classname);
+               free(classname);
+       }
+
+       return 0;
+}
+
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if(*index >= (current_strlen - 3)) {
+               return json_handle_error(string, index, 
+                       "_parse_json_null(): invalid null" );
+       }
+
+       if(!strncasecmp(string + (*index), "null", 4)) {
+               (*index) += 4;
+               obj->type = JSON_NULL;
+               return 0;
+       } else {
+               return json_handle_error(string, index,
+                       "_parse_json_null(): invalid null" );
+       }
+}
+
+/* should be at the first character of the bool at this point */
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       char* ret = "json_parse_json_bool(): truncated bool";
+
+       if( *index >= (current_strlen - 5))
+               return json_handle_error(string, index, ret);
+       
+       if(!strncasecmp( string + (*index), "false", 5)) {
+               (*index) += 5;
+               obj->value.b = 0;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       if( *index >= (current_strlen - 4))
+               return json_handle_error(string, index, ret);
+
+       if(!strncasecmp( string + (*index), "true", 4)) {
+               (*index) += 4;
+               obj->value.b = 1;
+               obj->type = JSON_BOOL;
+               return 0;
+       }
+
+       return json_handle_error(string, index, ret);
+}
+
+
+/* expecting the first character of the number */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! obj || *index >= current_strlen ) return -1;
+
+       growing_buffer* buf = buffer_init(64);
+       char c = string[*index];
+
+       int done = 0;
+       int dot_seen = 0;
+
+       /* negative number? */
+       if(c == '-') { buffer_add(buf, "-"); (*index)++; }
+
+       c = string[*index];
+
+       while(*index < current_strlen) {
+
+               if(is_number(c)) {
+                       buffer_add_char(buf, c);
+               }
+
+               else if( c == '.' ) {
+                       if(dot_seen) {
+                               buffer_free(buf);
+                               return json_handle_error(string, index, 
+                                       "json_parse_json_number(): malformed json number");
+                       }
+                       dot_seen = 1;
+                       buffer_add_char(buf, c);
+               } else {
+                       done = 1; break;
+               }
+
+               (*index)++;
+               c = string[*index];
+               if(done) break;
+       }
+
+       obj->type = JSON_NUMBER;
+       obj->value.n = strtod(buf->buf, NULL);
+       buffer_free(buf);
+       return 0;
+}
+
+/* index should point to the character directly following the '['.  when done
+ * index will point to the character directly following the ']' character
+ */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+
+       if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
+
+       int status = 0;
+       int in_parse = 0; /* true if this array already contains one item */
+       obj->type = JSON_ARRAY;
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == ']') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       json_eat_ws(string, index, 1, current_strlen);
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_array(): array item not followed by a ','");
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               jsonObject* item = jsonNewObject(NULL);
+
+               #ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == ']') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set) status = _json_parse_string(string, index, item, current_strlen);
+
+               #else
+               status = _json_parse_string(string, index, item, current_strlen);
+               #endif
+
+               if(status) { jsonObjectFree(item); return status; }
+               jsonObjectPush(obj, item);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_array(): array not closed");
+
+       return 0;
+}
+
+
+/* index should point to the character directly following the '{'.  when done
+ * index will point to the character directly following the '}'
+ */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
+
+       obj->type = JSON_HASH;
+       int status;
+       int in_parse = 0; /* true if we've already added one item to this object */
+       int set = 0;
+       int done = 0;
+
+       while(*index < current_strlen) {
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] == '}') {
+                       (*index)++;
+                       done = 1;
+                       break;
+               }
+
+               if(in_parse) {
+                       if(string[*index] != ',') {
+                               return json_handle_error(string, index,
+                                       "json_parse_json_object(): object missing ',' between elements" );
+                       }
+                       (*index)++;
+                       json_eat_ws(string, index, 1, current_strlen);
+               }
+
+               /* first we grab the hash key */
+               jsonObject* key_obj = jsonNewObject(NULL);
+               status = _json_parse_string(string, index, key_obj, current_strlen);
+               if(status) return status;
+
+               if(key_obj->type != JSON_STRING) {
+                       return json_handle_error(string, index, 
+                               "_json_parse_json_object(): hash key not a string");
+               }
+
+               char* key = key_obj->value.s;
+
+               json_eat_ws(string, index, 1, current_strlen);
+
+               if(string[*index] != ':') {
+                       return json_handle_error(string, index, 
+                               "json_parse_json_object(): hash key not followed by ':' character");
+               }
+
+               (*index)++;
+
+               /* now grab the value object */
+               json_eat_ws(string, index, 1, current_strlen);
+               jsonObject* value_obj = jsonNewObject(NULL);
+
+#ifndef STRICT_JSON_READ
+               if(*index < current_strlen) {
+                       if(string[*index] == ',' || string[*index] == '}') {
+                               status = 0;
+                               set = 1;
+                       }
+               }
+               if(!set)
+                       status = _json_parse_string(string, index, value_obj, current_strlen);
+
+#else
+                status = _json_parse_string(string, index, value_obj, current_strlen);
+#endif
+
+               if(status) return status;
+
+               /* put the data into the object and continue */
+               jsonObjectSetKey(obj, key, value_obj);
+               jsonObjectFree(key_obj);
+               in_parse = 1;
+               set = 0;
+       }
+
+       if(!done)
+               return json_handle_error(string, index,
+                       "json_parse_json_object(): object not closed");
+
+       return 0;
+}
+
+
+
+/* when done, index will point to the character after the closing quote */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+
+       int in_escape = 0;      
+       int done = 0;
+       growing_buffer* buf = buffer_init(64);
+
+       while(*index < current_strlen) {
+
+               char c = string[*index]; 
+
+               switch(c) {
+
+                       case '\\':
+                               if(in_escape) {
+                                       buffer_add(buf, "\\");
+                                       in_escape = 0;
+                               } else 
+                                       in_escape = 1;
+                               break;
+
+                       case '"':
+                               if(in_escape) {
+                                       buffer_add(buf, "\"");
+                                       in_escape = 0;
+                               } else 
+                                       done = 1;
+                               break;
+
+                       case 't':
+                               if(in_escape) {
+                                       buffer_add(buf,"\t");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'b':
+                               if(in_escape) {
+                                       buffer_add(buf,"\b");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'f':
+                               if(in_escape) {
+                                       buffer_add(buf,"\f");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'r':
+                               if(in_escape) {
+                                       buffer_add(buf,"\r");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'n':
+                               if(in_escape) {
+                                       buffer_add(buf,"\n");
+                                       in_escape = 0;
+                               } else 
+                                       buffer_add_char(buf, c);
+                               break;
+
+                       case 'u':
+                               if(in_escape) {
+                                       (*index)++;
+
+                                       if(*index >= (current_strlen - 4)) {
+                                               buffer_free(buf);
+                                               return json_handle_error(string, index,
+                                                       "json_parse_json_string(): truncated escaped unicode"); }
+
+                                       char buff[5];
+                                       memset(buff,0,5);
+                                       memcpy(buff, string + (*index), 4);
+
+
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+                                       /* The following chunk was borrowed with permission from 
+                                               json-c http://oss.metaparadigm.com/json-c/ */
+                                       unsigned char utf_out[3];
+                                       memset(utf_out,0,3);
+
+                                       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+
+                                       unsigned int ucs_char =
+                                               (hexdigit(string[*index] ) << 12) +
+                                               (hexdigit(string[*index + 1]) << 8) +
+                                               (hexdigit(string[*index + 2]) << 4) +
+                                               hexdigit(string[*index + 3]);
+       
+                                       if (ucs_char < 0x80) {
+                                               utf_out[0] = ucs_char;
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else if (ucs_char < 0x800) {
+                                               utf_out[0] = 0xc0 | (ucs_char >> 6);
+                                               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+
+                                       } else {
+                                               utf_out[0] = 0xe0 | (ucs_char >> 12);
+                                               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+                                               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+                                               buffer_add(buf, (char*) utf_out);
+                                       }
+                                       /* ----------------------------------------------------------------------- */
+                                       /* ----------------------------------------------------------------------- */
+
+                                       (*index) += 3;
+                                       in_escape = 0;
+
+                               } else {
+
+                                       buffer_add_char(buf, c);
+                               }
+
+                               break;
+
+                       default:
+                               buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       jsonObjectSetString(obj, buf->buf);
+       buffer_free(buf);
+       return 0;
+}
+
+
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
+       if( ! string || ! index ) return;
+       if(*index >= current_strlen)
+               return;
+
+       if( eat_all ) { /* removes newlines, etc */
+               while(string[*index] == ' '     || 
+                               string[*index] == '\n'  ||
+                               string[*index] == '\t') 
+                       (*index)++;
+       }
+
+       else    
+               while(string[*index] == ' ') (*index)++;
+}
+
+
+/* index should be at the '*' character at the beginning of the comment.
+ * when done, index will point to the first character after the final /
+ */
+int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
+       if( ! string || ! index || *index >= current_strlen ) return -1;
+       
+
+       if(string[*index] != '*' && string[*index] != '/' )
+               return json_handle_error(string, index, 
+                       "json_eat_comment(): invalid character after /");
+
+       /* chop out any // style comments */
+       if(string[*index] == '/') {
+               (*index)++;
+               char c = string[*index];
+               while(*index < current_strlen) {
+                       (*index)++;
+                       if(c == '\n') 
+                               return 0;
+                       c = string[*index];
+               }
+               return 0;
+       }
+
+       (*index)++;
+
+       int on_star                     = 0; /* true if we just saw a '*' character */
+
+       /* we're just past the '*' */
+       if(!parse_class) { /* we're not concerned with class hints */
+               while(*index < current_strlen) {
+                       if(string[*index] == '/') {
+                               if(on_star) {
+                                       (*index)++;
+                                       return 0;
+                               }
+                       }
+
+                       if(string[*index] == '*') on_star = 1;
+                       else on_star = 0;
+
+                       (*index)++;
+               }
+               return 0;
+       }
+
+
+
+       growing_buffer* buf = buffer_init(64);
+
+       int first_dash          = 0;
+       int second_dash = 0;
+       int third_dash          = 0;
+       int fourth_dash = 0;
+
+       int in_hint                     = 0;
+       int done                                = 0;
+
+       /*--S hint--*/   /* <-- Hints  look like this */
+       /*--E hint--*/
+
+       while(*index < current_strlen) {
+               char c = string[*index];
+
+               switch(c) {
+
+                       case '-':
+                               on_star = 0;
+                               if(third_dash)                  fourth_dash = 1;
+                               else if(in_hint)                third_dash      = 1;
+                               else if(first_dash)     second_dash = 1;
+                               else                                            first_dash = 1;
+                               break;
+
+                       case 'S':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               } 
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case 'E':
+                               on_star = 0;
+                               if(second_dash && !in_hint) {
+                                       (*index)++;
+                                       json_eat_ws(string, index, 1, current_strlen);
+                                       (*index)--; /* this will get incremented at the bottom of the loop */
+                                       in_hint = 1;
+                                       break;
+                               }
+
+                               if(second_dash && in_hint) {
+                                       buffer_add_char(buf, c);
+                                       break;
+                               }
+
+                       case '*':
+                               on_star = 1;
+                               break;
+
+                       case '/':
+                               if(on_star) 
+                                       done = 1;
+                               else
+                               on_star = 0;
+                               break;
+
+                       default:
+                               on_star = 0;
+                               if(in_hint)
+                                       buffer_add_char(buf, c);
+               }
+
+               (*index)++;
+               if(done) break;
+       }
+
+       if( buf->n_used > 0 && buffer)
+               *buffer = buffer_data(buf);
+
+       buffer_free(buf);
+       return 0;
+}
+
+int is_number(char c) {
+       switch(c) {
+               case '0':
+               case '1':
+               case '2':
+               case '3':
+               case '4':
+               case '5':
+               case '6':
+               case '7':
+               case '8':
+               case '9':
+                       return 1;
+       }
+       return 0;
+}
+
+int json_handle_error(char* string, unsigned long* index, char* err_msg) {
+
+       char buf[60];
+       memset(buf, 0, 60);
+
+       if(*index > 30)
+               strncpy( buf, string + (*index - 30), 59 );
+       else
+               strncpy( buf, string, 59 );
+
+       fprintf(stderr, 
+                       "\nError parsing json string at charracter %c "
+                       "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n", 
+                       string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
+
+       return -1;
+}
+
+
+jsonObject* legacy_jsonParseFile( const char* filename ) {
+       return json_parse_file( filename );
+}
+       
+jsonObject* json_parse_file(const char* filename) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = json_parse_string(data);
+       free(data);
+       return o;
+}
+
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj ) {
+
+       if(obj == NULL) return strdup("null");
+
+       growing_buffer* buf = buffer_init(64);
+
+       /* add class hints if we have a class name */
+       if(obj->classname) {
+               buffer_add(buf,"/*--S ");
+               buffer_add(buf,obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       switch( obj->type ) {
+
+               case JSON_BOOL: 
+                       if(obj->value.b) buffer_add(buf, "true"); 
+                       else buffer_add(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       double x = obj->value.n;
+
+                       /* if the number does not need to be a double,
+                               turn it into an int on the way out */
+                       if( x == (int) x ) {
+                               INT_TO_STRING((int)x);  
+                               buffer_add(buf, INTSTR);
+
+                       } else {
+                               DOUBLE_TO_STRING(x);
+                               buffer_add(buf, DOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       buffer_add(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       buffer_add(buf, "\"");
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       buffer_add(buf, output);
+                       free(output);
+                       buffer_add(buf, "\"");
+                       break;
+
+               case JSON_ARRAY:
+                       buffer_add(buf, "[");
+                       int i;
+                       for( i = 0; i!= obj->size; i++ ) {
+                               const jsonObject* x = jsonObjectGetIndex(obj,i);
+                               char* data = legacy_jsonObjectToJSON(x);
+                               buffer_add(buf, data);
+                               free(data);
+                               if(i != obj->size - 1)
+                                       buffer_add(buf, ",");
+                       }
+                       buffer_add(buf, "]");
+                       break;  
+
+               case JSON_HASH:
+       
+                       buffer_add(buf, "{");
+                       jsonObjectIterator* itr = jsonNewObjectIterator(obj);
+                       jsonObject* tmp;
+       
+                       while( (tmp = jsonObjectIteratorNext(itr)) ) {
+
+                               buffer_add(buf, "\"");
+
+                               char* key = itr->key;
+                               int len = strlen(key);
+                               char* output = uescape(key, len, 1);
+                               buffer_add(buf, output);
+                               free(output);
+
+                               buffer_add(buf, "\":");
+                               char* data =  legacy_jsonObjectToJSON(tmp);
+                               buffer_add(buf, data);
+                               if(jsonObjectIteratorHasNext(itr))
+                                       buffer_add(buf, ",");
+                               free(data);
+                       }
+
+                       jsonObjectIteratorFree(itr);
+                       buffer_add(buf, "}");
+                       break;
+               
+                       default:
+                               fprintf(stderr, "Unknown object type %d\n", obj->type);
+                               break;
+                               
+       }
+
+       /* close out the object hint */
+       if(obj->classname) {
+               buffer_add(buf, "/*--E ");
+               buffer_add(buf, obj->classname);
+               buffer_add(buf, "--*/");
+       }
+
+       char* data = buffer_data(buf);
+       buffer_free(buf);
+       return data;
+}
diff --git a/src/libstack/legacy_json.h b/src/libstack/legacy_json.h
new file mode 100644 (file)
index 0000000..27eac3f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+Copyright (C) 2005  Georgia Public Library Service 
+Bill Erickson <highfalutin@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+
+
+
+/* ---------------------------------------------------------------------------------------
+       JSON parser.
+ * --------------------------------------------------------------------------------------- */
+#ifndef LEGACY_JSON_H
+#define LEGACY_JSON_H
+
+#include "osrf_json.h"
+
+
+
+/* Parses the given JSON string and returns the built object. 
+ *     returns NULL (and prints parser error to stderr) on error.  
+ */
+
+jsonObject* json_parse_string(char* string);
+
+jsonObject* legacy_jsonParseString(char* string);
+jsonObject* legacy_jsonParseStringFmt( char* string, ... );
+
+jsonObject* json_parse_file( const char* filename );
+
+jsonObject* legacy_jsonParseFile( const char* string );
+
+
+
+/* does the actual parsing work.  returns 0 on success.  -1 on error and
+ * -2 if there was no object to build (string was all comments) 
+ */
+int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a string object */
+int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into a number or double object */
+int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns obj into an 'object' object */
+int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* returns 0 on success and turns object into an array object */
+int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* churns through whitespace and increments index as it goes.
+ * eat_all == true means we should eat newlines, tabs
+ */
+void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen);
+
+int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+/* removes comments from a json string.  if the comment contains a class hint
+ * and class_hint isn't NULL, an allocated char* with the class name will be
+ * shoved into *class_hint.  returns 0 on success, -1 on parse error.
+ * 'index' is assumed to be at the second character (*) of the comment
+ */
+int json_eat_comment(char* string, unsigned long* index, char** class_hint, int parse_class, int current_strlen);
+
+/* prints a useful error message to stderr. always returns -1 */
+int json_handle_error(char* string, unsigned long* index, char* err_msg);
+
+/* returns true if c is 0-9 */
+int is_number(char c);
+
+int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen);
+
+
+char* legacy_jsonObjectToJSON( const jsonObject* obj );
+
+#endif
diff --git a/src/libstack/osrf_json.h b/src/libstack/osrf_json.h
new file mode 100644 (file)
index 0000000..2c64b93
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+
+#include "utils.h"
+#include "osrf_list.h"
+#include "osrf_hash.h"
+
+#ifndef _JSON_H
+#define _JSON_H
+
+
+/* parser states */
+#define JSON_STATE_IN_OBJECT   0x1
+#define JSON_STATE_IN_ARRAY            0x2
+#define JSON_STATE_IN_STRING   0x4
+#define JSON_STATE_IN_UTF              0x8
+#define JSON_STATE_IN_ESCAPE   0x10
+#define JSON_STATE_IN_KEY              0x20
+#define JSON_STATE_IN_NULL             0x40
+#define JSON_STATE_IN_TRUE             0x80
+#define JSON_STATE_IN_FALSE            0x100
+#define JSON_STATE_IN_NUMBER   0x200
+#define JSON_STATE_IS_INVALID  0x400
+#define JSON_STATE_IS_DONE             0x800
+#define JSON_STATE_START_COMMEN        0x1000
+#define JSON_STATE_IN_COMMENT  0x2000
+#define JSON_STATE_END_COMMENT 0x4000
+
+
+/* object and array (container) states are pushed onto a stack so we
+ * can keep track of the object nest.  All other states are
+ * simply stored in the state field of the parser */
+#define JSON_STATE_SET(ctx,s) ctx->state |= s; /* set a state */
+#define JSON_STATE_REMOVE(ctx,s) ctx->state &= ~s; /* unset a state */
+#define JSON_STATE_CHECK(ctx,s) (ctx->state & s) ? 1 : 0 /* check if a state is set */
+#define JSON_STATE_POP(ctx) osrfListPop( ctx->stateStack ); /* remove a state from the stack */
+#define JSON_STATE_PUSH(ctx, state) osrfListPush( ctx->stateStack,(void*) state );/* push a state on the stack */
+#define JSON_STATE_PEEK(ctx) osrfListGetIndex(ctx->stateStack, ctx->stateStack->size -1) /* check which container type we're currently in */
+#define JSON_STATE_CHECK_STACK(ctx, s) (JSON_STATE_PEEK(ctx) == (void*) s ) ? 1 : 0  /* compare stack values */
+
+/* JSON types */
+#define JSON_HASH      0
+#define JSON_ARRAY     1
+#define JSON_STRING    2
+#define JSON_NUMBER    3
+#define JSON_NULL      4       
+#define JSON_BOOL      5
+
+#define JSON_PARSE_LAST_CHUNK 0x1 /* this is the last part of the string we're parsing */
+
+#define JSON_PARSE_FLAG_CHECK(ctx, f) (ctx->flags & f) ? 1 : 0 /* check if a parser state is set */
+
+#ifndef JSON_CLASS_KEY
+#define JSON_CLASS_KEY "__c"
+#endif
+#ifndef JSON_DATA_KEY
+#define JSON_DATA_KEY "__p"
+#endif
+
+
+struct jsonParserContextStruct {
+       int state;                                              /* what are we currently parsing */
+       char* chunk;                                    /* the chunk we're currently parsing */
+       int index;                                              /* where we are in parsing the current chunk */
+       int chunksize;                                  /* the size of the current chunk */
+       int flags;                                              /* parser flags */
+       osrfList* stateStack;           /* represents the nest of object/array states */
+       growing_buffer* buffer;         /* used to hold JSON strings, number, true, false, and null sequences */
+       growing_buffer* utfbuf;         /* holds the current unicode characters */
+       void* userData;                         /* opaque user pointer.  we ignore this */
+       struct jsonParserHandlerStruct* handler; /* the event handler struct */
+};
+typedef struct jsonParserContextStruct jsonParserContext;
+
+struct jsonParserHandlerStruct {
+       void (*handleStartObject)       (void* userData);
+       void (*handleObjectKey)         (void* userData, char* key);
+       void (*handleEndObject)         (void* userData);
+       void (*handleStartArray)        (void* userData);
+       void (*handleEndArray)          (void* userData);
+       void (*handleNull)                      (void* userData);
+       void (*handleString)                    (void* userData, char* string);
+       void (*handleBool)                      (void* userData, int boolval);
+       void (*handleNumber)                    (void* userData, long double num);
+       void (*handleError)                     (void* userData, char* err, ...);
+};
+typedef struct jsonParserHandlerStruct jsonParserHandler;
+
+struct _jsonObjectStruct {
+       unsigned long size;     /* number of sub-items */
+       char* classname;                /* optional class hint (not part of the JSON spec) */
+       int type;                               /* JSON type */
+       struct _jsonObjectStruct* parent;       /* who we're attached to */
+       union __jsonValue {     /* cargo */
+               osrfHash*       h;              /* object container */
+               osrfList*       l;              /* array container */
+               char*           s;              /* string */
+               int                     b;              /* bool */
+               long double     n;              /* number */
+       } value;
+};
+typedef struct _jsonObjectStruct jsonObject;
+
+struct _jsonObjectIteratorStruct {
+       jsonObject* obj; /* the object we're traversing */
+       osrfHashIterator* hashItr; /* the iterator for this hash */
+       char* key; /* if this object is an object, the current key */
+       unsigned long index; /* if this object is an array, the index */
+};
+typedef struct _jsonObjectIteratorStruct jsonObjectIterator;
+
+
+
+/** 
+ * Allocates a new parser context object
+ * @param handler The event handler struct
+ * @param userData Opaque user pointer which is available in callbacks
+ * and ignored by the parser
+ * @return An allocated parser context, NULL on error
+ */
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData);
+
+/**
+ * Deallocates a parser context
+ * @param ctx The context object
+ */
+void jsonParserFree( jsonParserContext* ctx );
+
+/**
+ * Parse a chunk of data.
+ * @param ctx The parser context
+ * @param data The data to parse
+ * @param datalen The size of the chunk to parser
+ * @param flags Reserved
+ */
+int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags );
+
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseString( char* str );
+jsonObject* jsonParseStringRaw( char* str );
+
+jsonObject* jsonParseStringFmt( char* str, ... );
+
+/**
+ * Parses a JSON string;
+ * @param str The string to parser
+ * @return The resulting JSON object or NULL on error
+ */
+jsonObject* jsonParseStringHandleError( void (*errorHandler) (const char*), char* str, ... );
+
+
+
+/**
+ * Creates a new json object
+ * @param data The string data this object will hold if 
+ * this object happens to be a JSON_STRING, NULL otherwise
+ * @return The allocated json object.  Must be freed with 
+ * jsonObjectFree()
+ */
+jsonObject* jsonNewObject(char* data, ...);
+
+/**
+ * Creates a new object of the given type
+ */
+jsonObject* jsonNewObjectType(int type);
+
+/**
+ * Creates a new number object
+ */
+jsonObject* jsonNewNumberObject( long double num );
+
+/**
+ * Deallocates an object
+ */
+void jsonObjectFree( jsonObject* o );
+
+/**
+ * Forces the given object to become an array (if it isn't already one) 
+ * and pushes the new object into the array
+ */
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo);
+
+/**
+ * Forces the given object to become a hash (if it isn't already one)
+ * and assigns the new object to the key of the hash
+ */
+unsigned long jsonObjectSetKey(
+               jsonObject* o, const char* key, jsonObject* newo);
+
+
+/**
+ * Turns the object into a JSON string.  The string must be freed by the caller */
+char* jsonObjectToJSON( const jsonObject* obj );
+char* jsonObjectToJSONRaw( const jsonObject* obj );
+
+
+/**
+ * Retrieves the object at the given key
+ */
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key );
+
+
+
+
+
+
+/** Allocates a new iterator 
+       @param obj The object over which to iterate.
+*/
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj);
+
+
+/** 
+       De-allocates an iterator 
+       @param iter The iterator object to free
+*/
+void jsonObjectIteratorFree(jsonObjectIterator* iter);
+
+/** 
+       Returns the object_node currently pointed to by the iterator
+       and increments the pointer to the next node
+       @param iter The iterator in question.
+ */
+jsonObject* jsonObjectIteratorNext(jsonObjectIterator* iter);
+
+
+/** 
+       @param iter The iterator.
+       @return True if there is another node after the current node.
+ */
+int jsonObjectIteratorHasNext(const jsonObjectIterator* iter);
+
+
+/** 
+       Returns a pointer to the object at the given index.  This call is
+       only valid if the object has a type of JSON_ARRAY.
+       @param obj The object
+       @param index The position within the object
+       @return The object at the given index.
+*/
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index );
+
+
+/* removes (and deallocates) the object at the given index (if one exists) and inserts 
+ * the new one.  returns the size on success, -1 on error 
+ * If obj is NULL, inserts a new object into the list with is_null set to true
+ */
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj);
+
+/* removes the object at the given index and, if more items exist,
+ * re-indexes (shifts down by 1) the rest of the objects in the array
+ */
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index);
+
+/* removes (and deallocates) the object with key 'key' if it exists */
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key);
+
+/* returns a pointer to the string data held by this object if this object
+       is a string.  Otherwise returns NULL*/
+char* jsonObjectGetString(const jsonObject*);
+
+long double jsonObjectGetNumber( const jsonObject* obj );
+
+/* sets the string data */
+void jsonObjectSetString(jsonObject* dest, const char* string);
+
+/* sets the number value for the object */
+void jsonObjectSetNumber(jsonObject* dest, double num);
+
+/* sets the class hint for this object */
+void jsonObjectSetClass(jsonObject* dest, const char* classname );
+
+int jsonBoolIsTrue( jsonObject* boolObj );
+
+
+jsonObject* jsonObjectClone( const jsonObject* o );
+
+
+/* tries to extract the string data from an object.
+       if object       -> NULL (the C NULL)
+       if array                ->      NULL  
+       if null         -> NULL 
+       if bool         -> NULL
+       if string/number the string version of either of those
+       The caller is responsible for freeing the returned string
+       */
+char* jsonObjectToSimpleString( const jsonObject* o );
+
+
+
+/* provides an XPATH style search interface (e.g. /some/node/here) and 
+       return the object at that location if one exists.  Naturally,  
+       every element in the path must be a proper object ("hash" / {}).
+       Returns NULL if the specified node is not found 
+       Note also that the object returned is a clone and
+       must be freed by the caller
+*/
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ... );
+
+
+/* formats a JSON string from printing.  User must free returned string */
+char* jsonFormatString( const char* jsonString );
+
+/* sets the error handler for all parsers */
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*));
+
+jsonObject* jsonParseFile( char* filename );
+
+/* ------------------------------------------------------------------------- */
+/**
+ * The following methods provide a ficility for serializing and
+ * deserializing "classed" JSON objects.  To give a JSON object a 
+ * class, simply call jsonObjectSetClass().  
+ * Then, calling jsonObjectEncodeClass() will convert the JSON
+ * object (and any sub-objects) to a JSON object with class 
+ * wrapper objects like so:
+ * { _c : "classname", _d : <json_thing> }
+ * In this example _c is the class key and _d is the data (object)
+ * key.  The keys are defined by the constants 
+ * OSRF_JSON_CLASS_KEY and OSRF_JSON_DATA_KEY
+ * To revive a serialized object, simply call
+ * jsonObjectDecodeClass()
+ */
+
+
+/** Converts a class-wrapped object into an object with the
+ * classname set
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectDecodeClass( jsonObject* obj );
+
+
+/** Converts an object with a classname into a
+ * class-wrapped (serialized) object
+ * Caller must free the returned object 
+ */ 
+jsonObject* jsonObjectEncodeClass( jsonObject* obj );
+
+/* ------------------------------------------------------------------------- */
+
+
+/**
+ *     Generates an XML representation of a JSON object */
+char* jsonObjectToXML(jsonObject*);
+
+#endif
diff --git a/src/libstack/osrf_json_object.c b/src/libstack/osrf_json_object.c
new file mode 100644 (file)
index 0000000..71fc181
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+#include "osrf_json.h"
+#include "osrf_json_utils.h"
+jsonObject* jsonNewObject(char* data, ...) {
+
+       jsonObject* o;
+       OSRF_MALLOC(o, sizeof(jsonObject));
+       o->type = JSON_NULL;
+
+       if(data) {
+               VA_LIST_TO_STRING(data);
+               o->type = JSON_STRING;
+               o->value.s = strdup(VA_BUF);
+       }
+
+       return o;
+}
+
+jsonObject* jsonNewNumberObject( long double num ) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = JSON_NUMBER;
+       o->value.n = num;
+       return o;
+}
+
+jsonObject* jsonNewObjectType(int type) {
+       jsonObject* o = jsonNewObject(NULL);
+       o->type = type;
+       return o;
+}
+
+void jsonObjectFree( jsonObject* o ) {
+
+       if(!o || o->parent) return;
+       free(o->classname);
+
+       switch(o->type) {
+               case JSON_HASH          : osrfHashFree(o->value.h); break;
+               case JSON_ARRAY : osrfListFree(o->value.l); break;
+               case JSON_STRING        : free(o->value.s); break;
+       }
+       free(o);
+}
+
+static void _jsonFreeHashItem(char* key, void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+static void _jsonFreeListItem(void* item){
+       if(!item) return;
+       jsonObject* o = (jsonObject*) item;
+       o->parent = NULL; /* detach the item */
+       jsonObjectFree(o);
+}
+
+
+unsigned long jsonObjectPush(jsonObject* o, jsonObject* newo) {
+       if(!(o && newo)) return -1;
+       JSON_INIT_CLEAR(o, JSON_ARRAY);
+       newo->parent = o;
+       osrfListPush( o->value.l, newo );
+       o->size = o->value.l->size;
+       return o->size;
+}
+
+unsigned long jsonObjectSetIndex(jsonObject* dest, unsigned long index, jsonObject* newObj) {
+       if(!(dest && newObj)) return -1;
+       JSON_INIT_CLEAR(dest, JSON_ARRAY);
+       newObj->parent = dest;
+       osrfListSet( dest->value.l, newObj, index );
+       dest->size = dest->value.l->size;
+       return dest->value.l->size;
+}
+
+unsigned long jsonObjectSetKey(
+               jsonObject* o, const char* key, jsonObject* newo) {
+       if(!(o && key && newo)) return -1;
+       JSON_INIT_CLEAR(o, JSON_HASH);
+       newo->parent = o;
+       osrfHashSet( o->value.h, newo, key );
+       o->size = o->value.h->size;
+       return o->size;
+}
+
+jsonObject* jsonObjectGetKey( const jsonObject* obj, const char* key ) {
+       if(!(obj && obj->type == JSON_HASH && obj->value.h && key)) return NULL;
+       return osrfHashGet( obj->value.h, key);
+}
+
+char* jsonObjectToJSON( const jsonObject* obj ) {
+       jsonObject* obj2 = jsonObjectEncodeClass( (jsonObject*) obj);
+       char* json = jsonObjectToJSONRaw(obj2);
+       jsonObjectFree(obj2);
+       return json;
+}
+
+char* jsonObjectToJSONRaw( const jsonObject* obj ) {
+       if(!obj) return NULL;
+       growing_buffer* buf = buffer_init(32);
+
+       switch(obj->type) {
+
+               case JSON_BOOL :
+                       if(obj->value.b) OSRF_BUFFER_ADD(buf, "true"); 
+                       else OSRF_BUFFER_ADD(buf, "false"); 
+                       break;
+
+               case JSON_NUMBER: {
+                       long double x = obj->value.n;
+                       if( x == (long) x ) {
+                               LONG_TO_STRING((long)x);        
+                               OSRF_BUFFER_ADD(buf, LONGSTR);
+
+                       } else {
+                               LONG_DOUBLE_TO_STRING(x);
+                               OSRF_BUFFER_ADD(buf, LONGDOUBLESTR);
+                       }
+                       break;
+               }
+
+               case JSON_NULL:
+                       OSRF_BUFFER_ADD(buf, "null");
+                       break;
+
+               case JSON_STRING:
+                       OSRF_BUFFER_ADD(buf, "\"");
+                       char* data = obj->value.s;
+                       int len = strlen(data);
+                       
+                       char* output = uescape(data, len, 1);
+                       OSRF_BUFFER_ADD(buf, output);
+                       free(output);
+                       OSRF_BUFFER_ADD(buf, "\"");
+                       break;
+                       
+               case JSON_ARRAY: {
+                       OSRF_BUFFER_ADD(buf, "[");
+                       if( obj->value.l ) {
+                               int i;
+                               for( i = 0; i != obj->value.l->size; i++ ) {
+                                       char* json = jsonObjectToJSON(osrfListGetIndex(obj->value.l, i));
+                                       if(i > 0) OSRF_BUFFER_ADD(buf, ",");
+                                       OSRF_BUFFER_ADD(buf, json);
+                                       free(json);
+                               }
+                       } 
+                       OSRF_BUFFER_ADD(buf, "]");
+                       break;
+               }
+
+               case JSON_HASH: {
+
+                       OSRF_BUFFER_ADD(buf, "{");
+                       osrfHashIterator* itr = osrfNewHashIterator(obj->value.h);
+                       jsonObject* item;
+                       int i = 0;
+
+                       while( (item = osrfHashIteratorNext(itr)) ) {
+                               if(i++ > 0) OSRF_BUFFER_ADD(buf, ",");
+                               buffer_fadd(buf, "\"%s\":", itr->current);
+                               char* json = jsonObjectToJSON(item);
+                               OSRF_BUFFER_ADD(buf, json);
+                               free(json);
+                       }
+
+                       osrfHashIteratorFree(itr);
+                       OSRF_BUFFER_ADD(buf, "}");
+                       break;
+               }
+       }
+
+       char* data = buffer_data(buf);
+       buffer_free(buf);
+       return data;
+}
+
+
+jsonObjectIterator* jsonNewObjectIterator(const jsonObject* obj) {
+       if(!obj) return NULL;
+       jsonObjectIterator* itr;
+       OSRF_MALLOC(itr, sizeof(jsonObjectIterator));
+
+       itr->obj                = (jsonObject*) obj;
+       itr->index      = 0;
+       itr->key                = NULL;
+
+       if( obj->type == JSON_HASH )
+               itr->hashItr = osrfNewHashIterator(obj->value.h);
+       
+       return itr;
+}
+
+void jsonObjectIteratorFree(jsonObjectIterator* itr) {
+       if(!itr) return;
+       free(itr->key);
+       osrfHashIteratorFree(itr->hashItr);
+       free(itr);
+}
+
+jsonObject* jsonObjectIteratorNext(jsonObjectIterator* itr) {
+       if(!(itr && itr->obj)) return NULL;
+       if( itr->obj->type == JSON_HASH ) {
+               if(!itr->hashItr) return NULL;
+               jsonObject* item = osrfHashIteratorNext(itr->hashItr);
+               free(itr->key);
+               itr->key = strdup(itr->hashItr->current);
+               return item;
+       } else {
+               return jsonObjectGetIndex( itr->obj, itr->index++ );
+       }
+}
+
+int jsonObjectIteratorHasNext(const jsonObjectIterator* itr) {
+       if(!(itr && itr->obj)) return 0;
+       if( itr->obj->type == JSON_HASH )
+               return osrfHashIteratorHasNext( itr->hashItr );
+       return (itr->index < itr->obj->size) ? 1 : 0;
+}
+
+jsonObject* jsonObjectGetIndex( const jsonObject* obj, unsigned long index ) {
+       if(!obj) return NULL;
+       return (obj->type == JSON_ARRAY) ? 
+               osrfListGetIndex(obj->value.l, index) : NULL;
+}
+
+
+
+unsigned long jsonObjectRemoveIndex(jsonObject* dest, unsigned long index) {
+       if( dest && dest->type == JSON_ARRAY ) {
+               osrfListRemove(dest->value.l, index);
+               return dest->value.l->size;
+       }
+       return -1;
+}
+
+
+unsigned long jsonObjectRemoveKey( jsonObject* dest, const char* key) {
+       if( dest && key && dest->type == JSON_HASH ) {
+               osrfHashRemove(dest->value.h, key);
+               return 1;
+       }
+       return -1;
+}
+
+char* jsonObjectGetString(const jsonObject* obj) {
+       return (obj && obj->type == JSON_STRING) ? obj->value.s : NULL;
+}
+
+long double jsonObjectGetNumber( const jsonObject* obj ) {
+       return (obj && obj->type == JSON_NUMBER) ? obj->value.n : 0;
+}
+
+void jsonObjectSetString(jsonObject* dest, const char* string) {
+       if(!(dest && string)) return;
+       JSON_INIT_CLEAR(dest, JSON_STRING);
+       free(dest->value.s);
+       dest->value.s = strdup(string);
+}
+
+void jsonObjectSetNumber(jsonObject* dest, double num) {
+       if(!dest) return;
+       JSON_INIT_CLEAR(dest, JSON_NUMBER);
+       dest->value.n = num;
+}
+
+void jsonObjectSetClass(jsonObject* dest, const char* classname ) {
+       if(!(dest && classname)) return;
+       free(dest->classname);
+       dest->classname = strdup(classname);
+}
+
+jsonObject* jsonObjectClone( const jsonObject* o ) {
+       if(!o) return NULL;
+       char* json = jsonObjectToJSONRaw(o);
+       jsonObject* oo = jsonParseStringRaw(json);
+       oo->type = o->type;
+       jsonObjectSetClass(oo, o->classname);
+       free(json);
+       return oo;
+}
+
+int jsonBoolIsTrue( jsonObject* boolObj ) {
+    if( boolObj && boolObj->type == JSON_BOOL && boolObj->value.b )
+        return 1;
+    return 0;
+}
+
+
+char* jsonObjectToSimpleString( const jsonObject* o ) {
+       if(!o) return NULL;
+
+       char* value = NULL;
+
+       switch( o->type ) {
+
+               case JSON_NUMBER: {
+
+                       if( o->value.n == (int) o->value.n ) {
+                               LONG_TO_STRING((long) o->value.n);      
+                               value = strdup(LONGSTR);
+
+                       } else {
+                               LONG_DOUBLE_TO_STRING(o->value.n);
+                               value = strdup(LONGDOUBLESTR);
+                       }
+
+                       break;
+               }
+
+               case JSON_STRING:
+                       value = strdup(o->value.s);
+       }
+
+       return value;
+}
+
+
diff --git a/src/libstack/osrf_json_parser.c b/src/libstack/osrf_json_parser.c
new file mode 100644 (file)
index 0000000..37ab058
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+#include "osrf_json.h"
+#include "osrf_json_utils.h"
+#include <ctype.h>
+
+
+/* if the client sets a global error handler, this will point to it */
+static void (*jsonClientErrorCallback) (const char*) = NULL;
+
+/* these are the handlers for our internal parser */
+static jsonParserHandler jsonInternalParserHandlerStruct = {
+       _jsonHandleStartObject,
+       _jsonHandleObjectKey,
+       _jsonHandleEndObject,
+       _jsonHandleStartArray,
+       _jsonHandleEndArray,
+       _jsonHandleNull,
+       _jsonHandleString,
+       _jsonHandleBool,
+       _jsonHandleNumber,
+       _jsonHandleError
+};
+static jsonParserHandler* 
+       jsonInternalParserHandler = &jsonInternalParserHandlerStruct; 
+
+
+jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData) {
+       jsonParserContext* ctx;
+       OSRF_MALLOC(ctx, sizeof(jsonParserContext));
+       ctx->stateStack                 = osrfNewList();
+       ctx->buffer                                     = buffer_init(512);
+       ctx->utfbuf                                     = buffer_init(5);
+       ctx->handler                            = handler;
+       ctx->state                                      = 0;
+       ctx->index                                      = 0;
+       ctx->chunk                                      = NULL;
+       ctx->userData                           = userData;
+       return ctx;
+}
+
+void jsonParserFree( jsonParserContext* ctx ) {
+       if(!ctx) return;
+       buffer_free(ctx->buffer);
+       buffer_free(ctx->utfbuf);
+       osrfListFree(ctx->stateStack);
+       free(ctx);
+}
+
+
+void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) {
+       jsonClientErrorCallback = errorHandler;
+}
+
+
+int _jsonParserError( jsonParserContext* ctx, char* err, ... ) {
+       if( ctx->handler->handleError ) {
+               VA_LIST_TO_STRING(err);
+               int pre = ctx->index - 15;
+               int post        = ctx->index + 15;
+               while( pre < 0 ) pre++;
+               while( post >= ctx->chunksize ) post--;
+               int l = post - pre;
+               char buf[l];
+        memset(buf, 0, l);
+               snprintf(buf, l, ctx->chunk + pre);
+               ctx->handler->handleError( ctx->userData, 
+                       "*JSON Parser Error\n - char  = %c\n "
+                       "- index = %d\n - near  => %s\n - %s", 
+                       ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
+       }
+       JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
+       return -1;
+}
+
+
+int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
+
+       /* collect as many of the utf characters as we can in this chunk */
+       JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
+
+       /* we ran off the end of the chunk */
+       if( ctx->utfbuf->n_used < 4 ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
+               return 1;
+       }
+
+       ctx->index--; /* push it back to index of the final utf char */
+
+       /* ----------------------------------------------------------------------- */
+       /* We have all of the escaped unicode data.  Write it to the buffer */
+       /* The following chunk is used with permission from 
+        * json-c http://oss.metaparadigm.com/json-c/ 
+        */
+       #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
+       unsigned char utf_out[4];
+       memset(utf_out,0,4);
+       char* buf = ctx->utfbuf->buf;
+
+       unsigned int ucs_char =
+               (hexdigit(buf[0] ) << 12) +
+               (hexdigit(buf[1]) << 8) +
+               (hexdigit(buf[2]) << 4) +
+               hexdigit(buf[3]);
+
+       if (ucs_char < 0x80) {
+               utf_out[0] = ucs_char;
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else if (ucs_char < 0x800) {
+               utf_out[0] = 0xc0 | (ucs_char >> 6);
+               utf_out[1] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+
+       } else {
+               utf_out[0] = 0xe0 | (ucs_char >> 12);
+               utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
+               utf_out[2] = 0x80 | (ucs_char & 0x3f);
+               OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
+       }
+       /* ----------------------------------------------------------------------- */
+       /* ----------------------------------------------------------------------- */
+
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+       buffer_reset(ctx->utfbuf);
+       return 0;
+}
+
+
+
+/* type : 0=null, 1=true, 2=false */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
+
+       switch(type) {
+
+               case 0: /* JSON null */
+
+                       /* first see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "null", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                               if( ctx->handler->handleNull ) 
+                                       ctx->handler->handleNull(ctx->userData);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
+                               return 1;
+                       } 
+
+                       if( strncasecmp(ctx->buffer->buf, "null", 4) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
+                       if( ctx->handler->handleNull ) 
+                               ctx->handler->handleNull(ctx->userData);
+                       break;
+
+               case 1: /* JSON true */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 3) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "true", 4) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 1);
+                               ctx->index += 4;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 4);
+                       if( ctx->buffer->n_used < 4 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
+                               return 1;
+                       } 
+                       if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
+                               return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
+                       }
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 1);
+                       break;
+
+               case 2: /* JSON false */
+
+                       /* see if we have it all first */
+                       if( ctx->chunksize > (ctx->index + 4) ) {
+                               if( strncasecmp(ctx->chunk + ctx->index, "false", 5) ) 
+                                       return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                               if( ctx->handler->handleBool ) 
+                                       ctx->handler->handleBool(ctx->userData, 0);
+                               ctx->index += 5;
+                               break;
+                       }
+
+                       JSON_CACHE_DATA(ctx, ctx->buffer, 5);
+                       if( ctx->buffer->n_used < 5 ) {
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
+                               return 1;
+                       }
+                       if( strncasecmp( ctx->buffer->buf, "false", 5 ) ) 
+                               return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
+                       if( ctx->handler->handleBool ) 
+                               ctx->handler->handleBool(ctx->userData, 0);
+                       break;
+
+               default: 
+                       fprintf(stderr, "Invalid type flag\n");
+                       return -1;
+
+       }
+
+       ctx->index--; /* set it back to the index of the final sequence character */
+       buffer_reset(ctx->buffer);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
+
+       return 0;
+}
+
+
+int _jsonParserHandleString( jsonParserContext* ctx ) {
+
+       char c = ctx->chunk[ctx->index];
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
+
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
+
+                       return _jsonParserHandleUnicode( ctx );
+                                               
+               } else {
+
+                       switch(c) {
+
+                               /* handle all of the escape chars */
+                               case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
+                               case '"'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
+                               case 't'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
+                               case 'b'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
+                               case 'f'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
+                               case 'r'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
+                               case 'n'        : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
+                               case 'u'        : 
+                                       ctx->index++; /* progress to the first utf char */
+                                       return _jsonParserHandleUnicode( ctx );
+                               default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+                       }
+               }
+
+               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
+               return 0;
+
+       } else {
+
+               switch(c) {
+
+                       case '"'        : /* this string is ending */
+                               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
+
+                                       /* object key */
+                                       if(ctx->handler->handleObjectKey) {
+                                               ctx->handler->handleObjectKey( 
+                                                       ctx->userData, ctx->buffer->buf);
+                                       }
+
+                               } else { /* regular json string */
+
+                                       if(ctx->handler->handleString) {
+                                               ctx->handler->handleString( 
+                                                       ctx->userData, ctx->buffer->buf );
+                                       }
+
+                               }
+
+                               buffer_reset(ctx->buffer); /* flush the buffer and states */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
+                       default  : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
+               }
+       }
+       return 0;
+}
+
+
+int _jsonParserHandleNumber( jsonParserContext* ctx ) {
+       char c = ctx->chunk[ctx->index];
+
+       do {
+               OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
+               c = ctx->chunk[++(ctx->index)];
+       } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
+
+       /* if we're run off the end of the chunk and we're not parsing the last chunk,
+        * save the number and the state */
+       if( ctx->index >= ctx->chunksize && 
+                       ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
+               JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
+               return 1;
+       }
+
+       /* make me more strict */
+       char* err = NULL;
+       long double d = strtod(ctx->buffer->buf, &err);
+       if(err && err[0] != '\0') 
+               return _jsonParserError(ctx, "Invalid number sequence");
+       JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
+       buffer_reset(ctx->buffer);
+       if(ctx->handler->handleNumber)
+               ctx->handler->handleNumber( ctx->userData, d );
+       ctx->index--; /* scooch back to the first non-digit number */
+       return 0;
+}
+
+
+
+
+int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags ) {
+
+       if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
+       ctx->chunksize  = datalen;
+       ctx->chunk              = data;
+       ctx->flags              = flags;
+       char c;
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
+               return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
+
+       if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
+               return _jsonParserError( ctx, "Extra content at end of JSON data" );
+
+       for( ctx->index = 0; (ctx->index < ctx->chunksize) && 
+                               (c = ctx->chunk[ctx->index]); ctx->index++ ) {
+
+               /* middle of parsing a string */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
+                       if( _jsonParserHandleString(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+               /* middle of parsing a number */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
+                       if( _jsonParserHandleNumber(ctx) == -1 )
+                               return -1;
+                       continue;
+               }
+
+
+#ifdef JSON_IGNORE_COMMENTS
+               /* we just saw a bare '/' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               continue;
+                       } else {
+                               return _jsonParserError( ctx, "Invalid comment initializer" );
+                       }
+               }
+
+               /* we're currently in the middle of a comment block */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
+                       if(c == '*') {
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               continue;
+                       }
+               }
+
+               /* we're in a comment, and we just saw a '*' character */
+               if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
+                       if( c == '/' ) { /* comment is finished */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                               continue;
+                       } else {
+                               /* looks like this isn't the end of the comment after all */
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
+                       }
+               }
+#endif
+
+               /* if we're in the middle of parsing a null/true/false sequence */
+               if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL | 
+                                       JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
+
+                       int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
+                               (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
+
+                       if( _jsonParserHandleMatch( ctx, type ) == -1 ) 
+                               return -1;
+                       continue;
+               }
+
+               JSON_EAT_WS(ctx);
+
+               /* handle all of the top level characters */
+               switch(c) {
+
+                       case '{' : /* starting an object */
+                               if( ctx->handler->handleStartObject) 
+                                       ctx->handler->handleStartObject( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case '}' : /* ending an object */
+                               if( ctx->handler->handleEndObject) 
+                                       ctx->handler->handleEndObject( ctx->userData ); 
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+
+                       case '[' : /* starting an array */
+                               if( ctx->handler->handleStartArray )
+                                       ctx->handler->handleStartArray( ctx->userData );
+                               JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
+                               break;
+
+                       case ']': /* ending an array */
+                               if( ctx->handler->handleEndArray )
+                                       ctx->handler->handleEndArray( ctx->userData );
+                               JSON_STATE_POP(ctx);
+                               if( JSON_STATE_PEEK(ctx) == NULL )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
+                               break;
+                               
+                       case ':' : /* done with the object key */
+                               JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case ',' : /* after object or array item */
+                               if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
+                                       JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
+                               break;
+
+                       case 'n' :
+                       case 'N' : /* null */
+                               if( _jsonParserHandleMatch( ctx, 0 ) == -1)
+                                       return -1;
+                               break;
+
+                       case 't' :
+                       case 'T' :
+                               if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
+                                       return -1;
+                               break;
+
+                       case 'f' :
+                       case 'F' :
+                               if( _jsonParserHandleMatch( ctx, 2 ) == -1)
+                                       return -1;
+                               break;
+
+                       case '"' : 
+                               JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
+                               break;
+
+#ifdef JSON_IGNORE_COMMENTS
+                       case '/' :
+                               JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
+                               break;
+#endif
+
+                       default:
+                               if( strchr(JSON_NUMBER_CHARS, c) ) {
+                                       if( _jsonParserHandleNumber( ctx ) == -1 )
+                                               return -1;
+                               } else {
+                                       return _jsonParserError( ctx, "Invalid Token" );
+                               }
+               }
+       }
+
+       return 0;
+}
+
+
+jsonInternalParser* _jsonNewInternalParser() {
+       jsonInternalParser* p;
+       OSRF_MALLOC(p, sizeof(jsonInternalParser));
+       p->ctx = jsonNewParser( jsonInternalParserHandler, p );
+       p->obj          = NULL;
+       p->lastkey      = NULL;
+       return p;
+}
+
+void _jsonInternalParserFree(jsonInternalParser* p) {
+       if(!p) return;
+       jsonParserFree(p->ctx);
+       free(p->lastkey);
+       free(p);
+}
+
+static jsonObject* _jsonParseStringImpl(char* str, void (*errorHandler) (const char*) ) {
+       jsonInternalParser* parser = _jsonNewInternalParser();
+       parser->handleError = errorHandler;
+       jsonParseChunk( parser->ctx, str, strlen(str),  JSON_PARSE_LAST_CHUNK );
+       jsonObject* obj = parser->obj;
+       _jsonInternalParserFree(parser);
+       return obj;
+}
+
+jsonObject* jsonParseStringHandleError( 
+               void (*errorHandler) (const char*), char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, errorHandler);
+}
+
+jsonObject* jsonParseString( char* str ) {
+       if(!str) return NULL;
+       jsonObject* obj =  _jsonParseStringImpl(str, NULL);
+       jsonObject* obj2 = jsonObjectDecodeClass(obj);
+       jsonObjectFree(obj);
+       return obj2;
+}
+
+jsonObject* jsonParseStringRaw( char* str ) {
+       if(!str) return NULL;
+       return _jsonParseStringImpl(str, NULL);
+}
+
+jsonObject* jsonParseStringFmt( char* str, ... ) {
+       if(!str) return NULL;
+       VA_LIST_TO_STRING(str);
+       return _jsonParseStringImpl(VA_BUF, NULL);
+}
+
+
+#define JSON_SHOVE_ITEM(ctx,type)  \
+       jsonInternalParser* p = (jsonInternalParser*) ctx;\
+       _jsonInsertParserItem(p, jsonNewObjectType(type));
+
+void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
+void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
+void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
+
+void _jsonHandleObjectKey(void* ctx, char* key) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       free(p->lastkey);
+       p->lastkey = strdup(key);
+}
+
+void _jsonHandleEndObject(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleEndArray(void* ctx) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       p->current = p->current->parent;
+}
+
+void _jsonHandleString(void* ctx, char* string) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewObject(string));
+}
+
+void _jsonHandleBool(void* ctx, int boolval) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       jsonObject* obj = jsonNewObjectType(JSON_BOOL);
+       obj->value.b = boolval;
+       _jsonInsertParserItem(p, obj);
+}
+
+void _jsonHandleNumber(void* ctx, long double num) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       _jsonInsertParserItem(p, jsonNewNumberObject(num));
+}
+
+void _jsonHandleError(void* ctx, char* str, ...) {
+       jsonInternalParser* p = (jsonInternalParser*) ctx;
+       VA_LIST_TO_STRING(str);
+
+       if( p->handleError ) 
+               p->handleError(VA_BUF);
+       else 
+               if( jsonClientErrorCallback ) 
+                       jsonClientErrorCallback(VA_BUF);
+
+       else fprintf(stderr, "%s\n", VA_BUF);
+       jsonObjectFree(p->obj);
+       p->obj = NULL;
+}
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
+
+       if( !p->obj ) {
+
+               /* new parser, set the new object to our object */
+               p->obj = p->current = newo;
+
+       } else {
+
+               /* insert the new object into the current container object */
+               switch(p->current->type) { 
+                       case JSON_HASH  : jsonObjectSetKey(p->current, p->lastkey, newo);  break;
+                       case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
+                       default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK); 
+               } 
+
+               /* if the new object is a container object, make it our current container */
+               if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )
+                       p->current = newo;      
+       }
+}
+
+
diff --git a/src/libstack/osrf_json_tools.c b/src/libstack/osrf_json_tools.c
new file mode 100644 (file)
index 0000000..6144e8c
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+#include "osrf_json.h"
+
+jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass );
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* path, ...);
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path);
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root);
+
+
+static char* __tabs(int count) {
+       growing_buffer* buf = buffer_init(24);
+       int i;
+       for(i=0;i<count;i++) OSRF_BUFFER_ADD(buf, "  ");
+       char* final = buffer_data( buf );
+       buffer_free( buf );
+       return final;
+}
+
+char* jsonFormatString( const char* string ) {
+       if(!string) return strdup("");
+
+       growing_buffer* buf = buffer_init(64);
+       int i;
+       int depth = 0;
+       char* tab = NULL;
+
+       char c;
+       for(i=0; i!= strlen(string); i++) {
+               c = string[i];
+
+               if( c == '{' || c == '[' ) {
+
+                       tab = __tabs(++depth);
+                       buffer_fadd( buf, "%c\n%s", c, tab);
+                       free(tab);
+
+               } else if( c == '}' || c == ']' ) {
+
+                       tab = __tabs(--depth);
+                       buffer_fadd( buf, "\n%s%c", tab, c);
+                       free(tab);
+
+                       if(string[i+1] != ',') {
+                               tab = __tabs(depth);
+                               buffer_fadd( buf, "%s", tab );  
+                               free(tab);
+                       }
+
+               } else if( c == ',' ) {
+
+                       tab = __tabs(depth);
+                       buffer_fadd(buf, ",\n%s", tab);
+                       free(tab);
+
+               } else { buffer_add_char(buf, c); }
+
+       }
+
+       char* result = buffer_data(buf);
+       buffer_free(buf);
+       return result;
+}
+
+
+
+jsonObject* jsonObjectDecodeClass( jsonObject* obj ) {
+       if(!obj) return jsonNewObject(NULL);
+
+       jsonObject* newObj              = NULL; 
+       jsonObject* classObj            = NULL; 
+       jsonObject* payloadObj  = NULL;
+       int i;
+
+       if( obj->type == JSON_HASH ) {
+
+               /* are we a special class object? */
+               if( (classObj = jsonObjectGetKey( obj, JSON_CLASS_KEY )) ) {
+
+                       /* do we have a payload */
+                       if( (payloadObj = jsonObjectGetKey( obj, JSON_DATA_KEY )) ) {
+                               newObj = jsonObjectDecodeClass( payloadObj ); 
+                               jsonObjectSetClass( newObj, jsonObjectGetString(classObj) );
+
+                       } else { /* class is defined but there is no payload */
+                               return NULL;
+                       }
+
+               } else { /* we're a regular hash */
+
+                       jsonObjectIterator* itr = jsonNewObjectIterator(obj);
+                       jsonObject* tmp;
+                       newObj = jsonNewObjectType(JSON_HASH);
+                       while( (tmp = jsonObjectIteratorNext(itr)) ) {
+                               jsonObject* o = jsonObjectDecodeClass(tmp);
+                               jsonObjectSetKey( newObj, itr->key, o );
+                       }
+                       jsonObjectIteratorFree(itr);
+               }
+
+       } else {
+
+               if( obj->type == JSON_ARRAY ) { /* we're an array */
+                       newObj = jsonNewObjectType(JSON_ARRAY);
+                       for( i = 0; i != obj->size; i++ ) {
+                               jsonObject* tmp = jsonObjectDecodeClass(jsonObjectGetIndex( obj, i ) );
+                               jsonObjectSetIndex( newObj, i, tmp );
+                       }
+
+               } else { /* not an aggregate type */
+                       newObj = jsonObjectClone(obj);
+               }
+       }
+               
+       return newObj;
+}
+
+jsonObject* jsonObjectEncodeClass( jsonObject* obj ) {
+       return _jsonObjectEncodeClass( obj, 0 );
+}
+
+jsonObject* _jsonObjectEncodeClass( jsonObject* obj, int ignoreClass ) {
+
+       //if(!obj) return NULL;
+       if(!obj) return jsonNewObject(NULL);
+       jsonObject* newObj = NULL;
+
+       if( obj->classname && ! ignoreClass ) {
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               jsonObjectSetKey( newObj, 
+                       JSON_CLASS_KEY, jsonNewObject(obj->classname) ); 
+
+               jsonObjectSetKey( newObj, 
+                       JSON_DATA_KEY, _jsonObjectEncodeClass(obj, 1));
+
+       } else if( obj->type == JSON_HASH ) {
+
+               jsonObjectIterator* itr = jsonNewObjectIterator(obj);
+               jsonObject* tmp;
+               newObj = jsonNewObjectType(JSON_HASH);
+
+               while( (tmp = jsonObjectIteratorNext(itr)) ) {
+                       jsonObjectSetKey( newObj, itr->key, 
+                                       _jsonObjectEncodeClass(tmp, 0));
+               }
+               jsonObjectIteratorFree(itr);
+
+       } else if( obj->type == JSON_ARRAY ) {
+
+               newObj = jsonNewObjectType(JSON_ARRAY);
+               int i;
+               for( i = 0; i != obj->size; i++ ) {
+                       jsonObjectSetIndex( newObj, i, 
+                               _jsonObjectEncodeClass(jsonObjectGetIndex( obj, i ), 0 ));
+               }
+
+       } else {
+               newObj = jsonObjectClone(obj);
+       }
+
+       return newObj;
+}
+
+
+
+
+static char* _escape_xml (char*);
+static int _recurse_jsonObjectToXML(jsonObject*, growing_buffer*);
+
+char* jsonObjectToXML(jsonObject* obj) {
+
+       growing_buffer * res_xml;
+       char * output;
+
+       res_xml = buffer_init(1024);
+
+       if (!obj)
+               return strdup("<null/>");
+       
+       _recurse_jsonObjectToXML( obj, res_xml );
+       output = buffer_data(res_xml);
+       
+       buffer_free(res_xml);
+
+       return output;
+
+}
+
+int _recurse_jsonObjectToXML(jsonObject* obj, growing_buffer* res_xml) {
+
+       char * hint = NULL;
+       char * bool_val = NULL;
+       int i = 0;
+       
+       if (obj->classname)
+               hint = strdup(obj->classname);
+
+       if(obj->type == JSON_NULL) {
+
+               if (hint)
+                       buffer_fadd(res_xml, "<null class_hint=\"%s\"/>",hint);
+               else
+                       buffer_add(res_xml, "<null/>");
+
+       } else if(obj->type == JSON_BOOL) {
+
+               if (obj->value.b)
+                       bool_val = strdup("true");
+               else
+                       bool_val = strdup("false");
+
+               if (hint)
+                       buffer_fadd(res_xml, "<boolean value=\"%s\" class_hint=\"%s\"/>", bool_val, hint);
+               else
+                       buffer_fadd(res_xml, "<boolean value=\"%s\"/>", bool_val);
+
+               free(bool_val);
+                
+       } else if (obj->type == JSON_STRING) {
+               if (hint) {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string class_hint=\"%s\">%s</string>", hint, t);
+                       free(t);
+               } else {
+                       char * t = _escape_xml(jsonObjectGetString(obj));
+                       buffer_fadd(res_xml,"<string>%s</string>", t);
+                       free(t);
+               }
+
+       } else if(obj->type == JSON_NUMBER) {
+               double x = jsonObjectGetNumber(obj);
+               if (hint) {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%d</number>", hint, (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number class_hint=\"%s\">%lf</number>", hint, x);
+               } else {
+                       if (x == (int)x)
+                               buffer_fadd(res_xml,"<number>%d</number>", (int)x);
+                       else
+                               buffer_fadd(res_xml,"<number>%lf</number>", x);
+               }
+
+       } else if (obj->type == JSON_ARRAY) {
+
+               if (hint) 
+                       buffer_fadd(res_xml,"<array class_hint=\"%s\">", hint);
+               else
+                               buffer_add(res_xml,"<array>");
+
+               for ( i = 0; i!= obj->size; i++ )
+                       _recurse_jsonObjectToXML(jsonObjectGetIndex(obj,i), res_xml);
+
+               buffer_add(res_xml,"</array>");
+
+       } else if (obj->type == JSON_HASH) {
+
+               if (hint)
+                       buffer_fadd(res_xml,"<object class_hint=\"%s\">", hint);
+               else
+                       buffer_add(res_xml,"<object>");
+
+               jsonObjectIterator* itr = jsonNewObjectIterator(obj);
+               jsonObject* tmp;
+               while( (tmp = jsonObjectIteratorNext(itr)) ) {
+                       buffer_fadd(res_xml,"<element key=\"%s\">",itr->key);
+                       _recurse_jsonObjectToXML(tmp, res_xml);
+                       buffer_add(res_xml,"</element>");
+               }
+               jsonObjectIteratorFree(itr);
+
+               buffer_add(res_xml,"</object>");
+       }
+
+       if (hint)
+               free(hint);
+
+       return 1;
+}
+
+char* _escape_xml (char* text) {
+       char* out;
+       growing_buffer* b = buffer_init(256);
+       int len = strlen(text);
+       int i;
+       for (i = 0; i < len; i++) {
+               if (text[i] == '&')
+                       buffer_add(b,"&amp;");
+               else if (text[i] == '<')
+                       buffer_add(b,"&lt;");
+               else if (text[i] == '>')
+                       buffer_add(b,"&gt;");
+               else
+                       buffer_add_char(b,text[i]);
+       }
+       out = buffer_data(b);
+       buffer_free(b);
+       return out;
+}
+
+
+jsonObject* jsonParseFile( char* filename ) {
+       if(!filename) return NULL;
+       char* data = file_to_string(filename);
+       jsonObject* o = jsonParseString(data);
+       free(data);
+       return o;
+}
+
+
+
+jsonObject* jsonObjectFindPath( const jsonObject* obj, char* format, ...) {
+       if(!obj || !format || strlen(format) < 1) return NULL;  
+
+       VA_LIST_TO_STRING(format);
+       char* buf = VA_BUF;
+       char* token = NULL;
+       char* t = buf;
+       char* tt; /* strtok storage */
+
+       /* copy the path before strtok_r destroys it */
+       char* pathcopy = strdup(buf);
+
+       /* grab the root of the path */
+       token = strtok_r(t, "/", &tt);
+       if(!token) return NULL;
+
+       /* special case where path starts with //  (start anywhere) */
+       if(strlen(pathcopy) > 2 && pathcopy[0] == '/' && pathcopy[1] == '/') {
+               jsonObject* it = _jsonObjectFindPathRecurse(obj, token, pathcopy + 1);
+               free(pathcopy);
+               return it;
+       }
+
+       free(pathcopy);
+
+       t = NULL;
+       do { 
+               obj = jsonObjectGetKey(obj, token);
+       } while( (token = strtok_r(NULL, "/", &tt)) && obj);
+
+       return jsonObjectClone(obj);
+}
+
+/* --------------------------------------------------------------- */
+
+
+
+jsonObject* _jsonObjectFindPathRecurse(const jsonObject* obj, char* root, char* path) {
+
+       if(!obj || ! root || !path) return NULL;
+
+       /* collect all of the potential objects */
+       jsonObject* arr = __jsonObjectFindPathRecurse(obj, root);
+
+       /* container for fully matching objects */
+       jsonObject* newarr = jsonParseString("[]");
+       int i;
+
+       /* path is just /root or /root/ */
+       if( strlen(root) + 2 >= strlen(path) ) {
+               return arr;
+
+       } else {
+
+               /* gather all of the sub-objects that match the full path */
+               for( i = 0; i < arr->size; i++ ) {
+                       jsonObject* a = jsonObjectGetIndex(arr, i);
+                       jsonObject* thing = jsonObjectFindPath(a , path + strlen(root) + 1); 
+
+                       if(thing) { //jsonObjectPush(newarr, thing);
+               if(thing->type == JSON_ARRAY) {
+               int i;
+                                       for( i = 0; i != thing->size; i++ )
+                                               jsonObjectPush(newarr, jsonObjectClone(jsonObjectGetIndex(thing,i)));
+                                       jsonObjectFree(thing);
+
+                               } else {
+                                       jsonObjectPush(newarr, thing);
+                               }                                               
+                       }
+               }
+       }
+       
+       jsonObjectFree(arr);
+       return newarr;
+}
+
+jsonObject* __jsonObjectFindPathRecurse(const jsonObject* obj, char* root) {
+
+       jsonObject* arr = jsonParseString("[]");
+       if(!obj) return arr;
+
+       int i;
+
+       /* if the current object has a node that matches, add it */
+
+       jsonObject* o = jsonObjectGetKey(obj, root);
+       if(o) jsonObjectPush( arr, jsonObjectClone(o) );
+
+       jsonObject* tmp = NULL;
+       jsonObject* childarr;
+       jsonObjectIterator* itr = jsonNewObjectIterator(obj);
+
+       /* recurse through the children and find all potential nodes */
+       while( (tmp = jsonObjectIteratorNext(itr)) ) {
+               childarr = __jsonObjectFindPathRecurse(tmp, root);
+               if(childarr && childarr->size > 0) {
+                       for( i = 0; i!= childarr->size; i++ ) {
+                               jsonObjectPush( arr, jsonObjectClone(jsonObjectGetIndex(childarr, i)) );
+                       }
+               }
+               jsonObjectFree(childarr);
+       }
+
+       jsonObjectIteratorFree(itr);
+
+       return arr;
+}
+
+
diff --git a/src/libstack/osrf_json_utils.h b/src/libstack/osrf_json_utils.h
new file mode 100644 (file)
index 0000000..76af666
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+Copyright (C) 2006  Georgia Public Library Service 
+Bill Erickson <billserickson@gmail.com>
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU 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.
+*/
+
+
+/* ----------------------------------------------------------------------- */
+/* Clients need not include this file.  These are internal utilities only      */
+/* ----------------------------------------------------------------------- */
+
+#define JSON_EAT_WS(ctx)       \
+       while( ctx->index < ctx->chunksize ) {  \
+               if(!isspace(ctx->chunk[ctx->index])) break; \
+               ctx->index++;   \
+       } \
+       if( ctx->index >= ctx->chunksize ) return 0; \
+       c = ctx->chunk[ctx->index];
+
+#define JSON_CACHE_DATA(ctx, buf, size) \
+       while( (buf->n_used < size) && (ctx->index < ctx->chunksize) ) \
+               buffer_add_char(buf, ctx->chunk[ctx->index++]); 
+
+#define JSON_LOG_MARK __FILE__,__LINE__
+
+#define JSON_NUMBER_CHARS "0123456789.+-e"
+
+
+/* cleans up an object if it is morphing another object, also
+ * verifies that the appropriate storage container exists where appropriate */
+#define JSON_INIT_CLEAR(_obj_, newtype)                \
+       if( _obj_->type == JSON_HASH && newtype != JSON_HASH ) {                        \
+               osrfHashFree(_obj_->value.h);                   \
+               _obj_->value.h = NULL;                                  \
+       } else if( _obj_->type == JSON_ARRAY && newtype != JSON_ARRAY ) {       \
+               osrfListFree(_obj_->value.l);                   \
+               _obj_->value.l = NULL;                                  \
+       } else if( _obj_->type == JSON_STRING && newtype != JSON_STRING ) { \
+               free(_obj_->value.s);                                           \
+               _obj_->value.s = NULL;                                  \
+       } \
+       _obj_->type = newtype;\
+       if( newtype == JSON_HASH && _obj_->value.h == NULL ) {  \
+               _obj_->value.h = osrfNewHash();         \
+               _obj_->value.h->freeItem = _jsonFreeHashItem; \
+       } else if( newtype == JSON_ARRAY && _obj_->value.l == NULL ) {  \
+               _obj_->value.l = osrfNewList();         \
+               _obj_->value.l->freeItem = _jsonFreeListItem;\
+       }                                                                                               \
+
+
+/** 
+ * These are the callbacks through which the top level parser 
+ * builds objects via the push parser
+ */
+void _jsonHandleStartObject(void*);
+void _jsonHandleObjectKey(void*, char* key);
+void _jsonHandleEndObject(void*);
+void _jsonHandleStartArray(void*);
+void _jsonHandleEndArray(void*);
+void _jsonHandleNull(void*);
+void _jsonHandleString(void*, char* string);
+void _jsonHandleBool(void*, int boolval);
+void _jsonHandleNumber(void*, long double num);
+void _jsonHandleError(void*, char* str, ...);
+
+struct jsonInternalParserStruct {
+       jsonParserContext* ctx;
+       jsonObject* obj;
+       jsonObject* current;
+       char* lastkey;
+       void (*handleError) (const char*);
+};
+typedef struct jsonInternalParserStruct jsonInternalParser;
+
+jsonInternalParser* _jsonNewInternalParser();
+void _jsonInternalParserFree(jsonInternalParser* p);
+
+/**
+ * Calls the defined error handler with the given error message.
+ * @return -1
+ */
+int _jsonParserError( jsonParserContext* ctx, char* err, ... );
+
+
+/**
+ *
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleUnicode( jsonParserContext* ctx );
+
+
+/**
+ * @param type 0 for null, 1 for true, 2 for false
+ * @return 0 on continue, 1 if it goes past the end of the string, -1 on error
+ */
+int _jsonParserHandleMatch( jsonParserContext* ctx, int type );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleString( jsonParserContext* ctx );
+
+/**
+ * @return 0 on continue, 1 on end of chunk, -1 on error 
+ */
+int _jsonParserHandleNumber( jsonParserContext* ctx );
+
+
+void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo );
+
+
+/* Utility method. finds any object in the tree that matches the path.  
+       Use this for finding paths that start with '//' */
+jsonObject* _jsonObjectFindPathRecurse( const jsonObject* o, char* root, char* path );
+
+
+/* returns a list of object whose key is 'root'.  These are used as
+       potential objects when doing a // search */
+jsonObject* __jsonObjectFindPathRecurse( const jsonObject* o, char* root );
+
+