Statistics
| Revision:

root / trunk / plugins / jsonrpc / json_tokener.c @ 1858

History | View | Annotate | Download (13.2 KB)

1
/* 
2
 * $Id: json_tokener.c,v 1.10 2004/07/27 00:42:31 mclark Exp $
3
 *
4
 * Copyright Metaparadigm Pte. Ltd. 2004.
5
 * Michael Clark 
6
 *
7
 * This library is free software; you can redistribute it and/or
8
 * modify it under the terms of the GNU Lesser General Public (LGPL)
9
 * License as published by the Free Software Foundation; either
10
 * version 2.1 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15
 * Lesser General Public License for more details: https://www.gnu.org/
16
 *
17
 */
18

                
19
#include 
20
#include 
21
#include 
22
#include 
23

                
24
#include "bits.h"
25
#include "debug.h"
26
#include "printbuf.h"
27
#include "arraylist.h"
28
#include "json_object.h"
29
#include "json_tokener.h"
30

                
31
#if defined(DARWIN) || defined(WIN32)
32
#undef strndup
33
char *strndup(const char *str, size_t len)
34
{
35
    char *ret = calloc(1, len + 1);
36
    strncpy(ret, str, len);
37
    return ret;
38
}
39
#endif
40

                
41
static struct json_object* json_tokener_do_parse(struct json_tokener *this);
42

                
43
struct json_object* json_tokener_parse(char * s)
44
{
45
  struct json_tokener tok;
46
  struct json_object* obj;
47

                
48
  tok.source = s;
49
  tok.pos = 0;
50
  tok.pb = printbuf_new();
51
  obj = json_tokener_do_parse(&tok);
52
  printbuf_free(tok.pb);
53
  return obj;
54
}
55

                
56
static struct json_object* json_tokener_do_parse(struct json_tokener *this)
57
{
58
  enum json_tokener_state state, saved_state;
59
  enum json_tokener_error err = json_tokener_success;
60
  struct json_object *current = NULL, *obj;
61
  char *obj_field_name = NULL;
62
  char quote_char = '\0';
63
  int deemed_double = 0, start_offset = 0;
64

                
65
  state = json_tokener_state_eatws;
66
  saved_state = json_tokener_state_start;
67

                
68
  char c;
69
  do {
70
    c = this->source[this->pos];
71
    switch(state) {
72

                
73
    case json_tokener_state_eatws:
74
      if(isspace(c)) {
75
        this->pos++;
76
      } else if(c == '/') {
77
        state = json_tokener_state_comment_start;
78
        start_offset = this->pos++;
79
      } else {
80
        state = saved_state;
81
      }
82
      break;
83

                
84
    case json_tokener_state_start:
85
      switch(c) {
86
      case '{':
87
        state = json_tokener_state_eatws;
88
        saved_state = json_tokener_state_object;
89
        current = json_object_new_object();
90
        this->pos++;
91
        break;
92
      case '[':
93
        state = json_tokener_state_eatws;
94
        saved_state = json_tokener_state_array;
95
        current = json_object_new_array();
96
        this->pos++;
97
        break;
98
      case 'N':
99
      case 'n':
100
        state = json_tokener_state_null;
101
        start_offset = this->pos++;
102
        break;
103
      case '"':
104
      case '\'':
105
        quote_char = c;
106
        printbuf_reset(this->pb);
107
        state = json_tokener_state_string;
108
        start_offset = ++this->pos;
109
        break;
110
      case 'T':
111
      case 't':
112
      case 'F':
113
      case 'f':
114
        state = json_tokener_state_boolean;
115
        start_offset = this->pos++;
116
        break;
117
      case '0' ... '9':
118
      case '-':
119
        deemed_double = 0;
120
        state = json_tokener_state_number;
121
        start_offset = this->pos++;
122
        break;
123
      default:
124
        err = json_tokener_error_parse_unexpected;
125
        goto out;
126
      }
127
      break;
128

                
129
    case json_tokener_state_finish:
130
      goto out;
131

                
132
    case json_tokener_state_null:
133
      if(strncasecmp("null", this->source + start_offset,
134
                     this->pos - start_offset)) {
135
        err = json_tokener_error_parse_null;
136
        goto out;
137
      }
138
      if(this->pos - start_offset == 4) {
139
        current = NULL;
140
        saved_state = json_tokener_state_finish;
141
        state = json_tokener_state_eatws;
142
      } else {
143
        this->pos++;
144
      }
145
      break;
146

                
147
    case json_tokener_state_comment_start:
148
      if(c == '*') {
149
        state = json_tokener_state_comment;
150
      } else if(c == '/') {
151
        state = json_tokener_state_comment_eol;
152
      } else {
153
        err = json_tokener_error_parse_comment;
154
        goto out;
155
      }
156
      this->pos++;
157
      break;
158

                
159
    case json_tokener_state_comment:
160
      if(c == '*') state = json_tokener_state_comment_end;
161
      this->pos++;
162
      break;
163

                
164
    case json_tokener_state_comment_eol:
165
      if(c == '\n') {
166
        if(mc_get_debug()) {
167
          char *tmp = strndup(this->source + start_offset,
168
                              this->pos - start_offset);
169
          mc_debug("json_tokener_comment: %s\n", tmp);
170
          free(tmp);
171
        }
172
        state = json_tokener_state_eatws;
173
      }
174
      this->pos++;
175
      break;
176

                
177
    case json_tokener_state_comment_end:
178
      if(c == '/') {
179
        if(mc_get_debug()) {
180
          char *tmp = strndup(this->source + start_offset,
181
                              this->pos - start_offset + 1);
182
          mc_debug("json_tokener_comment: %s\n", tmp);
183
          free(tmp);
184
        }
185
        state = json_tokener_state_eatws;
186
      } else {
187
        state = json_tokener_state_comment;
188
      }
189
      this->pos++;
190
      break;
191

                
192
    case json_tokener_state_string:
193
      if(c == quote_char) {
194
        printbuf_memappend(this->pb, this->source + start_offset,
195
                           this->pos - start_offset);
196
        current = json_object_new_string(this->pb->buf);
197
        saved_state = json_tokener_state_finish;
198
        state = json_tokener_state_eatws;
199
      } else if(c == '\\') {
200
        saved_state = json_tokener_state_string;
201
        state = json_tokener_state_string_escape;
202
      }
203
      this->pos++;
204
      break;
205

                
206
    case json_tokener_state_string_escape:
207
      switch(c) {
208
      case '"':
209
      case '\\':
210
        printbuf_memappend(this->pb, this->source + start_offset,
211
                           this->pos - start_offset - 1);
212
        start_offset = this->pos++;
213
        state = saved_state;
214
        break;
215
      case 'b':
216
      case 'n':
217
      case 'r':
218
      case 't':
219
        printbuf_memappend(this->pb, this->source + start_offset,
220
                           this->pos - start_offset - 1);
221
        if(c == 'b') printbuf_memappend(this->pb, "\b", 1);
222
        else if(c == 'n') printbuf_memappend(this->pb, "\n", 1);
223
        else if(c == 'r') printbuf_memappend(this->pb, "\r", 1);
224
        else if(c == 't') printbuf_memappend(this->pb, "\t", 1);
225
        start_offset = ++this->pos;
226
        state = saved_state;
227
        break;
228
      case 'u':
229
        printbuf_memappend(this->pb, this->source + start_offset,
230
                           this->pos - start_offset - 1);
231
        start_offset = ++this->pos;
232
        state = json_tokener_state_escape_unicode;
233
        break;
234
      default:
235
        err = json_tokener_error_parse_string;
236
        goto out;
237
      }
238
      break;
239

                
240
    case json_tokener_state_escape_unicode:
241
      if(strchr(json_hex_chars, c)) {
242
        this->pos++;
243
        if(this->pos - start_offset == 4) {
244
          unsigned char utf_out[3];
245
          unsigned int ucs_char =
246
            (hexdigit(*(this->source + start_offset)) << 12) +
247
            (hexdigit(*(this->source + start_offset + 1)) << 8) +
248
            (hexdigit(*(this->source + start_offset + 2)) << 4) +
249
            hexdigit(*(this->source + start_offset + 3));
250
          if (ucs_char < 0x80) {
251
            utf_out[0] = ucs_char;
252
            printbuf_memappend(this->pb, (char*) utf_out, 1);
253
          } else if (ucs_char < 0x800) {
254
            utf_out[0] = 0xc0 | (ucs_char >> 6);
255
            utf_out[1] = 0x80 | (ucs_char & 0x3f);
256
            printbuf_memappend(this->pb, (char*) utf_out, 2);
257
          } else {
258
            utf_out[0] = 0xe0 | (ucs_char >> 12);
259
            utf_out[1] = 0x80 | ((ucs_char >> 6) & 0x3f);
260
            utf_out[2] = 0x80 | (ucs_char & 0x3f);
261
            printbuf_memappend(this->pb, (char*) utf_out, 3);
262
          }
263
          start_offset = this->pos;
264
          state = saved_state;
265
        }
266
      } else {
267
        err = json_tokener_error_parse_string;
268
        goto out;
269
      }
270
      break;
271

                
272
    case json_tokener_state_boolean:
273
      if(strncasecmp("true", this->source + start_offset,
274
                 this->pos - start_offset) == 0) {
275
        if(this->pos - start_offset == 4) {
276
          current = json_object_new_boolean(1);
277
          saved_state = json_tokener_state_finish;
278
          state = json_tokener_state_eatws;
279
        } else {
280
          this->pos++;
281
        }
282
      } else if(strncasecmp("false", this->source + start_offset,
283
                        this->pos - start_offset) == 0) {
284
        if(this->pos - start_offset == 5) {
285
          current = json_object_new_boolean(0);
286
          saved_state = json_tokener_state_finish;
287
          state = json_tokener_state_eatws;
288
        } else {
289
          this->pos++;
290
        }
291
      } else {
292
        err = json_tokener_error_parse_boolean;
293
        goto out;
294
      }
295
      break;
296

                
297
    case json_tokener_state_number:
298
      if(!c || !strchr(json_number_chars, c)) {
299
        int numi;
300
        double numd;
301
        char *tmp = strndup(this->source + start_offset,
302
                            this->pos - start_offset);
303
        if(!deemed_double && sscanf(tmp, "%d", &numi) == 1) {
304
          current = json_object_new_int(numi);
305
        } else if(deemed_double && sscanf(tmp, "%lf", &numd) == 1) {
306
          current = json_object_new_double(numd);
307
        } else {
308
          free(tmp);
309
          err = json_tokener_error_parse_number;
310
          goto out;
311
        }
312
        free(tmp);
313
        saved_state = json_tokener_state_finish;
314
        state = json_tokener_state_eatws;
315
      } else {
316
        if(c == '.' || c == 'e') deemed_double = 1;
317
        this->pos++;
318
      }
319
      break;
320

                
321
    case json_tokener_state_array:
322
      if(c == ']') {
323
        this->pos++;
324
        saved_state = json_tokener_state_finish;
325
        state = json_tokener_state_eatws;
326
      } else {
327
        obj = json_tokener_do_parse(this);
328
        if(is_error(obj)) {
329
          err = json_tokener_error_parse_array;
330
          goto out;
331
        }
332
        json_object_array_add(current, obj);
333
        saved_state = json_tokener_state_array_sep;
334
        state = json_tokener_state_eatws;
335
      }
336
      break;
337

                
338
    case json_tokener_state_array_sep:
339
      if(c == ']') {
340
        this->pos++;
341
        saved_state = json_tokener_state_finish;
342
        state = json_tokener_state_eatws;
343
      } else if(c == ',') {
344
        this->pos++;
345
        saved_state = json_tokener_state_array;
346
        state = json_tokener_state_eatws;
347
      } else {
348
        json_object_put(current);
349
        err = json_tokener_error_parse_array;
350
        goto out;
351
      }
352
      break;
353

                
354
    case json_tokener_state_object:
355
      state = json_tokener_state_object_field_start;
356
      start_offset = this->pos;
357
      break;
358

                
359
    case json_tokener_state_object_field_start:
360
      if(c == '}') {
361
        this->pos++;
362
        saved_state = json_tokener_state_finish;
363
        state = json_tokener_state_eatws;
364
      } else if (c == '"' || c == '\'') {
365
        quote_char = c;
366
        printbuf_reset(this->pb);
367
        state = json_tokener_state_object_field;
368
        start_offset = ++this->pos;
369
      } else {
370
        err = json_tokener_error_parse_object;
371
        goto out;
372
      }
373
      break;
374

                
375
    case json_tokener_state_object_field:
376
      if(c == quote_char) {
377
        printbuf_memappend(this->pb, this->source + start_offset,
378
                           this->pos - start_offset);
379
        obj_field_name = strdup(this->pb->buf);
380
        saved_state = json_tokener_state_object_field_end;
381
        state = json_tokener_state_eatws;
382
      } else if(c == '\\') {
383
        saved_state = json_tokener_state_object_field;
384
        state = json_tokener_state_string_escape;
385
      }
386
      this->pos++;
387
      break;
388

                
389
    case json_tokener_state_object_field_end:
390
      if(c == ':') {
391
        this->pos++;
392
        saved_state = json_tokener_state_object_value;
393
        state = json_tokener_state_eatws;
394
      } else {
395
        err = -json_tokener_error_parse_object;
396
        goto out;
397
      }
398
      break;
399

                
400
    case json_tokener_state_object_value:
401
      obj = json_tokener_do_parse(this);
402
      if(is_error(obj)) {
403
        err = json_tokener_error_parse_object;
404
        goto out;
405
      }
406
      json_object_object_add(current, obj_field_name, obj);
407
      free(obj_field_name);
408
      obj_field_name = NULL;
409
      saved_state = json_tokener_state_object_sep;
410
      state = json_tokener_state_eatws;
411
      break;
412

                
413
    case json_tokener_state_object_sep:
414
      if(c == '}') {
415
        this->pos++;
416
        saved_state = json_tokener_state_finish;
417
        state = json_tokener_state_eatws;
418
      } else if(c == ',') {
419
        this->pos++;
420
        saved_state = json_tokener_state_object;
421
        state = json_tokener_state_eatws;
422
      } else {
423
        err = json_tokener_error_parse_object;
424
        goto out;
425
      }
426
      break;
427

                
428
    }
429
  } while(c);
430

                
431
  if(state != json_tokener_state_finish &&
432
     saved_state != json_tokener_state_finish)
433
    err = json_tokener_error_parse_eof;
434

                
435
 out:
436
  free(obj_field_name);
437
  if(err == json_tokener_success) return current;
438
  mc_debug("json_tokener_do_parse: error=%d state=%d char=%c\n",
439
           err, state, c);
440
  json_object_put(current);
441
  return NULL;
442
}