2 Copyright (C) 2005 Georgia Public Library Service
3 Bill Erickson <highfalutin@gmail.com>
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
17 #include "json_parser.h"
19 /* keep a copy of the length of the current json string so we don't
20 * have to calculate it in each function
22 int current_strlen; /* XXX need to move this into the function params for thread support */
25 jsonObject* jsonParseString( char* string) {
26 return json_parse_string( string );
29 jsonObject* jsonParseStringFmt( char* string, ... ) {
30 VA_LIST_TO_STRING(string);
31 return json_parse_string( VA_BUF );
36 //jsonObject* (*jsonParseString) (char* str) = &_jsonParseString;
38 jsonObject* json_parse_string(char* string) {
40 if(string == NULL) return NULL;
42 current_strlen = strlen(string);
44 if(current_strlen == 0)
47 unsigned long index = 0;
49 json_eat_ws(string, &index, 1, current_strlen); /* remove leading whitespace */
50 if(index == current_strlen) return NULL;
52 jsonObject* obj = jsonNewObject(NULL);
54 int status = _json_parse_string(string, &index, obj, current_strlen);
55 if(!status) return obj;
66 int _json_parse_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
67 if( !string || !index || *index >= current_strlen) return -2;
69 int status = 0; /* return code from parsing routines */
70 char* classname = NULL; /* object class hint */
71 json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
73 char c = string[*index];
75 /* remove any leading comments */
79 (*index)++; /* move to second comment char */
80 status = json_eat_comment(string, index, &classname, 1, current_strlen);
81 if(status) return status;
83 json_eat_ws(string, index, 1, current_strlen);
90 json_eat_ws(string, index, 1, current_strlen); /* remove leading whitespace */
92 if(*index >= current_strlen)
100 status = json_parse_json_string(string, index, obj, current_strlen); break;
105 status = json_parse_json_array(string, index, obj, current_strlen);
111 status = json_parse_json_object(string, index, obj, current_strlen);
117 status = json_parse_json_null(string, index, obj, current_strlen);
126 status = json_parse_json_bool(string, index, obj, current_strlen);
130 if(is_number(c) || c == '.' || c == '-') { /* are we a number? */
131 status = json_parse_json_number(string, index, obj, current_strlen);
132 if(status) return status;
137 /* we should never get here */
138 return json_handle_error(string, index, "_json_parse_string() final switch clause");
141 if(status) return status;
143 json_eat_ws(string, index, 1, current_strlen);
145 if( *index < current_strlen ) {
146 /* remove any trailing comments */
150 status = json_eat_comment(string, index, NULL, 0, current_strlen);
151 if(status) return status;
156 jsonObjectSetClass(obj, classname);
164 int json_parse_json_null(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
166 if(*index >= (current_strlen - 3)) {
167 return json_handle_error(string, index,
168 "_parse_json_null(): invalid null" );
171 if(!strncasecmp(string + (*index), "null", 4)) {
173 obj->type = JSON_NULL;
176 return json_handle_error(string, index,
177 "_parse_json_null(): invalid null" );
181 /* should be at the first character of the bool at this point */
182 int json_parse_json_bool(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
183 if( ! string || ! obj || *index >= current_strlen ) return -1;
185 char* ret = "json_parse_json_bool(): truncated bool";
187 if( *index >= (current_strlen - 5))
188 return json_handle_error(string, index, ret);
190 if(!strncasecmp( string + (*index), "false", 5)) {
193 obj->type = JSON_BOOL;
197 if( *index >= (current_strlen - 4))
198 return json_handle_error(string, index, ret);
200 if(!strncasecmp( string + (*index), "true", 4)) {
203 obj->type = JSON_BOOL;
207 return json_handle_error(string, index, ret);
211 /* expecting the first character of the number */
212 int json_parse_json_number(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
213 if( ! string || ! obj || *index >= current_strlen ) return -1;
215 growing_buffer* buf = buffer_init(64);
216 char c = string[*index];
221 /* negative number? */
222 if(c == '-') { buffer_add(buf, "-"); (*index)++; }
226 while(*index < current_strlen) {
229 buffer_add_char(buf, c);
232 else if( c == '.' ) {
235 return json_handle_error(string, index,
236 "json_parse_json_number(): malformed json number");
239 buffer_add_char(buf, c);
249 obj->type = JSON_NUMBER;
250 obj->value.n = strtod(buf->buf, NULL);
255 /* index should point to the character directly following the '['. when done
256 * index will point to the character directly following the ']' character
258 int json_parse_json_array(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
260 if( ! string || ! obj || ! index || *index >= current_strlen ) return -1;
263 int in_parse = 0; /* true if this array already contains one item */
264 obj->type = JSON_ARRAY;
268 while(*index < current_strlen) {
270 json_eat_ws(string, index, 1, current_strlen);
272 if(string[*index] == ']') {
279 json_eat_ws(string, index, 1, current_strlen);
280 if(string[*index] != ',') {
281 return json_handle_error(string, index,
282 "json_parse_json_array(): array item not followed by a ','");
285 json_eat_ws(string, index, 1, current_strlen);
288 jsonObject* item = jsonNewObject(NULL);
290 #ifndef STRICT_JSON_READ
291 if(*index < current_strlen) {
292 if(string[*index] == ',' || string[*index] == ']') {
297 if(!set) status = _json_parse_string(string, index, item, current_strlen);
300 status = _json_parse_string(string, index, item, current_strlen);
303 if(status) { jsonObjectFree(item); return status; }
304 jsonObjectPush(obj, item);
310 return json_handle_error(string, index,
311 "json_parse_json_array(): array not closed");
317 /* index should point to the character directly following the '{'. when done
318 * index will point to the character directly following the '}'
320 int json_parse_json_object(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
321 if( ! string || !obj || ! index || *index >= current_strlen ) return -1;
323 obj->type = JSON_HASH;
325 int in_parse = 0; /* true if we've already added one item to this object */
329 while(*index < current_strlen) {
331 json_eat_ws(string, index, 1, current_strlen);
333 if(string[*index] == '}') {
340 if(string[*index] != ',') {
341 return json_handle_error(string, index,
342 "json_parse_json_object(): object missing ',' between elements" );
345 json_eat_ws(string, index, 1, current_strlen);
348 /* first we grab the hash key */
349 jsonObject* key_obj = jsonNewObject(NULL);
350 status = _json_parse_string(string, index, key_obj, current_strlen);
351 if(status) return status;
353 if(key_obj->type != JSON_STRING) {
354 return json_handle_error(string, index,
355 "_json_parse_json_object(): hash key not a string");
358 char* key = key_obj->value.s;
360 json_eat_ws(string, index, 1, current_strlen);
362 if(string[*index] != ':') {
363 return json_handle_error(string, index,
364 "json_parse_json_object(): hash key not followed by ':' character");
369 /* now grab the value object */
370 json_eat_ws(string, index, 1, current_strlen);
371 jsonObject* value_obj = jsonNewObject(NULL);
373 #ifndef STRICT_JSON_READ
374 if(*index < current_strlen) {
375 if(string[*index] == ',' || string[*index] == '}') {
381 status = _json_parse_string(string, index, value_obj, current_strlen);
384 status = _json_parse_string(string, index, value_obj, current_strlen);
387 if(status) return status;
389 /* put the data into the object and continue */
390 jsonObjectSetKey(obj, key, value_obj);
391 jsonObjectFree(key_obj);
397 return json_handle_error(string, index,
398 "json_parse_json_object(): object not closed");
405 /* when done, index will point to the character after the closing quote */
406 int json_parse_json_string(char* string, unsigned long* index, jsonObject* obj, int current_strlen) {
407 if( ! string || ! index || *index >= current_strlen ) return -1;
411 growing_buffer* buf = buffer_init(64);
413 while(*index < current_strlen) {
415 char c = string[*index];
421 buffer_add(buf, "\\");
429 buffer_add(buf, "\"");
437 buffer_add(buf,"\t");
440 buffer_add_char(buf, c);
445 buffer_add(buf,"\b");
448 buffer_add_char(buf, c);
453 buffer_add(buf,"\f");
456 buffer_add_char(buf, c);
461 buffer_add(buf,"\r");
464 buffer_add_char(buf, c);
469 buffer_add(buf,"\n");
472 buffer_add_char(buf, c);
479 if(*index >= (current_strlen - 4)) {
481 return json_handle_error(string, index,
482 "json_parse_json_string(): truncated escaped unicode"); }
486 memcpy(buff, string + (*index), 4);
489 /* ----------------------------------------------------------------------- */
490 /* ----------------------------------------------------------------------- */
491 /* The following chunk was borrowed with permission from
492 json-c http://oss.metaparadigm.com/json-c/ */
493 unsigned char utf_out[3];
496 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
498 unsigned int ucs_char =
499 (hexdigit(string[*index] ) << 12) +
500 (hexdigit(string[*index + 1]) << 8) +
501 (hexdigit(string[*index + 2]) << 4) +
502 hexdigit(string[*index + 3]);
504 if (ucs_char < 0x80) {
505 utf_out[0] = ucs_char;
506 buffer_add(buf, utf_out);
508 } else if (ucs_char < 0x800) {
509 utf_out[0] = 0xc0 | (ucs_char >> 6);
510 utf_out[1] = 0x80 | (ucs_char & 0x3f);
511 buffer_add(buf, utf_out);
514 utf_out[0] = 0xe0 | (ucs_char >> 12);
515 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
516 utf_out[2] = 0x80 | (ucs_char & 0x3f);
517 buffer_add(buf, utf_out);
519 /* ----------------------------------------------------------------------- */
520 /* ----------------------------------------------------------------------- */
527 buffer_add_char(buf, c);
533 buffer_add_char(buf, c);
540 jsonObjectSetString(obj, buf->buf);
546 void json_eat_ws(char* string, unsigned long* index, int eat_all, int current_strlen) {
547 if( ! string || ! index ) return;
548 if(*index >= current_strlen)
551 if( eat_all ) { /* removes newlines, etc */
552 while(string[*index] == ' ' ||
553 string[*index] == '\n' ||
554 string[*index] == '\t')
559 while(string[*index] == ' ') (*index)++;
563 /* index should be at the '*' character at the beginning of the comment.
564 * when done, index will point to the first character after the final /
566 int json_eat_comment(char* string, unsigned long* index, char** buffer, int parse_class, int current_strlen) {
567 if( ! string || ! index || *index >= current_strlen ) return -1;
570 if(string[*index] != '*' && string[*index] != '/' )
571 return json_handle_error(string, index,
572 "json_eat_comment(): invalid character after /");
574 /* chop out any // style comments */
575 if(string[*index] == '/') {
577 char c = string[*index];
578 while(*index < current_strlen) {
589 int on_star = 0; /* true if we just saw a '*' character */
591 /* we're just past the '*' */
592 if(!parse_class) { /* we're not concerned with class hints */
593 while(*index < current_strlen) {
594 if(string[*index] == '/') {
601 if(string[*index] == '*') on_star = 1;
611 growing_buffer* buf = buffer_init(64);
621 /*--S hint--*/ /* <-- Hints look like this */
624 while(*index < current_strlen) {
625 char c = string[*index];
631 if(third_dash) fourth_dash = 1;
632 else if(in_hint) third_dash = 1;
633 else if(first_dash) second_dash = 1;
639 if(second_dash && !in_hint) {
641 json_eat_ws(string, index, 1, current_strlen);
642 (*index)--; /* this will get incremented at the bottom of the loop */
647 if(second_dash && in_hint) {
648 buffer_add_char(buf, c);
654 if(second_dash && !in_hint) {
656 json_eat_ws(string, index, 1, current_strlen);
657 (*index)--; /* this will get incremented at the bottom of the loop */
662 if(second_dash && in_hint) {
663 buffer_add_char(buf, c);
681 buffer_add_char(buf, c);
688 if( buf->n_used > 0 && buffer)
689 *buffer = buffer_data(buf);
695 int is_number(char c) {
712 int json_handle_error(char* string, unsigned long* index, char* err_msg) {
718 strncpy( buf, string + (*index - 30), 59 );
720 strncpy( buf, string, 59 );
723 "\nError parsing json string at charracter %c "
724 "(code %d) and index %ld\nString length: %d\nMsg:\t%s\nNear:\t%s\nFull String:\t%s\n",
725 string[*index], string[*index], *index, current_strlen, err_msg, buf, string );
731 jsonObject* jsonParseFile( const char* filename ) {
732 return json_parse_file( filename );
735 jsonObject* json_parse_file(const char* filename) {
736 if(!filename) return NULL;
737 char* data = file_to_string(filename);
738 jsonObject* o = json_parse_string(data);