--- /dev/null
+CC = gcc
+HEADER_DIR = ../../include
+CFLAGS = -g -O2 -Wall -fPIC -I $(HEADER_DIR) #-D_REENTRANT
+LDFLAGS = -shared -W1
+LIB_DIR = ../../lib
+LDLIBS = -L . -lobjson -L $(LIB_DIR)
+OBJS = object.o json_parser.o utils.o
+UTIL_DIR = ../utils
+
+all: test
+
+test: lib objson_test.c
+ $(CC) $(CFLAGS) $(LDLIBS) objson_test.c -o $@
+
+lib: $(OBJS)
+ $(CC) $(LDFLAGS) $(OBJS) -o $(LIB_DIR)/libobjson.so
+ cp *.h $(HEADER_DIR)/objson/
+
+object.o: object.h object.c
+ $(CC) -c $(CFLAGS) object.c -o $@
+
+json_parser.o: json_parser.h json_parser.c
+ $(CC) -c $(CFLAGS) json_parser.c -o $@
+
+utils.o: $(HEADER_DIR)/utils.h $(UTIL_DIR)/utils.c
+ $(CC) -c $(CFLAGS) $(UTIL_DIR)/utils.c -o $@
+
+clean:
+ /bin/rm -f *.o test
--- /dev/null
+#include "json_parser.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; /* XXX need to move this into the function params for thread support */
+
+object* json_parse_string(char* string) {
+
+ if(string == NULL) {
+ return NULL;
+ }
+
+ current_strlen = strlen(string);
+
+ if(current_strlen == 0) {
+ return NULL;
+ }
+
+ object* obj = new_object(NULL);
+ unsigned long index = 0;
+
+ int status = _json_parse_string(string, &index, obj);
+ if(!status)
+ return obj;
+
+ if(status == -2)
+ return NULL;
+
+ return NULL;
+}
+
+
+int _json_parse_string(char* string, unsigned long* index, object* obj) {
+ assert(string && index && *index < current_strlen);
+
+ int status = 0; /* return code from parsing routines */
+ char* classname = NULL; /* object class hint */
+ json_eat_ws(string, index, 1); /* 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);
+ if(status) return status;
+
+ json_eat_ws(string, index, 1);
+ c = string[*index];
+ if(c != '/')
+ break;
+ }
+ }
+
+ json_eat_ws(string, index, 1); /* remove leading whitespace */
+
+ if(*index >= current_strlen)
+ return -2;
+
+ switch(c) {
+
+ /* json string */
+ case '"':
+ (*index)++;
+ status = json_parse_json_string(string, index, obj);
+ break;
+
+ /* json array */
+ case '[':
+ (*index)++;
+ status = json_parse_json_array(string, index, obj);
+ break;
+
+ case '{':
+ (*index)++;
+ status = json_parse_json_object(string, index, obj);
+ break;
+
+ case 'n':
+ case 'N':
+ status = json_parse_json_null(string, index, obj);
+ break;
+
+
+ case 'f':
+ case 'F':
+ case 't':
+ case 'T':
+ status = json_parse_json_bool(string, index, obj);
+ break;
+
+ /* we should never get here */
+ default:
+ if(is_number(c) || c == '.' || c == '-') { /* are we a number? */
+ status = json_parse_json_number(string, index, obj);
+ if(status) return status;
+ break;
+ }
+
+ (*index)--;
+ return json_handle_error(string, index, "_json_parse_string() final switch clause");
+ }
+
+ if(status) return status;
+
+ json_eat_ws(string, index, 1);
+
+ if( *index < current_strlen ) {
+ /* remove any trailing comments */
+ c = string[*index];
+ if( c == '/' ) {
+ (*index)++;
+ status = json_eat_comment(string, index, NULL, 0);
+ if(status) return status;
+ }
+ }
+
+ if(classname){
+ obj->set_class(obj, classname);
+ free(classname);
+ }
+
+ return 0;
+}
+
+
+int json_parse_json_null(char* string, unsigned long* index, object* obj) {
+
+ if(*index >= (current_strlen - 3)) {
+ return json_handle_error(string, index,
+ "_parse_json_string(): invalid null" );
+ }
+
+ if(!strncasecmp(string + (*index), "null", 4)) {
+ (*index) += 4;
+ obj->is_null = 1;
+ return 0;
+ } else {
+ return json_handle_error(string, index,
+ "_parse_json_string(): invalid null" );
+ }
+}
+
+/* should be at the first character of the bool at this point */
+int json_parse_json_bool(char* string, unsigned long* index, object* obj) {
+ assert(string && obj && *index < current_strlen);
+
+ 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;
+ return 0;
+ }
+
+ if( *index >= (current_strlen - 4))
+ return json_handle_error(string, index, ret);
+
+ if(!strncasecmp( string + (*index), "true", 4)) {
+ (*index) += 4;
+ 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, object* obj) {
+ assert(string && obj && *index < current_strlen);
+
+ 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)++; }
+
+ while(*index < current_strlen) {
+
+ if(is_number(c))
+ buffer_add_char(buf, c);
+
+ else if( c == '.' ) {
+ if(dot_seen) {
+ return json_handle_error(string, index,
+ "json_parse_json_number(): malformed json number");
+ }
+ dot_seen = 1;
+ } else {
+ done = 1; break;
+ }
+ (*index)++;
+ c = string[*index];
+ if(done) break;
+ }
+
+ if(dot_seen) {
+ obj->is_double = 1;
+ obj->double_value = strtod(buf->buf, NULL);
+ buffer_free(buf);
+ return 0;
+
+ } else {
+ obj->is_number = 1;
+ obj->num_value = atol(buf->buf);
+ 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, object* obj) {
+ assert(string && obj && index && *index < current_strlen);
+
+ int status;
+ int in_parse = 0; /* true if this array already contains one item */
+ obj->is_array = 1;
+ while(*index < current_strlen) {
+
+ json_eat_ws(string, index, 1);
+
+ if(string[*index] == ']') {
+ (*index)++;
+ break;
+ }
+
+ if(in_parse) {
+ json_eat_ws(string, index, 1);
+ if(string[*index] != ',') {
+ return json_handle_error(string, index,
+ "json_parse_json_array(): array not followed by a ','");
+ }
+ (*index)++;
+ json_eat_ws(string, index, 1);
+ }
+
+ object* item = new_object(NULL);
+ status = _json_parse_string(string, index, item);
+
+ if(status) return status;
+ obj->push(obj, item);
+ in_parse = 1;
+ }
+
+ 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, object* obj) {
+ assert(string && obj && index && *index < current_strlen);
+
+ obj->is_hash = 1;
+ int status;
+ int in_parse = 0; /* true if we've already added one item to this object */
+
+ while(*index < current_strlen) {
+
+ json_eat_ws(string, index, 1);
+
+ if(string[*index] == '}') {
+ (*index)++;
+ break;
+ }
+
+ if(in_parse) {
+ if(string[*index] != ',') {
+ return json_handle_error(string, index,
+ "json_parse_json_object(): object missing ',' betweenn elements" );
+ }
+ (*index)++;
+ json_eat_ws(string, index, 1);
+ }
+
+ /* first we grab the hash key */
+ object* key_obj = new_object(NULL);
+ status = _json_parse_string(string, index, key_obj);
+ if(status) return status;
+
+ if(!key_obj->is_string) {
+ return json_handle_error(string, index,
+ "_json_parse_json_object(): hash key not a string");
+ }
+
+ char* key = key_obj->string_data;
+
+ json_eat_ws(string, index, 1);
+
+ 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);
+ object* value_obj = new_object(NULL);
+ status = _json_parse_string(string, index, value_obj);
+ if(status) return status;
+
+ /* put the data into the object and continue */
+ obj->add_key(obj, key, value_obj);
+ free_object(key_obj);
+ in_parse = 1;
+ }
+ return 0;
+}
+
+
+
+/* when done, index will point to the character after the closing quote */
+int json_parse_json_string(char* string, unsigned long* index, object* obj) {
+ assert(string && index && *index < current_strlen);
+
+ 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)) {
+ 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);
+
+
+ /* ------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------- */
+ /* This was taken directly 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, utf_out);
+
+ } else if (ucs_char < 0x800) {
+ utf_out[0] = 0xc0 | (ucs_char >> 6);
+ utf_out[1] = 0x80 | (ucs_char & 0x3f);
+ buffer_add(buf, 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, utf_out);
+ }
+ /* ------------------------------------------------------------------- */
+ /* ------------------------------------------------------------------- */
+
+
+ (*index) += 3;
+ in_escape = 0;
+
+ } else {
+
+ buffer_add_char(buf, c);
+ }
+
+ break;
+
+ default:
+ buffer_add_char(buf, c);
+ }
+
+ (*index)++;
+ if(done) break;
+ }
+
+ obj->set_string(obj, buf->buf);
+ buffer_free(buf);
+ return 0;
+}
+
+
+void json_eat_ws(char* string, unsigned long* index, int eat_all) {
+ assert(string && index);
+ 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) {
+ assert(string && index && *index < current_strlen);
+
+ 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);
+ (*index)--; /* this will get incremented at the bottom of the loop */
+ in_hint = 1;
+ break;
+ }
+
+ case 'E':
+ on_star = 0;
+ if(second_dash && !in_hint) {
+ (*index)++;
+ json_eat_ws(string, index, 1);
+ (*index)--; /* this will get incremented at the bottom of the loop */
+ in_hint = 1;
+ 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\nMsg:\t%s\nNear:\t%s\n\n",
+ string[*index], string[*index], *index, err_msg, buf );
+ return -1;
+}
+
+
--- /dev/null
+/* ---------------------------------------------------------------------------------------
+ JSON parser.
+ * --------------------------------------------------------------------------------------- */
+
+
+#include <stdio.h>
+#include "object.h"
+#include "utils.h"
+
+#ifndef JSON_PARSER_H
+#define JSON_PARSER_H
+
+
+
+
+/* returns NULL on error. if string is NULL, returns an object whose is_null flag
+ * is set to true */
+object* json_parse_string(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, object* obj);
+
+/* returns 0 on success and turns obj into a string object */
+int json_parse_json_string(char* string, unsigned long* index, object* obj);
+
+/* returns 0 on success and turns obj into a number or double object */
+int json_parse_json_number(char* string, unsigned long* index, object* obj);
+
+int json_parse_json_object(char* string, unsigned long* index, object* obj);
+
+/* returns 0 on success and turns object into an array object */
+int json_parse_json_array(char* string, unsigned long* index, object* obj);
+
+/* churns through whitespace and increments index as it goes.
+ * eat_all means we should eat newlines, tabs
+ */
+void json_eat_ws(char* string, unsigned long* index, int eat_all);
+
+int json_parse_json_bool(char* string, unsigned long* index, object* obj);
+
+/* 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);
+
+/* 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, object* obj);
+
+
+#endif
--- /dev/null
+#include "object.h"
+#include "json_parser.h"
+#include <fcntl.h>
+
+
+
+/* ---------------------------------------------------------------------- */
+/* See object.h for function info */
+/* ---------------------------------------------------------------------- */
+
+object* new_object(char* string_value) {
+ return _init_object(string_value);
+}
+
+object* _init_object(char* string_value) {
+
+ object* obj = (object*) safe_malloc(sizeof(object));
+ obj->size = 0;
+ obj->data = NULL;
+
+ obj->push = &object_push;
+ obj->add_index = &object_add_index;
+ obj->add_key = &object_add_key;
+ obj->get_index = &object_get_index;
+ obj->get_key = &object_get_key;
+ obj->get_string = &object_get_string;
+ obj->set_string = &object_set_string;
+ obj->set_number = &object_set_number;
+ obj->set_class = &object_set_class;
+ obj->set_double = &object_set_double;
+ obj->remove_index = &object_remove_index;
+ obj->remove_key = &object_remove_key;
+ obj->to_json = &object_to_json;
+ obj->set_comment = &object_set_comment;
+
+ if(string_value) {
+ obj->is_string = 1;
+ obj->string_data = strdup(string_value);
+ } else
+ obj->is_null = 1;
+
+ return obj;
+}
+
+object_node* new_object_node(object* obj) {
+ object_node* node = (object_node*) safe_malloc(sizeof(object_node));
+ node->item = obj;
+ node->next = NULL;
+ node->index = -1;
+ return node;
+}
+
+unsigned long object_push(object* obj, object* new_obj) {
+ assert(obj != NULL);
+ object_clear_type(obj);
+ obj->is_array = 1;
+
+ if(new_obj == NULL) {
+ new_obj = new_object(NULL);
+ new_obj->is_null = 1;
+ }
+
+ object_node* node = new_object_node(new_obj);
+ node->index = obj->size++;
+
+ if( obj->size > MAX_OBJECT_NODES )
+ return -1;
+
+ if(obj->data == NULL) {
+ obj->data = node;
+
+ } else {
+ /* append the node onto the end */
+ object_node* tmp = obj->data;
+ while(tmp) {
+ if(tmp->next == NULL) break;
+ tmp = tmp->next;
+ }
+ tmp->next = node;
+ }
+ return obj->size;
+}
+
+unsigned long object_add_index(object* obj, unsigned long index, object* new_obj) {
+ assert(obj != NULL && index <= MAX_OBJECT_NODES);
+ object_clear_type(obj);
+ obj->is_array = 1;
+
+ if(obj->size <= index)
+ obj->size = index + 1;
+
+ if(new_obj == NULL) {
+ new_obj = new_object(NULL);
+ new_obj->is_null = 1;
+ }
+
+ object_node* node = new_object_node(new_obj);
+ node->index = index;
+
+ if( obj->data == NULL ) {
+ obj->data = node;
+
+ } else {
+
+ if(obj->data->index == index) {
+ object_node* tmp = obj->data->next;
+ free_object_node(obj->data);
+ obj->data = node;
+ node->next = tmp;
+
+ } else {
+
+ object_node* prev = obj->data;
+ object_node* cur = prev->next;
+ int inserted = 0;
+
+ while(cur != NULL) {
+
+ /* replace an existing node */
+ if( cur->index == index ) {
+ object_node* tmp = cur->next;
+ free_object_node(cur);
+ node->next = tmp;
+ prev->next = node;
+ inserted = 1;
+ break;
+
+ /* instert between two nodes */
+ } else if( prev->index < index && cur->index > index ) {
+ prev->next = node;
+ node->next = cur;
+ inserted = 1;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ /* shove on to the end */
+ if(!inserted)
+ prev->next = node;
+ }
+ }
+
+ return obj->size;
+}
+
+
+void object_shift_index(object* obj, unsigned long index) {
+ assert(obj && index <= MAX_OBJECT_NODES);
+ if(obj->data == NULL) {
+ obj->size = 0;
+ return;
+ }
+
+ object_node* data = obj->data;
+ while(data) {
+ if(data->index >= index)
+ data->index--;
+ data = data->next;
+ }
+ obj->size--;
+}
+
+unsigned long object_remove_index(object* obj, unsigned long index) {
+ assert(obj != NULL && index <= MAX_OBJECT_NODES);
+ if(obj->data == NULL) return 0;
+
+ /* removing the first item in the list */
+ if(obj->data->index == index) {
+ object_node* tmp = obj->data->next;
+ free_object_node(obj->data);
+ obj->data = tmp;
+ object_shift_index(obj,index);
+ return obj->size;
+ }
+
+
+ object_node* prev = obj->data;
+ object_node* cur = prev->next;
+
+ while(cur) {
+ if(cur->index == index) {
+ object_node* tmp = cur->next;
+ free_object_node(cur);
+ prev->next = tmp;
+ object_shift_index(obj,index);
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ return obj->size;
+}
+
+
+unsigned long object_remove_key(object* obj, char* key) {
+ assert(obj && key);
+ if(obj->data == NULL) return 0;
+
+ /* removing the first item in the list */
+ if(!strcmp(obj->data->key, key)) {
+ object_node* tmp = obj->data->next;
+ free_object_node(obj->data);
+ obj->data = tmp;
+ if(!obj->data)
+ obj->size = 0;
+
+ return obj->size;
+ }
+
+ object_node* prev = obj->data;
+ object_node* cur = prev->next;
+
+ while(cur) {
+ if(!strcmp(cur->key,key)) {
+ object_node* tmp = cur->next;
+ free_object_node(cur);
+ prev->next = tmp;
+ obj->size--;
+ break;
+ }
+ prev = cur;
+ cur = cur->next;
+ }
+
+ return obj->size;
+}
+
+
+unsigned long object_add_key(object* obj, char* key, object* new_obj) {
+
+ assert(obj != NULL && key != NULL);
+ object_clear_type(obj);
+ obj->is_hash = 1;
+
+
+ if(new_obj == NULL) {
+ new_obj = new_object(NULL);
+ new_obj->is_null = 1;
+ }
+
+ object_node* node = new_object_node(new_obj);
+ node->key = strdup(key);
+
+ if( obj->data == NULL ) {
+ obj->data = node;
+ obj->size++;
+
+ } else {
+
+ /* replace the first node */
+ if(!strcmp(obj->data->key, key)) {
+ object_node* tmp = obj->data->next;
+ free_object_node(obj->data);
+ obj->data = node;
+ node->next = tmp;
+
+ } else {
+
+ object_node* prev = obj->data;
+ object_node* cur = prev->next;
+ int inserted = 0;
+
+ while(cur != NULL) {
+
+ /* replace an existing node */
+ if( !strcmp(cur->key, key) ) {
+ object_node* tmp = cur->next;
+ free_object_node(cur);
+ node->next = tmp;
+ prev->next = node;
+ inserted = 1;
+ break;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ }
+
+ /* shove on to the end */
+ if(!inserted) {
+ prev->next = node;
+ obj->size++;
+ }
+ }
+ }
+
+ return obj->size;
+}
+
+
+void free_object(object* obj) {
+
+ if(obj == NULL) return;
+ if(obj->classname) free(obj->classname);
+ if(obj->comment) free(obj->comment);
+
+ while(obj->data) {
+ object_node* tmp = obj->data->next;
+ free_object_node(obj->data);
+ obj->data = tmp;
+ }
+
+ if(obj->string_data)
+ free(obj->string_data);
+ free(obj);
+}
+
+void free_object_node(object_node* node) {
+ if(node == NULL) return;
+ if(node->key) free(node->key);
+ free_object(node->item);
+ free(node);
+}
+
+object* object_get_index( object* obj, unsigned long index ) {
+ assert(obj != NULL && index <= MAX_OBJECT_NODES);
+ object_node* node = obj->data;
+ while(node) {
+ if(node->index == index)
+ return node->item;
+ node = node->next;
+ }
+ return NULL;
+}
+
+object* object_get_key( object* obj, char* key ) {
+ assert(obj && key);
+ object_node* node = obj->data;
+
+ while(node) {
+ if(node->key && !strcmp(node->key, key))
+ return node->item;
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+char* object_get_string(object* obj) {
+ assert(obj != NULL);
+ return obj->string_data;
+}
+
+void object_set_string(object* obj, char* string) {
+ assert(obj);
+ object_clear_type(obj);
+ obj->is_string = 1;
+ if(string)
+ obj->string_data = strdup(string);
+}
+
+
+void object_set_number(object* obj, long num) {
+ assert(obj);
+ object_clear_type(obj);
+ obj->is_number = 1;
+ obj->num_value = num;
+}
+
+void object_set_double(object* obj, double num) {
+ assert(obj);
+ object_clear_type(obj);
+ obj->is_double = 1;
+ obj->double_value = num;
+}
+
+
+void object_set_class(object* obj, char* classname) {
+ assert(obj && classname);
+ obj->classname = strdup(classname);
+}
+
+
+
+char* object_to_json(object* 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, "--*/");
+ }
+
+ if(obj->is_bool && obj->bool_value)
+ buffer_add(buf, "true");
+
+ else if(obj->is_bool && ! obj->bool_value)
+ buffer_add(buf, "false");
+
+ else if(obj->is_number) {
+ char b[128];
+ memset(b, 0, 128);
+ sprintf(b, "%ld", obj->num_value);
+ buffer_add(buf, b);
+ }
+
+ else if(obj->is_double) {
+ char b[128];
+ memset(b, 0, 128);
+ sprintf(b, "%lf", obj->double_value);
+ buffer_add(buf, b);
+ }
+
+ if(obj->is_null)
+ buffer_add(buf, "null");
+
+ else if (obj->is_string) {
+
+ buffer_add(buf, "\"");
+ char* data = obj->string_data;
+ int len = strlen(data);
+
+ char* output = uescape(data, len, 1);
+ buffer_add(buf, output);
+ free(output);
+ buffer_add(buf, "\"");
+
+ } else if(obj->is_array) {
+
+ buffer_add(buf, "[");
+ int i;
+ for( i = 0; i!= obj->size; i++ ) {
+ char* data = object_to_json(obj->get_index(obj,i));
+ buffer_add(buf, data);
+ free(data);
+ if(i != obj->size - 1)
+ buffer_add(buf, ",");
+ }
+ buffer_add(buf, "]");
+
+ } else if(obj->is_hash) {
+ buffer_add(buf, "{");
+ object_iterator* itr = new_iterator(obj);
+ object_node* tmp;
+ while( (tmp = itr->next(itr)) ) {
+ buffer_add(buf, "\"");
+ buffer_add(buf, tmp->key);
+ buffer_add(buf, "\":");
+ char* data = object_to_json(tmp->item);
+ buffer_add(buf, data);
+ if(itr->has_next(itr))
+ buffer_add(buf, ",");
+ free(data);
+ }
+ free_iterator(itr);
+ buffer_add(buf, "}");
+ }
+
+ /* close out the object hint */
+ if(obj->classname) {
+ buffer_add(buf, "/*--E ");
+ buffer_add(buf, obj->classname);
+ buffer_add(buf, "--*/");
+ }
+
+ if(obj->comment) {
+ buffer_add(buf, " /*");
+ buffer_add(buf, obj->comment);
+ buffer_add(buf, "*/");
+ }
+
+ char* data = buffer_data(buf);
+ buffer_free(buf);
+ return data;
+
+}
+
+
+void object_clear_type(object* obj) {
+ if(obj == NULL) return;
+ obj->is_string = 0;
+ obj->is_hash = 0;
+ obj->is_array = 0;
+ obj->is_bool = 0;
+ obj->is_null = 0;
+}
+
+
+void object_set_comment(object* obj, char* com) {
+ assert(obj && com);
+ obj->comment = strdup(com);
+}
+
+
+
+/* ---------------------------------------------------------------------- */
+/* Iterator */
+
+object_iterator* new_iterator(object* obj) {
+ object_iterator* iter = safe_malloc(sizeof(object_iterator));
+ iter->obj = obj;
+ iter->current = obj->data;
+ iter->next = &object_iterator_next;
+ iter->has_next = &object_iterator_has_next;
+ return iter;
+}
+
+object_node* object_iterator_next(object_iterator* itr) {
+ assert( itr != NULL );
+
+ object_node* tmp = itr->current;
+ if(tmp == NULL) return NULL;
+ itr->current = itr->current->next;
+
+ return tmp;
+}
+
+void free_iterator(object_iterator* iter) {
+ if(iter == NULL) return;
+ free(iter);
+}
+
+int object_iterator_has_next(object_iterator* itr) {
+ assert(itr);
+ if(itr->current) return 1;
+ return 0;
+}
+
+
--- /dev/null
+/* ---------------------------------------------------------------------------------------
+ Generic object framework for C. An object can be either a string, boolean, null,
+ number, array or hash (think Perl hash, dictionary, etc.).
+ * --------------------------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <assert.h>
+#include "utils.h"
+
+/* does we need this? */
+#define MAX_OBJECT_NODES 1000000
+
+#ifndef OBJECT_H
+#define OBJECT_H
+
+/* top leve generic object structuure */
+struct object_struct {
+
+ /* how many sub-objects do we contain. Note that this includes null
+ * array elements in sparse arrays */
+ unsigned long size;
+
+ /* optional class hint */
+ char* classname;
+
+ /* these determine how we define a given object */
+ int is_array;
+ int is_hash;
+ int is_string;
+ int is_null;
+ int is_bool;
+ int is_number;
+ int is_double;
+
+ /* attached accessor/mutator methods for the OO inclined*/
+ unsigned long (*push) (struct object_struct* src, struct object_struct*);
+ unsigned long (*add_index) (struct object_struct* src, unsigned long index, struct object_struct*);
+ unsigned long (*add_key) (struct object_struct* src, char* key, struct object_struct*);
+ struct object_struct* (*get_index) (struct object_struct*, unsigned long index);
+ struct object_struct* (*get_key) (struct object_struct*, char* key);
+ void (*set_string) (struct object_struct*, char*);
+ void (*set_number) (struct object_struct*, long number);
+ void (*set_double) (struct object_struct*, double number);
+ void (*set_class) (struct object_struct*, char* classname);
+ unsigned long (*remove_index) (struct object_struct*, unsigned long index);
+ unsigned long (*remove_key) (struct object_struct*, char* key);
+ char* (*get_string) (struct object_struct*);
+ char* (*to_json) (struct object_struct*);
+ void (*set_comment) (struct object_struct*, char* com);
+
+ /* our list of sub-objects */
+ struct object_node_struct* data;
+
+ /* if we're a string, here's our data */
+ char* string_data;
+
+ /* if we're a boolean value, here's our value */
+ int bool_value;
+
+ /* if we're a number, here's our value */
+ long num_value;
+
+ /* if we're a double, here's our value */
+ double double_value;
+
+ /* client may provide a comment string which will be
+ * added serialized object when applicable
+ */
+ char* comment;
+};
+typedef struct object_struct object;
+
+
+/* this contains a single element of the object along with the elements
+ * index (if this object is an array) and key (if this object is a hash)
+ */
+struct object_node_struct {
+ unsigned long index; /* our array position */
+ char* key; /* our hash key */
+ object* item; /* our object */
+ struct object_node_struct* next; /* pointer to the next object node */
+};
+typedef struct object_node_struct object_node;
+
+/* utility object for iterating over hash objects */
+struct object_iterator_struct {
+ object* obj; /* the topic object */
+ object_node* current; /* the current node within the object */
+ object_node* (*next) (struct object_iterator_struct*);
+ int (*has_next) (struct object_iterator_struct*);
+};
+typedef struct object_iterator_struct object_iterator;
+
+/* allocates a new iterator */
+object_iterator* new_iterator(object* obj);
+
+/* de-allocates an iterator */
+void free_iterator(object_iterator*);
+
+/* returns the object_node currently pointed to by the iterator
+ * and increments the pointer to the next node
+ */
+object_node* object_iterator_next(object_iterator*);
+
+/* returns true if there is another node after the node
+ * currently pointed to
+ */
+int object_iterator_has_next(object_iterator*);
+
+
+/* allocates a new object. classname is optional */
+object* new_object(char* string);
+
+/* utility method for initing an object */
+object* _init_object();
+
+/* returns a pointer to the object at the given index */
+object* object_get_index( object* obj, unsigned long index );
+
+
+/* returns a pointer to the object with the given key */
+object* object_get_key( object* obj, char* key );
+
+/* de-allocates a object */
+void free_object(object*);
+
+/* allocates a new object node */
+object_node* new_object_node(object* obj);
+
+/* de-allocates a object node */
+void free_object_node(object_node*);
+
+/* pushes the given object onto the end of the list,
+ * returns the size on success, -1 on error
+ */
+unsigned long object_push(object*, object* obj);
+
+/* removes the object at the given index (if one exists) and inserts
+ * the new one. returns the size on success, -1 on error
+ */
+unsigned long object_add_index(object*, unsigned long index, object* obj);
+
+/* inserts the new object, overwriting any previous object with the given key
+ * returns the size on success, -1 on error
+ * if 'obj' is NULL, a new object is inserted at key 'key' with 'is_null'
+ * set to true
+ */
+unsigned long object_add_key(object*, char* key, object* obj);
+
+/* 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 object_remove_index(object*, unsigned long index);
+
+/* de-allocates the object at index 'index' and sets the field to null.
+ * this will *not* change the object size */
+//unsigned long object_clear_index(object*, unsigned long index);
+
+/* removes the object with key 'key' if it exists */
+unsigned long object_remove_key(object*, char* key);
+
+/* returns a pointer to the string data held by this object */
+char* object_get_string(object*);
+
+/* sets the string data */
+void object_set_string(object*, char* string);
+
+/* sets the number value for the object */
+void object_set_number(object*, long num);
+
+/* sets the double value for this object */
+void object_set_double(object*, double num);
+
+/* sets the class hint for this object */
+void object_set_class(object*, char* classname);
+
+/* converts an object to a json string. client is responsible for freeing the return string */
+char* object_to_json(object*);
+
+/* utility function. clears all of the is_* flags */
+void object_clear_type(object*);
+
+/* set this object's comment string */
+void object_set_comment(object*, char*);
+
+/* starting at index 'index', shifts all indices down by one and
+ * decrements the objects size by 1
+ */
+void object_shift_index(object*, unsigned long index);
+
+
+
+#endif