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);
94 int pre = ctx->index - 15;
95 int post= ctx->index + 15;
96 while( pre < 0 ) pre++;
97 while( post >= ctx->chunksize ) post--;
100 snprintf(buf, sizeof(buf), ctx->chunk + pre);
101 ctx->handler->handleError( ctx->userData,
102 "*JSON Parser Error\n - char = %c\n "
103 "- index = %d\n - near => %s\n - %s",
104 ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
106 JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
111 int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
113 /* collect as many of the utf characters as we can in this chunk */
114 JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
116 /* we ran off the end of the chunk */
117 if( ctx->utfbuf->n_used < 4 ) {
118 JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
122 ctx->index--; /* push it back to index of the final utf char */
124 /* ----------------------------------------------------------------------- */
125 /* We have all of the escaped unicode data. Write it to the buffer */
126 /* The following chunk is used with permission from
127 * json-c http://oss.metaparadigm.com/json-c/
129 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
130 unsigned char utf_out[4];
131 memset(utf_out, 0, sizeof(utf_out));
132 char* buf = ctx->utfbuf->buf;
134 unsigned int ucs_char =
135 (hexdigit(buf[0] ) << 12) +
136 (hexdigit(buf[1]) << 8) +
137 (hexdigit(buf[2]) << 4) +
140 if (ucs_char < 0x80) {
141 utf_out[0] = ucs_char;
142 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
144 } else if (ucs_char < 0x800) {
145 utf_out[0] = 0xc0 | (ucs_char >> 6);
146 utf_out[1] = 0x80 | (ucs_char & 0x3f);
147 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
150 utf_out[0] = 0xe0 | (ucs_char >> 12);
151 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
152 utf_out[2] = 0x80 | (ucs_char & 0x3f);
153 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
155 /* ----------------------------------------------------------------------- */
156 /* ----------------------------------------------------------------------- */
158 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
159 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
160 OSRF_BUFFER_RESET(ctx->utfbuf);
166 /* type : 0=null, 1=true, 2=false */
167 int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
171 case 0: /* JSON null */
173 /* first see if we have it all first */
174 if( ctx->chunksize > (ctx->index + 3) ) {
175 if( strncasecmp(ctx->chunk + ctx->index, "null", 4) )
176 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
177 if( ctx->handler->handleNull )
178 ctx->handler->handleNull(ctx->userData);
183 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
184 if( ctx->buffer->n_used < 4 ) {
185 JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
189 if( strncasecmp(ctx->buffer->buf, "null", 4) )
190 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
191 if( ctx->handler->handleNull )
192 ctx->handler->handleNull(ctx->userData);
195 case 1: /* JSON true */
197 /* see if we have it all first */
198 if( ctx->chunksize > (ctx->index + 3) ) {
199 if( strncasecmp(ctx->chunk + ctx->index, "true", 4) )
200 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
201 if( ctx->handler->handleBool )
202 ctx->handler->handleBool(ctx->userData, 1);
207 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
208 if( ctx->buffer->n_used < 4 ) {
209 JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
212 if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
213 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
215 if( ctx->handler->handleBool )
216 ctx->handler->handleBool(ctx->userData, 1);
219 case 2: /* JSON false */
221 /* see if we have it all first */
222 if( ctx->chunksize > (ctx->index + 4) ) {
223 if( strncasecmp(ctx->chunk + ctx->index, "false", 5) )
224 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
225 if( ctx->handler->handleBool )
226 ctx->handler->handleBool(ctx->userData, 0);
231 JSON_CACHE_DATA(ctx, ctx->buffer, 5);
232 if( ctx->buffer->n_used < 5 ) {
233 JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
236 if( strncasecmp( ctx->buffer->buf, "false", 5 ) )
237 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
238 if( ctx->handler->handleBool )
239 ctx->handler->handleBool(ctx->userData, 0);
243 fprintf(stderr, "Invalid type flag\n");
248 ctx->index--; /* set it back to the index of the final sequence character */
249 OSRF_BUFFER_RESET(ctx->buffer);
250 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
251 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
252 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
258 int _jsonParserHandleString( jsonParserContext* ctx ) {
260 char c = ctx->chunk[ctx->index];
262 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
264 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
266 return _jsonParserHandleUnicode( ctx );
272 /* handle all of the escape chars */
273 case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
274 case '"' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
275 case 't' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
276 case 'b' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
277 case 'f' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
278 case 'r' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
279 case 'n' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
281 ctx->index++; /* progress to the first utf char */
282 return _jsonParserHandleUnicode( ctx );
283 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
287 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
294 case '"' : /* this string is ending */
295 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
298 if(ctx->handler->handleObjectKey) {
299 ctx->handler->handleObjectKey(
300 ctx->userData, ctx->buffer->buf);
303 } else { /* regular json string */
305 if(ctx->handler->handleString) {
306 ctx->handler->handleString(
307 ctx->userData, ctx->buffer->buf );
312 OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
313 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
314 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
317 case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
318 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
325 int _jsonParserHandleNumber( jsonParserContext* ctx ) {
326 char c = ctx->chunk[ctx->index];
329 OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
330 c = ctx->chunk[++(ctx->index)];
331 } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
333 /* if we're run off the end of the chunk and we're not parsing the last chunk,
334 * save the number and the state */
335 if( ctx->index >= ctx->chunksize &&
336 ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
337 JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
341 /* make me more strict */
343 double d = strtod(ctx->buffer->buf, &err);
344 if(err && err[0] != '\0')
345 return _jsonParserError(ctx, "Invalid number sequence");
346 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
347 OSRF_BUFFER_RESET(ctx->buffer);
348 if(ctx->handler->handleNumber)
349 ctx->handler->handleNumber( ctx->userData, d );
350 ctx->index--; /* scooch back to the first non-digit number */
357 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags ) {
359 if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
360 ctx->chunksize = datalen;
365 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
366 return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
368 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
369 return _jsonParserError( ctx, "Extra content at end of JSON data" );
371 for( ctx->index = 0; (ctx->index < ctx->chunksize) &&
372 (c = ctx->chunk[ctx->index]); ctx->index++ ) {
374 /* middle of parsing a string */
375 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
376 if( _jsonParserHandleString(ctx) == -1 )
381 /* middle of parsing a number */
382 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
383 if( _jsonParserHandleNumber(ctx) == -1 )
389 #ifdef OSRF_JSON_ALLOW_COMMENTS
390 /* we just saw a bare '/' character */
391 if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
393 JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
394 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
397 return _jsonParserError( ctx, "Invalid comment initializer" );
401 /* we're currently in the middle of a comment block */
402 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
404 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
405 JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
412 /* we're in a comment, and we just saw a '*' character */
413 if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
414 if( c == '/' ) { /* comment is finished */
415 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
418 /* looks like this isn't the end of the comment after all */
419 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
420 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
425 /* if we're in the middle of parsing a null/true/false sequence */
426 if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL |
427 JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
429 int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
430 (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
432 if( _jsonParserHandleMatch( ctx, type ) == -1 )
439 /* handle all of the top level characters */
442 case '{' : /* starting an object */
443 if( ctx->handler->handleStartObject)
444 ctx->handler->handleStartObject( ctx->userData );
445 JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
446 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
449 case '}' : /* ending an object */
450 if( ctx->handler->handleEndObject)
451 ctx->handler->handleEndObject( ctx->userData );
452 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
454 if( JSON_STATE_PEEK(ctx) == NULL )
455 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
458 case '[' : /* starting an array */
459 if( ctx->handler->handleStartArray )
460 ctx->handler->handleStartArray( ctx->userData );
461 JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
464 case ']': /* ending an array */
465 if( ctx->handler->handleEndArray )
466 ctx->handler->handleEndArray( ctx->userData );
468 if( JSON_STATE_PEEK(ctx) == NULL )
469 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
472 case ':' : /* done with the object key */
473 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
476 case ',' : /* after object or array item */
477 if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
478 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
482 case 'N' : /* null */
483 if( _jsonParserHandleMatch( ctx, 0 ) == -1)
489 if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
495 if( _jsonParserHandleMatch( ctx, 2 ) == -1)
500 JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
503 #ifdef OSRF_JSON_ALLOW_COMMENTS
505 JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
510 if( strchr(JSON_NUMBER_CHARS, c) ) {
511 if( _jsonParserHandleNumber( ctx ) == -1 )
514 return _jsonParserError( ctx, "Invalid Token" );
523 jsonInternalParser* _jsonNewInternalParser() {
524 jsonInternalParser* p;
526 // Use the static instance of jsonInternalParser,
529 if( staticParserInUse )
530 OSRF_MALLOC(p, sizeof(jsonInternalParser));
533 staticParserInUse = 1;
536 p->ctx = jsonNewParser( &jsonInternalParserHandler, p );
540 p->handleError = NULL;
544 void _jsonInternalParserFree(jsonInternalParser* p) {
546 jsonParserFree(p->ctx);
549 // if the jsonInternalParser was allocated
550 // dynamically, then free it
552 if( &staticParser == p )
553 staticParserInUse = 0;
558 static jsonObject* _jsonParseStringImpl(const char* str, void (*errorHandler) (const char*) ) {
559 jsonInternalParser* parser = _jsonNewInternalParser();
560 parser->handleError = errorHandler;
561 jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK );
562 jsonObject* obj = parser->obj;
563 _jsonInternalParserFree(parser);
567 jsonObject* jsonParseStringHandleError(
568 void (*errorHandler) (const char*), char* str, ... ) {
569 if(!str) return NULL;
570 VA_LIST_TO_STRING(str);
571 return _jsonParseStringImpl(VA_BUF, errorHandler);
574 jsonObject* jsonParseString( const char* str ) {
575 if(!str) return NULL;
576 jsonObject* obj = _jsonParseStringImpl(str, NULL);
577 jsonObject* obj2 = jsonObjectDecodeClass(obj);
582 jsonObject* jsonParseStringRaw( const char* str ) {
583 if(!str) return NULL;
584 return _jsonParseStringImpl(str, NULL);
587 jsonObject* jsonParseStringFmt( const char* str, ... ) {
588 if(!str) return NULL;
589 VA_LIST_TO_STRING(str);
590 return _jsonParseStringImpl(VA_BUF, NULL);
594 #define JSON_SHOVE_ITEM(ctx,type) \
595 jsonInternalParser* p = (jsonInternalParser*) ctx;\
596 _jsonInsertParserItem(p, jsonNewObjectType(type));
598 void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
599 void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
600 void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
602 void _jsonHandleObjectKey(void* ctx, char* key) {
603 jsonInternalParser* p = (jsonInternalParser*) ctx;
605 p->lastkey = strdup(key);
608 void _jsonHandleEndObject(void* ctx) {
609 jsonInternalParser* p = (jsonInternalParser*) ctx;
610 p->current = p->current->parent;
613 void _jsonHandleEndArray(void* ctx) {
614 jsonInternalParser* p = (jsonInternalParser*) ctx;
615 p->current = p->current->parent;
618 void _jsonHandleString(void* ctx, char* string) {
619 jsonInternalParser* p = (jsonInternalParser*) ctx;
620 _jsonInsertParserItem(p, jsonNewObject(string));
623 void _jsonHandleBool(void* ctx, int boolval) {
624 jsonInternalParser* p = (jsonInternalParser*) ctx;
625 jsonObject* obj = jsonNewObjectType(JSON_BOOL);
626 obj->value.b = boolval;
627 _jsonInsertParserItem(p, obj);
630 void _jsonHandleNumber(void* ctx, double num) {
631 jsonInternalParser* p = (jsonInternalParser*) ctx;
632 _jsonInsertParserItem(p, jsonNewNumberObject(num));
635 void _jsonHandleError(void* ctx, char* str, ...) {
636 jsonInternalParser* p = (jsonInternalParser*) ctx;
637 VA_LIST_TO_STRING(str);
640 p->handleError(VA_BUF);
642 if( jsonClientErrorCallback )
643 jsonClientErrorCallback(VA_BUF);
645 else fprintf(stderr, "%s\n", VA_BUF);
646 jsonObjectFree(p->obj);
651 void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
655 /* new parser, set the new object to our object */
656 p->obj = p->current = newo;
660 /* insert the new object into the current container object */
661 switch(p->current->type) {
662 case JSON_HASH : jsonObjectSetKey(p->current, p->lastkey, newo); break;
663 case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
664 default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK);
667 /* if the new object is a container object, make it our current container */
668 if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )