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--;
78 snprintf(buf, sizeof(buf), ctx->chunk + pre);
79 ctx->handler->handleError( ctx->userData,
80 "*JSON Parser Error\n - char = %c\n "
81 "- index = %d\n - near => %s\n - %s",
82 ctx->chunk[ctx->index], ctx->index, buf, VA_BUF );
84 JSON_STATE_SET(ctx, JSON_STATE_IS_INVALID);
89 int _jsonParserHandleUnicode( jsonParserContext* ctx ) {
91 /* collect as many of the utf characters as we can in this chunk */
92 JSON_CACHE_DATA(ctx, ctx->utfbuf, 4);
94 /* we ran off the end of the chunk */
95 if( ctx->utfbuf->n_used < 4 ) {
96 JSON_STATE_SET(ctx, JSON_STATE_IN_UTF);
100 ctx->index--; /* push it back to index of the final utf char */
102 /* ----------------------------------------------------------------------- */
103 /* We have all of the escaped unicode data. Write it to the buffer */
104 /* The following chunk is used with permission from
105 * json-c http://oss.metaparadigm.com/json-c/
107 #define hexdigit(x) ( ((x) <= '9') ? (x) - '0' : ((x) & 7) + 9)
108 unsigned char utf_out[4];
109 memset(utf_out, 0, sizeof(utf_out));
110 char* buf = ctx->utfbuf->buf;
112 unsigned int ucs_char =
113 (hexdigit(buf[0] ) << 12) +
114 (hexdigit(buf[1]) << 8) +
115 (hexdigit(buf[2]) << 4) +
118 if (ucs_char < 0x80) {
119 utf_out[0] = ucs_char;
120 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
122 } else if (ucs_char < 0x800) {
123 utf_out[0] = 0xc0 | (ucs_char >> 6);
124 utf_out[1] = 0x80 | (ucs_char & 0x3f);
125 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
128 utf_out[0] = 0xe0 | (ucs_char >> 12);
129 utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
130 utf_out[2] = 0x80 | (ucs_char & 0x3f);
131 OSRF_BUFFER_ADD(ctx->buffer, (char*)utf_out);
133 /* ----------------------------------------------------------------------- */
134 /* ----------------------------------------------------------------------- */
136 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_UTF);
137 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
138 OSRF_BUFFER_RESET(ctx->utfbuf);
144 /* type : 0=null, 1=true, 2=false */
145 int _jsonParserHandleMatch( jsonParserContext* ctx, int type ) {
149 case 0: /* JSON null */
151 /* first see if we have it all first */
152 if( ctx->chunksize > (ctx->index + 3) ) {
153 if( strncasecmp(ctx->chunk + ctx->index, "null", 4) )
154 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
155 if( ctx->handler->handleNull )
156 ctx->handler->handleNull(ctx->userData);
161 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
162 if( ctx->buffer->n_used < 4 ) {
163 JSON_STATE_SET(ctx, JSON_STATE_IN_NULL);
167 if( strncasecmp(ctx->buffer->buf, "null", 4) )
168 return _jsonParserError(ctx, "Invalid JSON 'null' sequence");
169 if( ctx->handler->handleNull )
170 ctx->handler->handleNull(ctx->userData);
173 case 1: /* JSON true */
175 /* see if we have it all first */
176 if( ctx->chunksize > (ctx->index + 3) ) {
177 if( strncasecmp(ctx->chunk + ctx->index, "true", 4) )
178 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
179 if( ctx->handler->handleBool )
180 ctx->handler->handleBool(ctx->userData, 1);
185 JSON_CACHE_DATA(ctx, ctx->buffer, 4);
186 if( ctx->buffer->n_used < 4 ) {
187 JSON_STATE_SET(ctx, JSON_STATE_IN_TRUE);
190 if( strncasecmp( ctx->buffer->buf, "true", 4 ) ) {
191 return _jsonParserError(ctx, "Invalid JSON 'true' sequence");
193 if( ctx->handler->handleBool )
194 ctx->handler->handleBool(ctx->userData, 1);
197 case 2: /* JSON false */
199 /* see if we have it all first */
200 if( ctx->chunksize > (ctx->index + 4) ) {
201 if( strncasecmp(ctx->chunk + ctx->index, "false", 5) )
202 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
203 if( ctx->handler->handleBool )
204 ctx->handler->handleBool(ctx->userData, 0);
209 JSON_CACHE_DATA(ctx, ctx->buffer, 5);
210 if( ctx->buffer->n_used < 5 ) {
211 JSON_STATE_SET(ctx, JSON_STATE_IN_FALSE);
214 if( strncasecmp( ctx->buffer->buf, "false", 5 ) )
215 return _jsonParserError(ctx, "Invalid JSON 'false' sequence");
216 if( ctx->handler->handleBool )
217 ctx->handler->handleBool(ctx->userData, 0);
221 fprintf(stderr, "Invalid type flag\n");
226 ctx->index--; /* set it back to the index of the final sequence character */
227 OSRF_BUFFER_RESET(ctx->buffer);
228 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NULL);
229 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_TRUE);
230 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_FALSE);
236 int _jsonParserHandleString( jsonParserContext* ctx ) {
238 char c = ctx->chunk[ctx->index];
240 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_ESCAPE) ) {
242 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_UTF) ) {
244 return _jsonParserHandleUnicode( ctx );
250 /* handle all of the escape chars */
251 case '\\': OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\\' ); break;
252 case '"' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\"' ); break;
253 case 't' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\t' ); break;
254 case 'b' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\b' ); break;
255 case 'f' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\f' ); break;
256 case 'r' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\r' ); break;
257 case 'n' : OSRF_BUFFER_ADD_CHAR( ctx->buffer, '\n' ); break;
259 ctx->index++; /* progress to the first utf char */
260 return _jsonParserHandleUnicode( ctx );
261 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
265 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_ESCAPE);
272 case '"' : /* this string is ending */
273 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_KEY) ) {
276 if(ctx->handler->handleObjectKey) {
277 ctx->handler->handleObjectKey(
278 ctx->userData, ctx->buffer->buf);
281 } else { /* regular json string */
283 if(ctx->handler->handleString) {
284 ctx->handler->handleString(
285 ctx->userData, ctx->buffer->buf );
290 OSRF_BUFFER_RESET(ctx->buffer); /* flush the buffer and states */
291 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_STRING);
292 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
295 case '\\' : JSON_STATE_SET(ctx, JSON_STATE_IN_ESCAPE); break;
296 default : OSRF_BUFFER_ADD_CHAR( ctx->buffer, c );
303 int _jsonParserHandleNumber( jsonParserContext* ctx ) {
304 char c = ctx->chunk[ctx->index];
307 OSRF_BUFFER_ADD_CHAR(ctx->buffer, c);
308 c = ctx->chunk[++(ctx->index)];
309 } while( strchr(JSON_NUMBER_CHARS, c) && ctx->index < ctx->chunksize );
311 /* if we're run off the end of the chunk and we're not parsing the last chunk,
312 * save the number and the state */
313 if( ctx->index >= ctx->chunksize &&
314 ! JSON_PARSE_FLAG_CHECK(ctx, JSON_PARSE_LAST_CHUNK) ) {
315 JSON_STATE_SET(ctx, JSON_STATE_IN_NUMBER);
319 /* make me more strict */
321 double d = strtod(ctx->buffer->buf, &err);
322 if(err && err[0] != '\0')
323 return _jsonParserError(ctx, "Invalid number sequence");
324 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_NUMBER);
325 OSRF_BUFFER_RESET(ctx->buffer);
326 if(ctx->handler->handleNumber)
327 ctx->handler->handleNumber( ctx->userData, d );
328 ctx->index--; /* scooch back to the first non-digit number */
335 int jsonParseChunk( jsonParserContext* ctx, const char* data, int datalen, int flags ) {
337 if( !( ctx && ctx->handler && data && datalen > 0 )) return -1;
338 ctx->chunksize = datalen;
343 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_INVALID) )
344 return _jsonParserError( ctx, "JSON Parser cannot continue after an error" );
346 if( JSON_STATE_CHECK(ctx, JSON_STATE_IS_DONE) )
347 return _jsonParserError( ctx, "Extra content at end of JSON data" );
349 for( ctx->index = 0; (ctx->index < ctx->chunksize) &&
350 (c = ctx->chunk[ctx->index]); ctx->index++ ) {
352 /* middle of parsing a string */
353 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_STRING)) {
354 if( _jsonParserHandleString(ctx) == -1 )
359 /* middle of parsing a number */
360 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_NUMBER) ) {
361 if( _jsonParserHandleNumber(ctx) == -1 )
367 #ifdef OSRF_JSON_ALLOW_COMMENTS
368 /* we just saw a bare '/' character */
369 if( JSON_STATE_CHECK(ctx, JSON_STATE_START_COMMENT) ) {
371 JSON_STATE_REMOVE(ctx, JSON_STATE_START_COMMENT);
372 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
375 return _jsonParserError( ctx, "Invalid comment initializer" );
379 /* we're currently in the middle of a comment block */
380 if( JSON_STATE_CHECK(ctx, JSON_STATE_IN_COMMENT) ) {
382 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_COMMENT);
383 JSON_STATE_SET(ctx, JSON_STATE_END_COMMENT);
390 /* we're in a comment, and we just saw a '*' character */
391 if( JSON_STATE_CHECK(ctx, JSON_STATE_END_COMMENT) ) {
392 if( c == '/' ) { /* comment is finished */
393 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
396 /* looks like this isn't the end of the comment after all */
397 JSON_STATE_SET(ctx, JSON_STATE_IN_COMMENT);
398 JSON_STATE_REMOVE(ctx, JSON_STATE_END_COMMENT);
403 /* if we're in the middle of parsing a null/true/false sequence */
404 if( JSON_STATE_CHECK(ctx, (JSON_STATE_IN_NULL |
405 JSON_STATE_IN_TRUE | JSON_STATE_IN_FALSE)) ) {
407 int type = (JSON_STATE_CHECK(ctx, JSON_STATE_IN_NULL)) ? 0 :
408 (JSON_STATE_CHECK(ctx, JSON_STATE_IN_TRUE)) ? 1 : 2;
410 if( _jsonParserHandleMatch( ctx, type ) == -1 )
417 /* handle all of the top level characters */
420 case '{' : /* starting an object */
421 if( ctx->handler->handleStartObject)
422 ctx->handler->handleStartObject( ctx->userData );
423 JSON_STATE_PUSH(ctx, JSON_STATE_IN_OBJECT);
424 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
427 case '}' : /* ending an object */
428 if( ctx->handler->handleEndObject)
429 ctx->handler->handleEndObject( ctx->userData );
430 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
432 if( JSON_STATE_PEEK(ctx) == NULL )
433 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
436 case '[' : /* starting an array */
437 if( ctx->handler->handleStartArray )
438 ctx->handler->handleStartArray( ctx->userData );
439 JSON_STATE_PUSH(ctx, JSON_STATE_IN_ARRAY);
442 case ']': /* ending an array */
443 if( ctx->handler->handleEndArray )
444 ctx->handler->handleEndArray( ctx->userData );
446 if( JSON_STATE_PEEK(ctx) == NULL )
447 JSON_STATE_SET(ctx, JSON_STATE_IS_DONE);
450 case ':' : /* done with the object key */
451 JSON_STATE_REMOVE(ctx, JSON_STATE_IN_KEY);
454 case ',' : /* after object or array item */
455 if( JSON_STATE_CHECK_STACK(ctx, JSON_STATE_IN_OBJECT) )
456 JSON_STATE_SET(ctx, JSON_STATE_IN_KEY);
460 case 'N' : /* null */
461 if( _jsonParserHandleMatch( ctx, 0 ) == -1)
467 if( _jsonParserHandleMatch( ctx, 1 ) == -1 )
473 if( _jsonParserHandleMatch( ctx, 2 ) == -1)
478 JSON_STATE_SET(ctx, JSON_STATE_IN_STRING);
481 #ifdef OSRF_JSON_ALLOW_COMMENTS
483 JSON_STATE_SET(ctx, JSON_STATE_START_COMMENT);
488 if( strchr(JSON_NUMBER_CHARS, c) ) {
489 if( _jsonParserHandleNumber( ctx ) == -1 )
492 return _jsonParserError( ctx, "Invalid Token" );
501 jsonInternalParser* _jsonNewInternalParser() {
502 jsonInternalParser* p;
503 OSRF_MALLOC(p, sizeof(jsonInternalParser));
504 p->ctx = jsonNewParser( jsonInternalParserHandler, p );
510 void _jsonInternalParserFree(jsonInternalParser* p) {
512 jsonParserFree(p->ctx);
517 static jsonObject* _jsonParseStringImpl(const char* str, void (*errorHandler) (const char*) ) {
518 jsonInternalParser* parser = _jsonNewInternalParser();
519 parser->handleError = errorHandler;
520 jsonParseChunk( parser->ctx, str, strlen(str), JSON_PARSE_LAST_CHUNK );
521 jsonObject* obj = parser->obj;
522 _jsonInternalParserFree(parser);
526 jsonObject* jsonParseStringHandleError(
527 void (*errorHandler) (const char*), char* str, ... ) {
528 if(!str) return NULL;
529 VA_LIST_TO_STRING(str);
530 return _jsonParseStringImpl(VA_BUF, errorHandler);
533 jsonObject* jsonParseString( const char* str ) {
534 if(!str) return NULL;
535 jsonObject* obj = _jsonParseStringImpl(str, NULL);
536 jsonObject* obj2 = jsonObjectDecodeClass(obj);
541 jsonObject* jsonParseStringRaw( const char* str ) {
542 if(!str) return NULL;
543 return _jsonParseStringImpl(str, NULL);
546 jsonObject* jsonParseStringFmt( const char* str, ... ) {
547 if(!str) return NULL;
548 VA_LIST_TO_STRING(str);
549 return _jsonParseStringImpl(VA_BUF, NULL);
553 #define JSON_SHOVE_ITEM(ctx,type) \
554 jsonInternalParser* p = (jsonInternalParser*) ctx;\
555 _jsonInsertParserItem(p, jsonNewObjectType(type));
557 void _jsonHandleStartObject(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_HASH); }
558 void _jsonHandleStartArray(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_ARRAY); }
559 void _jsonHandleNull(void* ctx) { JSON_SHOVE_ITEM(ctx, JSON_NULL); }
561 void _jsonHandleObjectKey(void* ctx, char* key) {
562 jsonInternalParser* p = (jsonInternalParser*) ctx;
564 p->lastkey = strdup(key);
567 void _jsonHandleEndObject(void* ctx) {
568 jsonInternalParser* p = (jsonInternalParser*) ctx;
569 p->current = p->current->parent;
572 void _jsonHandleEndArray(void* ctx) {
573 jsonInternalParser* p = (jsonInternalParser*) ctx;
574 p->current = p->current->parent;
577 void _jsonHandleString(void* ctx, char* string) {
578 jsonInternalParser* p = (jsonInternalParser*) ctx;
579 _jsonInsertParserItem(p, jsonNewObject(string));
582 void _jsonHandleBool(void* ctx, int boolval) {
583 jsonInternalParser* p = (jsonInternalParser*) ctx;
584 jsonObject* obj = jsonNewObjectType(JSON_BOOL);
585 obj->value.b = boolval;
586 _jsonInsertParserItem(p, obj);
589 void _jsonHandleNumber(void* ctx, double num) {
590 jsonInternalParser* p = (jsonInternalParser*) ctx;
591 _jsonInsertParserItem(p, jsonNewNumberObject(num));
594 void _jsonHandleError(void* ctx, char* str, ...) {
595 jsonInternalParser* p = (jsonInternalParser*) ctx;
596 VA_LIST_TO_STRING(str);
599 p->handleError(VA_BUF);
601 if( jsonClientErrorCallback )
602 jsonClientErrorCallback(VA_BUF);
604 else fprintf(stderr, "%s\n", VA_BUF);
605 jsonObjectFree(p->obj);
610 void _jsonInsertParserItem( jsonInternalParser* p, jsonObject* newo ) {
614 /* new parser, set the new object to our object */
615 p->obj = p->current = newo;
619 /* insert the new object into the current container object */
620 switch(p->current->type) {
621 case JSON_HASH : jsonObjectSetKey(p->current, p->lastkey, newo); break;
622 case JSON_ARRAY: jsonObjectPush(p->current, newo); break;
623 default: fprintf(stderr, "%s:%d -> how?\n", JSON_LOG_MARK);
626 /* if the new object is a container object, make it our current container */
627 if( newo->type == JSON_ARRAY || newo->type == JSON_HASH )