--- /dev/null
+# --------------------------------------------------------------
+# 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
+
+
--- /dev/null
+/*
+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;
+}
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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
--- /dev/null
+/*
+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;
+}
+
+
--- /dev/null
+/*
+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;
+ }
+}
+
+
--- /dev/null
+/*
+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,"&");
+ else if (text[i] == '<')
+ buffer_add(b,"<");
+ else if (text[i] == '>')
+ buffer_add(b,">");
+ 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;
+}
+
+
--- /dev/null
+/*
+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 );
+
+