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 jsonParserHandler jsonInternalParserHandlerStruct = {
26 _jsonHandleStartObject,
29 _jsonHandleStartArray,
37 static jsonParserHandler*
38 jsonInternalParserHandler = &jsonInternalParserHandlerStruct;
41 jsonParserContext* jsonNewParser( jsonParserHandler* handler, void* userData) {
42 jsonParserContext* ctx;
43 OSRF_MALLOC(ctx, sizeof(jsonParserContext));
44 ctx->stateStack = osrfNewList();
45 ctx->buffer = buffer_init(512);
46 ctx->utfbuf = buffer_init(5);
47 ctx->handler = handler;
51 ctx->userData = userData;
55 void jsonParserFree( jsonParserContext* ctx ) {
57 buffer_free(ctx->buffer);
58 buffer_free(ctx->utfbuf);
59 osrfListFree(ctx->stateStack);
64 void jsonSetGlobalErrorHandler(void (*errorHandler) (const char*)) {
65 jsonClientErrorCallback = errorHandler;
69 int _jsonParserError( jsonParserContext* ctx, char* err, ... ) {
70 if( ctx->handler->handleError ) {
71 VA_LIST_TO_STRING(err);
72 int pre = ctx->index - 15;
73 int post= ctx->index + 15;
74 while( pre < 0 ) pre++;
75 while( post >= ctx->chunksize ) post--;
79 snprintf(buf, l, ctx->chunk + pre);
80 ctx->handler->handleError( ctx->userData,
81 "*JSON Parser Error\n - char = %c\n "
82 "- index = %d\n - near => %s\n - %s",
83 ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
85 JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
90 int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
92 /* collect as many of the utf characters as we can in this chunk */
93 JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
95 /* we ran off the end of the chunk */
96 if( ctx->utfbuf->n_used < 4 ) {
97 JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
101 ctx->index--; /* push it back to index of the final utf char */
103 /* ----------------------------------------------------------------------- */
104 /* We have all of the escaped unicode data. Write it to the buffer */
105 /* The following chunk is used with permission from
106 * json-c http://oss.metaparadigm.com/json-c/
108 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
109 unsigned char utf_out[4];
111 char* buf = ctx->utfbuf->buf;
113 unsigned int ucs_char =
114 (hexdigit(buf[0] ) << 12) +
115 (hexdigit(buf[1]) << 8) +
116 (hexdigit(buf[2]) << 4) +
119 if (ucs_char < 0x80) {
120 utf_out[0] = ucs_char;
121 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
123 } else if (ucs_char < 0x800) {
124 utf_out[0] = 0xc0 | (ucs_char >> 6);
125 utf_out[1] = 0x80 | (ucs_char & 0x3f);
126 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
129 utf_out[0] = 0xe0 | (ucs_char >> 12);
130 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
131 utf_out[2] = 0x80 | (ucs_char & 0x3f);
132 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
134 /* ----------------------------------------------------------------------- */
135 /* ----------------------------------------------------------------------- */
137 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
138 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
139 OSRF_BUFFER_RESET(ctx->utfbuf);
145 /* type : 0=null, 1=true, 2=false */
146 int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
150 case 0: /* JSON null */
152 /* first see if we have it all first */
153 if( ctx->chunksize > (ctx->index + 3) ) {
154 if( strncasecmp(ctx->chunk + ctx->index, "null", 4) )
155 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
156 if( ctx->handler->handleNull )
157 ctx->handler->handleNull(ctx->userData);
162 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
163 if( ctx->buffer->n_used < 4 ) {
164 JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
168 if( strncasecmp(ctx->buffer->buf, "null", 4) )
169 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
170 if( ctx->handler->handleNull )
171 ctx->handler->handleNull(ctx->userData);
174 case 1: /* JSON true */
176 /* see if we have it all first */
177 if( ctx->chunksize > (ctx->index + 3) ) {
178 if( strncasecmp(ctx->chunk + ctx->index, "true", 4) )
179 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
180 if( ctx->handler->handleBool )
181 ctx->handler->handleBool(ctx->userData, 1);
186 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
187 if( ctx->buffer->n_used < 4 ) {
188 JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
191 if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
192 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
194 if( ctx->handler->handleBool )
195 ctx->handler->handleBool(ctx->userData, 1);
198 case 2: /* JSON false */
200 /* see if we have it all first */
201 if( ctx->chunksize > (ctx->index + 4) ) {
202 if( strncasecmp(ctx->chunk + ctx->index, "false", 5) )
203 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
204 if( ctx->handler->handleBool )
205 ctx->handler->handleBool(ctx->userData, 0);
210 JSON_CACHE_DATA(ctx, ctx->buffer, 5);
211 if( ctx->buffer->n_used < 5 ) {
212 JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
215 if( strncasecmp( ctx->buffer->buf, "false", 5 ) )
216 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
217 if( ctx->handler->handleBool )
218 ctx->handler->handleBool(ctx->userData, 0);
222 fprintf(stderr, "Invalid type flag\n");
227 ctx->index--; /* set it back to the index of the final sequence character */
228 OSRF_BUFFER_RESET(ctx->buffer);
229 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
230 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
231 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
237 int _jsonParserHandleString( jsonParserContext* ctx ) {
239 char c = ctx->chunk[ctx->index];
241 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
243 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
245 return _jsonParserHandleUnicode( ctx );
251 /* handle all of the escape chars */
252 case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
253 case '"' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
254 case 't' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
255 case 'b' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
256 case 'f' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
257 case 'r' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
258 case 'n' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
260 ctx->index++; /* progress to the first utf char */
261 return _jsonParserHandleUnicode( ctx );
262 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
266 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
273 case '"' : /* this string is ending */
274 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
277 if(ctx->handler->handleObjectKey) {
278 ctx->handler->handleObjectKey(
279 ctx->userData, ctx->buffer->buf);
282 } else { /* regular json string */
284 if(ctx->handler->handleString) {
285 ctx->handler->handleString(
286 ctx->userData, ctx->buffer->buf );
291 OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
292 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
293 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
296 case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
297 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
304 int _jsonParserHandleNumber( jsonParserContext* ctx ) {
305 char c = ctx->chunk[ctx->index];
308 OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
309 c = ctx->chunk[++(ctx->index)];
310 } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
312 /* if we're run off the end of the chunk and we're not parsing the last chunk,
313 * save the number and the state */
314 if( ctx->index >= ctx->chunksize &&
315 ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
316 JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
320 /* make me more strict */
322 double d = strtod(ctx->buffer->buf, &err);
323 if(err && err[0] != '\0')
324 return _jsonParserError(ctx, "Invalid number sequence");
325 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
326 OSRF_BUFFER_RESET(ctx->buffer);
327 if(ctx->handler->handleNumber)
328 ctx->handler->handleNumber( ctx->userData, d );
329 ctx->index--; /* scooch back to the first non-digit number */
336 int jsonParseChunk( jsonParserContext* ctx, char* data, int datalen, int flags ) {
338 if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
339 ctx->chunksize = datalen;
344 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
345 return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
347 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
348 return _jsonParserError( ctx, "Extra content at end of JSON data" );
350 for( ctx->index = 0; (ctx->index < ctx->chunksize) &&
351 (c = ctx->chunk[ctx->index]); ctx->index++ ) {
353 /* middle of parsing a string */
354 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
355 if( _jsonParserHandleString(ctx) == -1 )
360 /* middle of parsing a number */
361 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
362 if( _jsonParserHandleNumber(ctx) == -1 )
368 #ifdef OSRF_JSON_ALLOW_COMMENTS
369 /* we just saw a bare '/' character */
370 if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
372 JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
373 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
376 return _jsonParserError( ctx, "Invalid comment initializer" );
380 /* we're currently in the middle of a comment block */
381 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
383 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
384 JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
391 /* we're in a comment, and we just saw a '*' character */
392 if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
393 if( c == '/' ) { /* comment is finished */
394 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
397 /* looks like this isn't the end of the comment after all */
398 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
399 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
404 /* if we're in the middle of parsing a null/true/false sequence */
405 if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL |
406 JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
408 int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
409 (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
411 if( _jsonParserHandleMatch( ctx, type ) == -1 )
418 /* handle all of the top level characters */
421 case '{' : /* starting an object */
422 if( ctx->handler->handleStartObject)
423 ctx->handler->handleStartObject( ctx->userData );
424 JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
425 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
428 case '}' : /* ending an object */
429 if( ctx->handler->handleEndObject)
430 ctx->handler->handleEndObject( ctx->userData );
431 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
433 if( JSON_STATE_PEEK(ctx) == NULL )
434 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
437 case '[' : /* starting an array */
438 if( ctx->handler->handleStartArray )
439 ctx->handler->handleStartArray( ctx->userData );
440 JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
443 case ']': /* ending an array */
444 if( ctx->handler->handleEndArray )
445 ctx->handler->handleEndArray( ctx->userData );
447 if( JSON_STATE_PEEK(ctx) == NULL )
448 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
451 case ':' : /* done with the object key */
452 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
455 case ',' : /* after object or array item */
456 if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
457 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
461 case 'N' : /* null */
462 if( _jsonParserHandleMatch( ctx, 0 ) == -1)
468 if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
474 if( _jsonParserHandleMatch( ctx, 2 ) == -1)
479 JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
482 #ifdef OSRF_JSON_ALLOW_COMMENTS
484 JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
489 if( strchr(JSON_NUMBER_CHARS, c) ) {
490 if( _jsonParserHandleNumber( ctx ) == -1 )
493 return _jsonParserError( ctx, "Invalid Token" );
502 jsonInternalParser* _jsonNewInternalParser() {
503 jsonInternalParser* p;
504 OSRF_MALLOC(p, sizeof(jsonInternalParser));
505 p->ctx = jsonNewParser( jsonInternalParserHandler, p );
511 void _jsonInternalParserFree(jsonInternalParser* p) {
513 jsonParserFree(p->ctx);
518 static jsonObject* _jsonParseStringImpl(char* str, void (*errorHandler) (const char*) ) {
519 jsonInternalParser* parser = _jsonNewInternalParser();
520 parser->handleError = errorHandler;
521 jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK );
522 jsonObject* obj = parser->obj;
523 _jsonInternalParserFree(parser);
527 jsonObject* jsonParseStringHandleError(
528 void (*errorHandler) (const char*), char* str, ... ) {
529 if(!str) return NULL;
530 VA_LIST_TO_STRING(str);
531 return _jsonParseStringImpl(VA_BUF, errorHandler);
534 jsonObject* jsonParseString( char* str ) {
535 if(!str) return NULL;
536 jsonObject* obj = _jsonParseStringImpl(str, NULL);
537 jsonObject* obj2 = jsonObjectDecodeClass(obj);
542 jsonObject* jsonParseStringRaw( char* str ) {
543 if(!str) return NULL;
544 return _jsonParseStringImpl(str, NULL);
547 jsonObject* jsonParseStringFmt( char* str, ... ) {
548 if(!str) return NULL;
549 VA_LIST_TO_STRING(str);
550 return _jsonParseStringImpl(VA_BUF, NULL);
554 #define JSON_SHOVE_ITEM(ctx,type) \
555 jsonInternalParser* p = (jsonInternalParser*) ctx;\
556 _jsonInsertParserItem(p, jsonNewObjectType(type));
558 void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
559 void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
560 void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
562 void _jsonHandleObjectKey(void* ctx, char* key) {
563 jsonInternalParser* p = (jsonInternalParser*) ctx;
565 p->lastkey = strdup(key);
568 void _jsonHandleEndObject(void* ctx) {
569 jsonInternalParser* p = (jsonInternalParser*) ctx;
570 p->current = p->current->parent;
573 void _jsonHandleEndArray(void* ctx) {
574 jsonInternalParser* p = (jsonInternalParser*) ctx;
575 p->current = p->current->parent;
578 void _jsonHandleString(void* ctx, char* string) {
579 jsonInternalParser* p = (jsonInternalParser*) ctx;
580 _jsonInsertParserItem(p, jsonNewObject(string));
583 void _jsonHandleBool(void* ctx, int boolval) {
584 jsonInternalParser* p = (jsonInternalParser*) ctx;
585 jsonObject* obj = jsonNewObjectType(JSON_BOOL);
586 obj->value.b = boolval;
587 _jsonInsertParserItem(p, obj);
590 void _jsonHandleNumber(void* ctx, double num) {
591 jsonInternalParser* p = (jsonInternalParser*) ctx;
592 _jsonInsertParserItem(p, jsonNewNumberObject(num));
595 void _jsonHandleError(void* ctx, char* str, ...) {
596 jsonInternalParser* p = (jsonInternalParser*) ctx;
597 VA_LIST_TO_STRING(str);
600 p->handleError(VA_BUF);
602 if( jsonClientErrorCallback )
603 jsonClientErrorCallback(VA_BUF);
605 else fprintf(stderr, "%s\n", VA_BUF);
606 jsonObjectFree(p->obj);
611 void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
615 /* new parser, set the new object to our object */
616 p->obj = p->current = newo;
620 /* insert the new object into the current container object */
621 switch(p->current->type) {
622 case JSON_HASH : jsonObjectSetKey(p->current, p->lastkey, newo); break;
623 case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
624 default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK);
627 /* if the new object is a container object, make it our current container */
628 if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )