2 Copyright (C) 2006 Georgia Public Library Service
3 Bill Erickson <billserickson@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.
16 #include <opensrf/osrf_json.h>
17 #include <opensrf/osrf_json_utils.h>
21 /* if the client sets a global error handler, this will point to it */
22 static void (*jsonClientErrorCallback) (const char*) = NULL;
24 /* these are the handlers for our internal parser */
25 static const jsonParserHandler jsonInternalParserHandler = {
26 _jsonHandleStartObject,
29 _jsonHandleStartArray,
38 static jsonParserContext staticContext;
39 static int staticContextInUse = 0; // boolean
41 static jsonInternalParser staticParser;
42 static int staticParserInUse = 0; // boolean
44 jsonParserContext* jsonNewParser( const jsonParserHandler* handler, void* userData) {
45 jsonParserContext* ctx;
47 // Use the static instance of jsonParserContext,
50 if( staticContextInUse )
51 OSRF_MALLOC(ctx, sizeof(jsonParserContext));
54 staticContextInUse = 1;
57 ctx->stateStack = osrfNewList();
58 ctx->buffer = buffer_init(512);
59 ctx->utfbuf = buffer_init(5);
60 ctx->handler = handler;
66 ctx->userData = userData;
70 void jsonParserFree( jsonParserContext* ctx ) {
72 buffer_free(ctx->buffer);
73 buffer_free(ctx->utfbuf);
74 osrfListFree(ctx->stateStack);
76 // if the jsonParserContext was allocated
77 // dynamically, then free it
79 if( &staticContext == ctx )
80 staticContextInUse = 0;
86 void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) {
87 jsonClientErrorCallback = errorHandler;
91 int _jsonParserError( jsonParserContext* ctx, char* err, ... ) {
92 if( ctx->handler->handleError ) {
93 VA_LIST_TO_STRING(err);
95 // Determine the beginning and ending points of a JSON
96 // fragment to display, from the vicinity of the error
98 int pre = ctx->index - 15;
99 if( pre < 0 ) pre = 0;
100 int post= ctx->index + 15;
101 if( post >= ctx->chunksize ) post = ctx->chunksize - 1;
103 // Copy the fragment into a buffer
105 int len = post - pre + 1; // length of fragment
107 memcpy( buf, ctx->chunk + pre, len );
110 // Issue an error message
112 ctx->handler->handleError( ctx->userData,
113 "*JSON Parser Error\n - char = %c\n "
114 "- index = %d\n - near => %s\n - %s",
115 ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
117 JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
122 int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
124 /* collect as many of the utf characters as we can in this chunk */
125 JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
127 /* we ran off the end of the chunk */
128 if( ctx->utfbuf->n_used < 4 ) {
129 JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
133 ctx->index--; /* push it back to index of the final utf char */
135 /* ----------------------------------------------------------------------- */
136 /* We have all of the escaped unicode data. Write it to the buffer */
137 /* The following chunk is used with permission from
138 * json-c http://oss.metaparadigm.com/json-c/
140 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
141 unsigned char utf_out[4];
142 memset(utf_out, 0, sizeof(utf_out));
143 char* buf = ctx->utfbuf->buf;
145 unsigned int ucs_char =
146 (hexdigit(buf[0] ) << 12) +
147 (hexdigit(buf[1]) << 8) +
148 (hexdigit(buf[2]) << 4) +
151 if (ucs_char < 0x80) {
152 utf_out[0] = ucs_char;
153 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
155 } else if (ucs_char < 0x800) {
156 utf_out[0] = 0xc0 | (ucs_char >> 6);
157 utf_out[1] = 0x80 | (ucs_char & 0x3f);
158 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
161 utf_out[0] = 0xe0 | (ucs_char >> 12);
162 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
163 utf_out[2] = 0x80 | (ucs_char & 0x3f);
164 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
166 /* ----------------------------------------------------------------------- */
167 /* ----------------------------------------------------------------------- */
169 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
170 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
171 OSRF_BUFFER_RESET(ctx->utfbuf);
177 /* type : 0=null, 1=true, 2=false */
178 int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
182 case 0: /* JSON null */
184 /* first see if we have it all first */
185 if( ctx->chunksize > (ctx->index + 3) ) {
186 if( strncasecmp(ctx->chunk + ctx->index, "null", 4) )
187 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
188 if( ctx->handler->handleNull )
189 ctx->handler->handleNull(ctx->userData);
194 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
195 if( ctx->buffer->n_used < 4 ) {
196 JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
200 if( strncasecmp(ctx->buffer->buf, "null", 4) )
201 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
202 if( ctx->handler->handleNull )
203 ctx->handler->handleNull(ctx->userData);
206 case 1: /* JSON true */
208 /* see if we have it all first */
209 if( ctx->chunksize > (ctx->index + 3) ) {
210 if( strncasecmp(ctx->chunk + ctx->index, "true", 4) )
211 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
212 if( ctx->handler->handleBool )
213 ctx->handler->handleBool(ctx->userData, 1);
218 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
219 if( ctx->buffer->n_used < 4 ) {
220 JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
223 if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
224 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
226 if( ctx->handler->handleBool )
227 ctx->handler->handleBool(ctx->userData, 1);
230 case 2: /* JSON false */
232 /* see if we have it all first */
233 if( ctx->chunksize > (ctx->index + 4) ) {
234 if( strncasecmp(ctx->chunk + ctx->index, "false", 5) )
235 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
236 if( ctx->handler->handleBool )
237 ctx->handler->handleBool(ctx->userData, 0);
242 JSON_CACHE_DATA(ctx, ctx->buffer, 5);
243 if( ctx->buffer->n_used < 5 ) {
244 JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
247 if( strncasecmp( ctx->buffer->buf, "false", 5 ) )
248 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
249 if( ctx->handler->handleBool )
250 ctx->handler->handleBool(ctx->userData, 0);
254 fprintf(stderr, "Invalid type flag\n");
259 ctx->index--; /* set it back to the index of the final sequence character */
260 OSRF_BUFFER_RESET(ctx->buffer);
261 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
262 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
263 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
269 int _jsonParserHandleString( jsonParserContext* ctx ) {
271 char c = ctx->chunk[ctx->index];
273 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
275 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
277 return _jsonParserHandleUnicode( ctx );
283 /* handle all of the escape chars */
284 case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
285 case '"' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
286 case 't' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
287 case 'b' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
288 case 'f' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
289 case 'r' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
290 case 'n' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
292 ctx->index++; /* progress to the first utf char */
293 return _jsonParserHandleUnicode( ctx );
294 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
298 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
305 case '"' : /* this string is ending */
306 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
309 if(ctx->handler->handleObjectKey) {
310 ctx->handler->handleObjectKey(
311 ctx->userData, ctx->buffer->buf);
314 } else { /* regular json string */
316 if(ctx->handler->handleString) {
317 ctx->handler->handleString(
318 ctx->userData, ctx->buffer->buf );
323 OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
324 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
325 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
328 case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
329 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
336 int _jsonParserHandleNumber( jsonParserContext* ctx ) {
337 char c = ctx->chunk[ctx->index];
340 OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
341 c = ctx->chunk[++(ctx->index)];
342 } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
344 /* if we're run off the end of the chunk and we're not parsing the last chunk,
345 * save the number and the state */
346 if( ctx->index >= ctx->chunksize &&
347 ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
348 JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
352 /* make me more strict */
354 double d = strtod(ctx->buffer->buf, &err);
355 if(err && err[0] != '\0')
356 return _jsonParserError(ctx, "Invalid number sequence");
357 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
358 OSRF_BUFFER_RESET(ctx->buffer);
359 if(ctx->handler->handleNumber)
360 ctx->handler->handleNumber( ctx->userData, d );
361 ctx->index--; /* scooch back to the first non-digit number */
368 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags ) {
370 if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
371 ctx->chunksize = datalen;
376 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
377 return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
379 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
380 return _jsonParserError( ctx, "Extra content at end of JSON data" );
382 for( ctx->index = 0; (ctx->index < ctx->chunksize) &&
383 (c = ctx->chunk[ctx->index]); ctx->index++ ) {
385 /* middle of parsing a string */
386 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
387 if( _jsonParserHandleString(ctx) == -1 )
392 /* middle of parsing a number */
393 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
394 if( _jsonParserHandleNumber(ctx) == -1 )
400 #ifdef OSRF_JSON_ALLOW_COMMENTS
401 /* we just saw a bare '/' character */
402 if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
404 JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
405 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
408 return _jsonParserError( ctx, "Invalid comment initializer" );
412 /* we're currently in the middle of a comment block */
413 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
415 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
416 JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
423 /* we're in a comment, and we just saw a '*' character */
424 if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
425 if( c == '/' ) { /* comment is finished */
426 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
429 /* looks like this isn't the end of the comment after all */
430 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
431 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
436 /* if we're in the middle of parsing a null/true/false sequence */
437 if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL |
438 JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
440 int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
441 (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
443 if( _jsonParserHandleMatch( ctx, type ) == -1 )
450 /* handle all of the top level characters */
453 case '{' : /* starting an object */
454 if( ctx->handler->handleStartObject)
455 ctx->handler->handleStartObject( ctx->userData );
456 JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
457 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
460 case '}' : /* ending an object */
461 if( ctx->handler->handleEndObject)
462 ctx->handler->handleEndObject( ctx->userData );
463 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
465 if( JSON_STATE_PEEK(ctx) == NULL )
466 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
469 case '[' : /* starting an array */
470 if( ctx->handler->handleStartArray )
471 ctx->handler->handleStartArray( ctx->userData );
472 JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
475 case ']': /* ending an array */
476 if( ctx->handler->handleEndArray )
477 ctx->handler->handleEndArray( ctx->userData );
479 if( JSON_STATE_PEEK(ctx) == NULL )
480 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
483 case ':' : /* done with the object key */
484 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
487 case ',' : /* after object or array item */
488 if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
489 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
493 case 'N' : /* null */
494 if( _jsonParserHandleMatch( ctx, 0 ) == -1)
500 if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
506 if( _jsonParserHandleMatch( ctx, 2 ) == -1)
511 JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
514 #ifdef OSRF_JSON_ALLOW_COMMENTS
516 JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
521 if( strchr(JSON_NUMBER_CHARS, c) ) {
522 if( _jsonParserHandleNumber( ctx ) == -1 )
525 return _jsonParserError( ctx, "Invalid Token" );
534 jsonInternalParser* _jsonNewInternalParser() {
535 jsonInternalParser* p;
537 // Use the static instance of jsonInternalParser,
540 if( staticParserInUse )
541 OSRF_MALLOC(p, sizeof(jsonInternalParser));
544 staticParserInUse = 1;
547 p->ctx = jsonNewParser( &jsonInternalParserHandler, p );
551 p->handleError = NULL;
555 void _jsonInternalParserFree(jsonInternalParser* p) {
557 jsonParserFree(p->ctx);
560 // if the jsonInternalParser was allocated
561 // dynamically, then free it
563 if( &staticParser == p )
564 staticParserInUse = 0;
569 static jsonObject* _jsonParseStringImpl(const char* str, void (*errorHandler) (const char*) ) {
570 jsonInternalParser* parser = _jsonNewInternalParser();
571 parser->handleError = errorHandler;
572 jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK );
573 jsonObject* obj = parser->obj;
574 _jsonInternalParserFree(parser);
578 jsonObject* jsonParseStringHandleError(
579 void (*errorHandler) (const char*), char* str, ... ) {
580 if(!str) return NULL;
581 VA_LIST_TO_STRING(str);
582 return _jsonParseStringImpl(VA_BUF, errorHandler);
585 jsonObject* jsonParseString( const char* str ) {
586 if(!str) return NULL;
587 jsonObject* obj = _jsonParseStringImpl(str, NULL);
588 jsonObject* obj2 = jsonObjectDecodeClass(obj);
593 jsonObject* jsonParseStringRaw( const char* str ) {
594 if(!str) return NULL;
595 return _jsonParseStringImpl(str, NULL);
598 jsonObject* jsonParseStringFmt( const char* str, ... ) {
599 if(!str) return NULL;
600 VA_LIST_TO_STRING(str);
601 return _jsonParseStringImpl(VA_BUF, NULL);
605 #define JSON_SHOVE_ITEM(ctx,type) \
606 jsonInternalParser* p = (jsonInternalParser*) ctx;\
607 _jsonInsertParserItem(p, jsonNewObjectType(type));
609 void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
610 void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
611 void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
613 void _jsonHandleObjectKey(void* ctx, char* key) {
614 jsonInternalParser* p = (jsonInternalParser*) ctx;
616 p->lastkey = strdup(key);
619 void _jsonHandleEndObject(void* ctx) {
620 jsonInternalParser* p = (jsonInternalParser*) ctx;
621 p->current = p->current->parent;
624 void _jsonHandleEndArray(void* ctx) {
625 jsonInternalParser* p = (jsonInternalParser*) ctx;
626 p->current = p->current->parent;
629 void _jsonHandleString(void* ctx, char* string) {
630 jsonInternalParser* p = (jsonInternalParser*) ctx;
631 _jsonInsertParserItem(p, jsonNewObject(string));
634 void _jsonHandleBool(void* ctx, int boolval) {
635 jsonInternalParser* p = (jsonInternalParser*) ctx;
636 jsonObject* obj = jsonNewObjectType(JSON_BOOL);
637 obj->value.b = boolval;
638 _jsonInsertParserItem(p, obj);
641 void _jsonHandleNumber(void* ctx, double num) {
642 jsonInternalParser* p = (jsonInternalParser*) ctx;
643 _jsonInsertParserItem(p, jsonNewNumberObject(num));
646 void _jsonHandleError(void* ctx, char* str, ...) {
647 jsonInternalParser* p = (jsonInternalParser*) ctx;
648 VA_LIST_TO_STRING(str);
651 p->handleError(VA_BUF);
653 if( jsonClientErrorCallback )
654 jsonClientErrorCallback(VA_BUF);
656 else fprintf(stderr, "%s\n", VA_BUF);
657 jsonObjectFree(p->obj);
662 void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
666 /* new parser, set the new object to our object */
667 p->obj = p->current = newo;
671 /* insert the new object into the current container object */
672 switch(p->current->type) {
673 case JSON_HASH : jsonObjectSetKey(p->current, p->lastkey, newo); break;
674 case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
675 default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK);
678 /* if the new object is a container object, make it our current container */
679 if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )