1 #include "json_parser.h"
3 /* keep a copy of the length of the current json string so we don't
4 * have to calculate it in each function
6 int current_strlen; /* XXX need to move this into the function params for thread support */
8 object* json_parse_string(char* string) {
14 current_strlen = strlen(string);
16 if(current_strlen == 0) {
20 object* obj = new_object(NULL);
21 unsigned long index = 0;
23 int status = _json_parse_string(string, &index, obj);
34 int _json_parse_string(char* string, unsigned long* index, object* obj) {
35 assert(string && index && *index < current_strlen);
37 int status = 0; /* return code from parsing routines */
38 char* classname = NULL; /* object class hint */
39 json_eat_ws(string, index, 1); /* remove leading whitespace */
41 char c = string[*index];
43 /* remove any leading comments */
47 (*index)++; /* move to second comment char */
48 status = json_eat_comment(string, index, &classname, 1);
49 if(status) return status;
51 json_eat_ws(string, index, 1);
58 json_eat_ws(string, index, 1); /* remove leading whitespace */
60 if(*index >= current_strlen)
68 status = json_parse_json_string(string, index, obj);
74 status = json_parse_json_array(string, index, obj);
79 status = json_parse_json_object(string, index, obj);
84 status = json_parse_json_null(string, index, obj);
92 status = json_parse_json_bool(string, index, obj);
95 /* we should never get here */
97 if(is_number(c) || c == '.' || c == '-') { /* are we a number? */
98 status = json_parse_json_number(string, index, obj);
99 if(status) return status;
104 return json_handle_error(string, index, "_json_parse_string() final switch clause");
107 if(status) return status;
109 json_eat_ws(string, index, 1);
111 if( *index < current_strlen ) {
112 /* remove any trailing comments */
116 status = json_eat_comment(string, index, NULL, 0);
117 if(status) return status;
122 obj->set_class(obj, classname);
130 int json_parse_json_null(char* string, unsigned long* index, object* obj) {
132 if(*index >= (current_strlen - 3)) {
133 return json_handle_error(string, index,
134 "_parse_json_string(): invalid null" );
137 if(!strncasecmp(string + (*index), "null", 4)) {
142 return json_handle_error(string, index,
143 "_parse_json_string(): invalid null" );
147 /* should be at the first character of the bool at this point */
148 int json_parse_json_bool(char* string, unsigned long* index, object* obj) {
149 assert(string && obj && *index < current_strlen);
151 char* ret = "json_parse_json_bool(): truncated bool";
153 if( *index >= (current_strlen - 5))
154 return json_handle_error(string, index, ret);
156 if(!strncasecmp( string + (*index), "false", 5)) {
161 if( *index >= (current_strlen - 4))
162 return json_handle_error(string, index, ret);
164 if(!strncasecmp( string + (*index), "true", 4)) {
169 return json_handle_error(string, index, ret);
173 /* expecting the first character of the number */
174 int json_parse_json_number(char* string, unsigned long* index, object* obj) {
175 assert(string && obj && *index < current_strlen);
177 growing_buffer* buf = buffer_init(64);
178 char c = string[*index];
183 /* negative number? */
184 if(c == '-') { buffer_add(buf, "-"); (*index)++; }
186 while(*index < current_strlen) {
189 buffer_add_char(buf, c);
191 else if( c == '.' ) {
193 return json_handle_error(string, index,
194 "json_parse_json_number(): malformed json number");
207 obj->double_value = strtod(buf->buf, NULL);
213 obj->num_value = atol(buf->buf);
219 /* index should point to the character directly following the '['. when done
220 * index will point to the character directly following the ']' character
222 int json_parse_json_array(char* string, unsigned long* index, object* obj) {
223 assert(string && obj && index && *index < current_strlen);
226 int in_parse = 0; /* true if this array already contains one item */
228 while(*index < current_strlen) {
230 json_eat_ws(string, index, 1);
232 if(string[*index] == ']') {
238 json_eat_ws(string, index, 1);
239 if(string[*index] != ',') {
240 return json_handle_error(string, index,
241 "json_parse_json_array(): array not followed by a ','");
244 json_eat_ws(string, index, 1);
247 object* item = new_object(NULL);
248 status = _json_parse_string(string, index, item);
250 if(status) return status;
251 obj->push(obj, item);
259 /* index should point to the character directly following the '{'. when done
260 * index will point to the character directly following the '}'
262 int json_parse_json_object(char* string, unsigned long* index, object* obj) {
263 assert(string && obj && index && *index < current_strlen);
267 int in_parse = 0; /* true if we've already added one item to this object */
269 while(*index < current_strlen) {
271 json_eat_ws(string, index, 1);
273 if(string[*index] == '}') {
279 if(string[*index] != ',') {
280 return json_handle_error(string, index,
281 "json_parse_json_object(): object missing ',' betweenn elements" );
284 json_eat_ws(string, index, 1);
287 /* first we grab the hash key */
288 object* key_obj = new_object(NULL);
289 status = _json_parse_string(string, index, key_obj);
290 if(status) return status;
292 if(!key_obj->is_string) {
293 return json_handle_error(string, index,
294 "_json_parse_json_object(): hash key not a string");
297 char* key = key_obj->string_data;
299 json_eat_ws(string, index, 1);
301 if(string[*index] != ':') {
302 return json_handle_error(string, index,
303 "json_parse_json_object(): hash key not followed by ':' character");
308 /* now grab the value object */
309 json_eat_ws(string, index, 1);
310 object* value_obj = new_object(NULL);
311 status = _json_parse_string(string, index, value_obj);
312 if(status) return status;
314 /* put the data into the object and continue */
315 obj->add_key(obj, key, value_obj);
316 free_object(key_obj);
324 /* when done, index will point to the character after the closing quote */
325 int json_parse_json_string(char* string, unsigned long* index, object* obj) {
326 assert(string && index && *index < current_strlen);
330 growing_buffer* buf = buffer_init(64);
332 while(*index < current_strlen) {
334 char c = string[*index];
340 buffer_add(buf, "\\");
348 buffer_add(buf, "\"");
356 buffer_add(buf,"\t");
359 buffer_add_char(buf, c);
364 buffer_add(buf,"\b");
367 buffer_add_char(buf, c);
372 buffer_add(buf,"\f");
375 buffer_add_char(buf, c);
380 buffer_add(buf,"\r");
383 buffer_add_char(buf, c);
388 buffer_add(buf,"\n");
391 buffer_add_char(buf, c);
398 if(*index >= (current_strlen - 4)) {
399 return json_handle_error(string, index,
400 "json_parse_json_string(): truncated escaped unicode"); }
404 memcpy(buff, string + (*index), 4);
407 /* ------------------------------------------------------------------- */
408 /* ------------------------------------------------------------------- */
409 /* This was taken directly from json-c http://oss.metaparadigm.com/json-c/ */
410 unsigned char utf_out[3];
413 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
415 unsigned int ucs_char =
416 (hexdigit(string[*index] ) << 12) +
417 (hexdigit(string[*index + 1]) << 8) +
418 (hexdigit(string[*index + 2]) << 4) +
419 hexdigit(string[*index + 3]);
421 if (ucs_char < 0x80) {
422 utf_out[0] = ucs_char;
423 buffer_add(buf, utf_out);
425 } else if (ucs_char < 0x800) {
426 utf_out[0] = 0xc0 | (ucs_char >> 6);
427 utf_out[1] = 0x80 | (ucs_char & 0x3f);
428 buffer_add(buf, utf_out);
431 utf_out[0] = 0xe0 | (ucs_char >> 12);
432 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
433 utf_out[2] = 0x80 | (ucs_char & 0x3f);
434 buffer_add(buf, utf_out);
436 /* ------------------------------------------------------------------- */
437 /* ------------------------------------------------------------------- */
445 buffer_add_char(buf, c);
451 buffer_add_char(buf, c);
458 obj->set_string(obj, buf->buf);
464 void json_eat_ws(char* string, unsigned long* index, int eat_all) {
465 assert(string && index);
466 if(*index >= current_strlen)
469 if( eat_all ) { /* removes newlines, etc */
470 while(string[*index] == ' ' ||
471 string[*index] == '\n' ||
472 string[*index] == '\t')
477 while(string[*index] == ' ') (*index)++;
481 /* index should be at the '*' character at the beginning of the comment.
482 * when done, index will point to the first character after the final /
484 int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class) {
485 assert(string && index && *index < current_strlen);
487 if(string[*index] != '*' && string[*index] != '/' )
488 return json_handle_error(string, index,
489 "json_eat_comment(): invalid character after /");
491 /* chop out any // style comments */
492 if(string[*index] == '/') {
494 char c = string[*index];
495 while(*index < current_strlen) {
506 int on_star = 0; /* true if we just saw a '*' character */
508 /* we're just past the '*' */
509 if(!parse_class) { /* we're not concerned with class hints */
510 while(*index < current_strlen) {
511 if(string[*index] == '/') {
518 if(string[*index] == '*') on_star = 1;
528 growing_buffer* buf = buffer_init(64);
538 /*--S hint--*/ /* <-- Hints look like this */
541 while(*index < current_strlen) {
542 char c = string[*index];
548 if(third_dash) fourth_dash = 1;
549 else if(in_hint) third_dash = 1;
550 else if(first_dash) second_dash = 1;
556 if(second_dash && !in_hint) {
558 json_eat_ws(string, index, 1);
559 (*index)--; /* this will get incremented at the bottom of the loop */
566 if(second_dash && !in_hint) {
568 json_eat_ws(string, index, 1);
569 (*index)--; /* this will get incremented at the bottom of the loop */
588 buffer_add_char(buf, c);
595 if( buf->n_used > 0 && buffer)
596 *buffer = buffer_data(buf);
602 int is_number(char c) {
619 int json_handle_error(char* string, unsigned long* index, char* err_msg) {
625 strncpy( buf, string + (*index - 30), 59 );
627 strncpy( buf, string, 59 );
630 "\nError parsing json string at charracter %c (code %d) and index %ld\nMsg:\t%s\nNear:\t%s\n\n",
631 string[*index], string[*index], *index, err_msg, buf );