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 if(ctx->handler->handleNumber)
354 if( jsonIsNumeric( ctx->buffer->buf ) )
355 ctx->handler->handleNumber( ctx->userData, ctx->buffer->buf );
357 // The number string is not numeric according to JSON rules.
358 // Scrub it into an acceptable format.
360 char* scrubbed = jsonScrubNumber( ctx->buffer->buf );
362 return _jsonParserError(ctx, "Invalid number sequence");
364 ctx->handler->handleNumber( ctx->userData, scrubbed );
370 ctx->index--; /* scooch back to the first non-digit number */
371 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
372 OSRF_BUFFER_RESET(ctx->buffer);
376 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags ) {
378 if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
379 ctx->chunksize = datalen;
384 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
385 return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
387 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
388 return _jsonParserError( ctx, "Extra content at end of JSON data" );
390 for( ctx->index = 0; (ctx->index < ctx->chunksize) &&
391 (c = ctx->chunk[ctx->index]); ctx->index++ ) {
393 /* middle of parsing a string */
394 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
395 if( _jsonParserHandleString(ctx) == -1 )
400 /* middle of parsing a number */
401 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
402 if( _jsonParserHandleNumber(ctx) == -1 )
408 #ifdef OSRF_JSON_ALLOW_COMMENTS
409 /* we just saw a bare '/' character */
410 if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
412 JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
413 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
416 return _jsonParserError( ctx, "Invalid comment initializer" );
420 /* we're currently in the middle of a comment block */
421 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
423 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
424 JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
431 /* we're in a comment, and we just saw a '*' character */
432 if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
433 if( c == '/' ) { /* comment is finished */
434 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
437 /* looks like this isn't the end of the comment after all */
438 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
439 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
444 /* if we're in the middle of parsing a null/true/false sequence */
445 if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL |
446 JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
448 int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
449 (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
451 if( _jsonParserHandleMatch( ctx, type ) == -1 )
458 /* handle all of the top level characters */
461 case '{' : /* starting an object */
462 if( ctx->handler->handleStartObject)
463 ctx->handler->handleStartObject( ctx->userData );
464 JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
465 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
468 case '}' : /* ending an object */
469 if( ctx->handler->handleEndObject)
470 ctx->handler->handleEndObject( ctx->userData );
471 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
473 if( JSON_STATE_PEEK(ctx) == NULL )
474 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
477 case '[' : /* starting an array */
478 if( ctx->handler->handleStartArray )
479 ctx->handler->handleStartArray( ctx->userData );
480 JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
483 case ']': /* ending an array */
484 if( ctx->handler->handleEndArray )
485 ctx->handler->handleEndArray( ctx->userData );
487 if( JSON_STATE_PEEK(ctx) == NULL )
488 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
491 case ':' : /* done with the object key */
492 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
495 case ',' : /* after object or array item */
496 if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
497 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
501 case 'N' : /* null */
502 if( _jsonParserHandleMatch( ctx, 0 ) == -1)
508 if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
514 if( _jsonParserHandleMatch( ctx, 2 ) == -1)
519 JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
522 #ifdef OSRF_JSON_ALLOW_COMMENTS
524 JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
529 if( strchr(JSON_NUMBER_CHARS, c) ) {
530 if( _jsonParserHandleNumber( ctx ) == -1 )
533 return _jsonParserError( ctx, "Invalid Token" );
542 jsonInternalParser* _jsonNewInternalParser() {
543 jsonInternalParser* p;
545 // Use the static instance of jsonInternalParser,
548 if( staticParserInUse )
549 OSRF_MALLOC(p, sizeof(jsonInternalParser));
552 staticParserInUse = 1;
555 p->ctx = jsonNewParser( &jsonInternalParserHandler, p );
559 p->handleError = NULL;
563 void _jsonInternalParserFree(jsonInternalParser* p) {
565 jsonParserFree(p->ctx);
568 // if the jsonInternalParser was allocated
569 // dynamically, then free it
571 if( &staticParser == p )
572 staticParserInUse = 0;
577 static jsonObject* _jsonParseStringImpl(const char* str, void (*errorHandler) (const char*) ) {
578 jsonInternalParser* parser = _jsonNewInternalParser();
579 parser->handleError = errorHandler;
580 jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK );
581 jsonObject* obj = parser->obj;
582 _jsonInternalParserFree(parser);
586 jsonObject* jsonParseStringHandleError(
587 void (*errorHandler) (const char*), char* str, ... ) {
588 if(!str) return NULL;
589 VA_LIST_TO_STRING(str);
590 return _jsonParseStringImpl(VA_BUF, errorHandler);
593 jsonObject* jsonParseString( const char* str ) {
594 if(!str) return NULL;
595 jsonObject* obj = _jsonParseStringImpl(str, NULL);
596 jsonObject* obj2 = jsonObjectDecodeClass(obj);
601 jsonObject* jsonParseStringRaw( const char* str ) {
602 if(!str) return NULL;
603 return _jsonParseStringImpl(str, NULL);
606 jsonObject* jsonParseStringFmt( const char* str, ... ) {
607 if(!str) return NULL;
608 VA_LIST_TO_STRING(str);
609 return _jsonParseStringImpl(VA_BUF, NULL);
613 #define JSON_SHOVE_ITEM(ctx,type) \
614 jsonInternalParser* p = (jsonInternalParser*) ctx;\
615 _jsonInsertParserItem(p, jsonNewObjectType(type));
617 void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
618 void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
619 void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
621 void _jsonHandleObjectKey(void* ctx, char* key) {
622 jsonInternalParser* p = (jsonInternalParser*) ctx;
624 p->lastkey = strdup(key);
627 void _jsonHandleEndObject(void* ctx) {
628 jsonInternalParser* p = (jsonInternalParser*) ctx;
629 p->current = p->current->parent;
632 void _jsonHandleEndArray(void* ctx) {
633 jsonInternalParser* p = (jsonInternalParser*) ctx;
634 p->current = p->current->parent;
637 void _jsonHandleString(void* ctx, char* string) {
638 jsonInternalParser* p = (jsonInternalParser*) ctx;
639 _jsonInsertParserItem(p, jsonNewObject(string));
642 void _jsonHandleBool(void* ctx, int boolval) {
643 jsonInternalParser* p = (jsonInternalParser*) ctx;
644 jsonObject* obj = jsonNewObjectType(JSON_BOOL);
645 obj->value.b = boolval;
646 _jsonInsertParserItem(p, obj);
649 void _jsonHandleNumber(void* ctx, const char* numstr) {
650 jsonObject* obj = jsonNewNumberStringObject(numstr);
651 jsonInternalParser* p = (jsonInternalParser*) ctx;
652 _jsonInsertParserItem(p, obj);
655 void _jsonHandleError(void* ctx, char* str, ...) {
656 jsonInternalParser* p = (jsonInternalParser*) ctx;
657 VA_LIST_TO_STRING(str);
660 p->handleError(VA_BUF);
662 if( jsonClientErrorCallback )
663 jsonClientErrorCallback(VA_BUF);
665 else fprintf(stderr, "%s\n", VA_BUF);
666 jsonObjectFree(p->obj);
671 void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
675 /* new parser, set the new object to our object */
676 p->obj = p->current = newo;
680 /* insert the new object into the current container object */
681 if(p->current->type == JSON_HASH)
682 jsonObjectSetKey(p->current, p->lastkey, newo);
683 else // assume it's a JSON_ARRAY; if it isn't, it'll become one
684 jsonObjectPush(p->current, newo);
686 /* if the new object is a container object, make it our current container */
687 if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )