1 /*-
2 * Copyright (c) 2010 Alistair Crooks <agc@NetBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25 #include <sys/types.h>
26
27 #include <inttypes.h>
28 #include <regex.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include "mj.h"
36 #include "defs.h"
37
38 #define JSON_ESCAPE '\xac'
39 #define JSON_INDENT 4
40
41 /*
42 * save 'n' chars of 's' in malloc'd memory
43 *
44 * optionally encode embedded quotes and null bytes
45 */
46 static char *
strnsave(const char * s,int n,int encoded)47 strnsave(const char *s, int n, int encoded)
48 {
49 char *newc;
50 char *cp;
51 int i;
52
53 if (n < 0) {
54 n = (int)strlen(s);
55 }
56 NEWARRAY(char, cp, n + n + 1, "strnsave", return NULL);
57 switch (encoded) {
58 case MJ_JSON_ENCODE:
59 newc = cp;
60 for (i = 0 ; i < n ; i++) {
61 if (*s == JSON_ESCAPE) {
62 *newc++ = JSON_ESCAPE;
63 *newc++ = '1';
64 s += 1;
65 } else if (*s == '"') {
66 *newc++ = JSON_ESCAPE;
67 *newc++ = '2';
68 s += 1;
69 } else if (*s == 0x0) {
70 *newc++ = JSON_ESCAPE;
71 *newc++ = '0';
72 s += 1;
73 } else {
74 *newc++ = *s++;
75 }
76 }
77 *newc = 0x0;
78 break;
79 default:
80 (void) memcpy(cp, s, (unsigned)n);
81 cp[n] = 0x0;
82 break;
83 }
84 return cp;
85 }
86
87 /* look in an object for the item */
88 static int
findentry(mj_t * atom,const char * name,const unsigned from,const unsigned incr)89 findentry(mj_t *atom, const char *name, const unsigned from, const unsigned incr)
90 {
91 unsigned i;
92
93 for (i = from ; i < atom->c ; i += incr) {
94 if (strcmp(name, atom->value.v[i].value.s) == 0) {
95 return i;
96 }
97 }
98 return -1;
99 }
100
101 /* create a real number */
102 static void
create_number(mj_t * atom,double d)103 create_number(mj_t *atom, double d)
104 {
105 char number[128];
106
107 atom->type = MJ_NUMBER;
108 atom->c = snprintf(number, sizeof(number), "%g", d);
109 atom->value.s = strnsave(number, (int)atom->c, MJ_HUMAN);
110 }
111
112 /* create an integer */
113 static void
create_integer(mj_t * atom,int64_t i)114 create_integer(mj_t *atom, int64_t i)
115 {
116 char number[128];
117
118 atom->type = MJ_NUMBER;
119 atom->c = snprintf(number, sizeof(number), "%" PRIi64, i);
120 atom->value.s = strnsave(number, (int)atom->c, MJ_HUMAN);
121 }
122
123 /* create a string */
124 static void
create_string(mj_t * atom,const char * s,ssize_t len)125 create_string(mj_t *atom, const char *s, ssize_t len)
126 {
127 atom->type = MJ_STRING;
128 atom->value.s = strnsave(s, (int)len, MJ_JSON_ENCODE);
129 atom->c = (unsigned)strlen(atom->value.s);
130 }
131
132 #define MJ_OPEN_BRACKET (MJ_OBJECT + 1) /* 8 */
133 #define MJ_CLOSE_BRACKET (MJ_OPEN_BRACKET + 1) /* 9 */
134 #define MJ_OPEN_BRACE (MJ_CLOSE_BRACKET + 1) /* 10 */
135 #define MJ_CLOSE_BRACE (MJ_OPEN_BRACE + 1) /* 11 */
136 #define MJ_COLON (MJ_CLOSE_BRACE + 1) /* 12 */
137 #define MJ_COMMA (MJ_COLON + 1) /* 13 */
138
139 /* return the token type, and start and finish locations in string */
140 static int
gettok(const char * s,int * from,int * to,int * tok)141 gettok(const char *s, int *from, int *to, int *tok)
142 {
143 static regex_t tokregex;
144 regmatch_t matches[15];
145 static int compiled;
146
147 if (!compiled) {
148 compiled = 1;
149 (void) regcomp(&tokregex,
150 "[ \t\r\n]*(([+-]?[0-9]{1,21}(\\.[0-9]*)?([eE][-+][0-9]+)?)|"
151 "(\"([^\"]|\\\\.)*\")|(null)|(false)|(true)|([][{}:,]))",
152 REG_EXTENDED);
153 }
154 if (regexec(&tokregex, &s[*from = *to], 15, matches, 0) != 0) {
155 return *tok = -1;
156 }
157 *to = *from + (int)(matches[1].rm_eo);
158 *tok = (matches[2].rm_so >= 0) ? MJ_NUMBER :
159 (matches[5].rm_so >= 0) ? MJ_STRING :
160 (matches[7].rm_so >= 0) ? MJ_NULL :
161 (matches[8].rm_so >= 0) ? MJ_FALSE :
162 (matches[9].rm_so >= 0) ? MJ_TRUE :
163 (matches[10].rm_so < 0) ? -1 :
164 (s[*from + (int)(matches[10].rm_so)] == '[') ? MJ_OPEN_BRACKET :
165 (s[*from + (int)(matches[10].rm_so)] == ']') ? MJ_CLOSE_BRACKET :
166 (s[*from + (int)(matches[10].rm_so)] == '{') ? MJ_OPEN_BRACE :
167 (s[*from + (int)(matches[10].rm_so)] == '}') ? MJ_CLOSE_BRACE :
168 (s[*from + (int)(matches[10].rm_so)] == ':') ? MJ_COLON :
169 MJ_COMMA;
170 *from += (int)(matches[1].rm_so);
171 return *tok;
172 }
173
174 /* minor function used to indent a JSON field */
175 static void
indent(FILE * fp,unsigned depth,const char * trailer)176 indent(FILE *fp, unsigned depth, const char *trailer)
177 {
178 unsigned i;
179
180 for (i = 0 ; i < depth ; i++) {
181 (void) fprintf(fp, " ");
182 }
183 if (trailer) {
184 (void) fprintf(fp, "%s", trailer);
185 }
186 }
187
188 /***************************************************************************/
189
190 /* return the number of entries in the array */
191 int
mj_arraycount(mj_t * atom)192 mj_arraycount(mj_t *atom)
193 {
194 return atom->c;
195 }
196
197 /* create a new JSON node */
198 int
mj_create(mj_t * atom,const char * type,...)199 mj_create(mj_t *atom, const char *type, ...)
200 {
201 va_list args;
202 ssize_t len;
203 char *s;
204
205 if (strcmp(type, "false") == 0) {
206 atom->type = MJ_FALSE;
207 atom->c = 0;
208 } else if (strcmp(type, "true") == 0) {
209 atom->type = MJ_TRUE;
210 atom->c = 1;
211 } else if (strcmp(type, "null") == 0) {
212 atom->type = MJ_NULL;
213 } else if (strcmp(type, "number") == 0) {
214 va_start(args, type);
215 create_number(atom, (double)va_arg(args, double));
216 va_end(args);
217 } else if (strcmp(type, "integer") == 0) {
218 va_start(args, type);
219 create_integer(atom, (int64_t)va_arg(args, int64_t));
220 va_end(args);
221 } else if (strcmp(type, "string") == 0) {
222 va_start(args, type);
223 s = (char *)va_arg(args, char *);
224 len = (size_t)va_arg(args, size_t);
225 va_end(args);
226 create_string(atom, s, len);
227 } else if (strcmp(type, "array") == 0) {
228 atom->type = MJ_ARRAY;
229 } else if (strcmp(type, "object") == 0) {
230 atom->type = MJ_OBJECT;
231 } else {
232 (void) fprintf(stderr, "weird type '%s'\n", type);
233 return 0;
234 }
235 return 1;
236 }
237
238 /*
239 * put a JSON tree into a text string
240 *
241 * optionally keep encoded quotes and null bytes
242 */
243 int
mj_snprint(char * buf,size_t size,mj_t * atom,int encoded)244 mj_snprint(char *buf, size_t size, mj_t *atom, int encoded)
245 {
246 unsigned i;
247 char *s;
248 char *bp;
249 int cc;
250
251 switch(atom->type) {
252 case MJ_NULL:
253 return snprintf(buf, size, "null");
254 case MJ_FALSE:
255 return snprintf(buf, size, "false");
256 case MJ_TRUE:
257 return snprintf(buf, size, "true");
258 case MJ_NUMBER:
259 return snprintf(buf, size, "%s", atom->value.s);
260 case MJ_STRING:
261 if (size < 3)
262 return 0;
263 switch (encoded) {
264 case MJ_JSON_ENCODE:
265 return snprintf(buf, size, "\"%s\"", atom->value.s);
266 default:
267 for (bp = buf, *bp++ = '"', s = atom->value.s ;
268 (size_t)(bp - buf) < size - 2 && (unsigned)(s - atom->value.s) < atom->c ; ) {
269 if (*s == JSON_ESCAPE) {
270 switch(s[1]) {
271 case '0':
272 if ((size_t)(bp - buf) < size - 3)
273 break;
274 *bp++ = '\\';
275 *bp++ = '0';
276 s += 2;
277 break;
278 case '1':
279 *bp++ = JSON_ESCAPE;
280 s += 2;
281 break;
282 case '2':
283 if ((size_t)(bp - buf) < size - 3)
284 break;
285 *bp++ = '\\';
286 *bp++ = '"';
287 s += 2;
288 break;
289 default:
290 (void) fprintf(stderr, "unrecognised character '%02x'\n", (uint8_t)s[1]);
291 s += 1;
292 break;
293 }
294 } else {
295 *bp++ = *s++;
296 }
297 }
298 *bp++ = '"';
299 *bp = 0x0;
300 return bp - buf;
301 }
302 case MJ_ARRAY:
303 cc = snprintf(buf, size, "[ ");
304 for (i = 0 ; i < atom->c ; i++) {
305 const char *sep = i+1 < atom->c ? ", " : " ";
306
307 cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i], encoded);
308 cc += snprintf(&buf[cc], size - cc, "%s", sep);
309 }
310 return cc + snprintf(&buf[cc], size - cc, "]\n");
311 case MJ_OBJECT:
312 cc = snprintf(buf, size, "{ ");
313 for (i = 0 ; i < atom->c - 1; i += 2) {
314 const char *sep = i+2 < atom->c ? ", " : " ";
315
316 cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i], encoded);
317 cc += snprintf(&buf[cc], size - cc, ":");
318 cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i + 1], encoded);
319 cc += snprintf(&buf[cc], size - cc, "%s", sep);
320 }
321 return cc + snprintf(&buf[cc], size - cc, "}\n");
322 default:
323 (void) fprintf(stderr, "mj_snprint: weird type %d\n", atom->type);
324 return 0;
325 }
326 }
327
328 /* allocate and print the atom */
329 int
mj_asprint(char ** buf,mj_t * atom,int encoded)330 mj_asprint(char **buf, mj_t *atom, int encoded)
331 {
332 size_t size;
333
334 size = mj_string_size(atom) + 1;
335 if ((*buf = calloc(1, size)) == NULL) {
336 return -1;
337 }
338 return mj_snprint(*buf, size, atom, encoded);
339 }
340
341 /* read into a JSON tree from a string */
342 int
mj_parse(mj_t * atom,const char * s,int * from,int * to,int * tok)343 mj_parse(mj_t *atom, const char *s, int *from, int *to, int *tok)
344 {
345 int i;
346
347 switch(atom->type = *tok = gettok(s, from, to, tok)) {
348 case MJ_NUMBER:
349 atom->value.s = strnsave(&s[*from], *to - *from, MJ_HUMAN);
350 atom->c = atom->size = (unsigned)strlen(atom->value.s);
351 return gettok(s, from, to, tok);
352 case MJ_STRING:
353 atom->value.s = strnsave(&s[*from + 1], *to - *from - 2, MJ_JSON_ENCODE);
354 atom->c = atom->size = (unsigned)strlen(atom->value.s);
355 return gettok(s, from, to, tok);
356 case MJ_NULL:
357 case MJ_FALSE:
358 case MJ_TRUE:
359 atom->c = (unsigned)*to;
360 return gettok(s, from, to, tok);
361 case MJ_OPEN_BRACKET:
362 mj_create(atom, "array");
363 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
364 while (mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACKET) {
365 if (*tok != MJ_COMMA) {
366 (void) fprintf(stderr, "1. expected comma (got %d) at '%s'\n", *tok, &s[*from]);
367 break;
368 }
369 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
370 }
371 return gettok(s, from, to, tok);
372 case MJ_OPEN_BRACE:
373 mj_create(atom, "object");
374 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
375 for (i = 0 ; mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACE ; i++) {
376 if (((i % 2) == 0 && *tok != MJ_COLON) || ((i % 2) == 1 && *tok != MJ_COMMA)) {
377 (void) fprintf(stderr, "2. expected comma (got %d) at '%s'\n", *tok, &s[*from]);
378 break;
379 }
380 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
381 }
382 return gettok(s, from, to, tok);
383 default:
384 return *tok;
385 }
386 }
387
388 /* return the index of the item which corresponds to the name in the array */
389 int
mj_object_find(mj_t * atom,const char * name,const unsigned from,const unsigned incr)390 mj_object_find(mj_t *atom, const char *name, const unsigned from, const unsigned incr)
391 {
392 return findentry(atom, name, from, incr);
393 }
394
395 /* find an atom in a composite mj JSON node */
396 mj_t *
mj_get_atom(mj_t * atom,...)397 mj_get_atom(mj_t *atom, ...)
398 {
399 unsigned i;
400 va_list args;
401 char *name;
402 int n;
403
404 switch(atom->type) {
405 case MJ_ARRAY:
406 va_start(args, atom);
407 i = va_arg(args, int);
408 va_end(args);
409 return (i < atom->c) ? &atom->value.v[i] : NULL;
410 case MJ_OBJECT:
411 va_start(args, atom);
412 name = va_arg(args, char *);
413 va_end(args);
414 return ((n = findentry(atom, name, 0, 2)) >= 0) ? &atom->value.v[n + 1] : NULL;
415 default:
416 return NULL;
417 }
418 }
419
420 /* perform a deep copy on an mj JSON atom */
421 int
mj_deepcopy(mj_t * dst,mj_t * src)422 mj_deepcopy(mj_t *dst, mj_t *src)
423 {
424 unsigned i;
425
426 switch(src->type) {
427 case MJ_FALSE:
428 case MJ_TRUE:
429 case MJ_NULL:
430 (void) memcpy(dst, src, sizeof(*dst));
431 return 1;
432 case MJ_STRING:
433 case MJ_NUMBER:
434 (void) memcpy(dst, src, sizeof(*dst));
435 dst->value.s = strnsave(src->value.s, -1, MJ_HUMAN);
436 dst->c = dst->size = (unsigned)strlen(dst->value.s);
437 return 1;
438 case MJ_ARRAY:
439 case MJ_OBJECT:
440 (void) memcpy(dst, src, sizeof(*dst));
441 NEWARRAY(mj_t, dst->value.v, dst->size, "mj_deepcopy()", return 0);
442 for (i = 0 ; i < src->c ; i++) {
443 if (!mj_deepcopy(&dst->value.v[i], &src->value.v[i])) {
444 return 0;
445 }
446 }
447 return 1;
448 default:
449 (void) fprintf(stderr, "weird type '%d'\n", src->type);
450 return 0;
451 }
452 }
453
454 /* do a deep delete on the object */
455 void
mj_delete(mj_t * atom)456 mj_delete(mj_t *atom)
457 {
458 unsigned i;
459
460 switch(atom->type) {
461 case MJ_STRING:
462 case MJ_NUMBER:
463 free(atom->value.s);
464 break;
465 case MJ_ARRAY:
466 case MJ_OBJECT:
467 for (i = 0 ; i < atom->c ; i++) {
468 mj_delete(&atom->value.v[i]);
469 }
470 /* XXX - agc - causing problems? free(atom->value.v); */
471 break;
472 default:
473 break;
474 }
475 }
476
477 /* return the string size needed for the textual output of the JSON node */
478 int
mj_string_size(mj_t * atom)479 mj_string_size(mj_t *atom)
480 {
481 unsigned i;
482 int cc;
483
484 switch(atom->type) {
485 case MJ_NULL:
486 case MJ_TRUE:
487 /* true */
488 return 4;
489 case MJ_FALSE:
490 /* false */
491 return 5;
492 case MJ_NUMBER:
493 return atom->c;
494 case MJ_STRING:
495 /* "string" */
496 return atom->c + 2;
497 case MJ_ARRAY:
498 /* start '[ ' */
499 for (cc = 2, i = 0 ; i < atom->c ; i++) {
500 cc += mj_string_size(&atom->value.v[i]);
501 /* separator ', ' or ' ' */
502 cc += (i < atom->c - 1) ? 2 : 1;
503 }
504 /* end ']' */
505 return cc + 1;
506 case MJ_OBJECT:
507 /* start '{ ' */
508 for (cc = 2, i = 0 ; i < atom->c ; i += 2) {
509 /* key:value */
510 cc += mj_string_size(&atom->value.v[i]) + 1 + mj_string_size(&atom->value.v[i + 1]);
511 /* separator ', ' or ' ' */
512 cc += (i < atom->c - 1) ? 2 : 1;
513 }
514 /* end '}' */
515 return cc + 1;
516 default:
517 (void) fprintf(stderr, "mj_string_size: weird type %d\n", atom->type);
518 return 0;
519 }
520 }
521
522 /* create a new atom, and append it to the array or object */
523 int
mj_append(mj_t * atom,const char * type,...)524 mj_append(mj_t *atom, const char *type, ...)
525 {
526 va_list args;
527 ssize_t len;
528 char *s;
529
530 if (atom->type != MJ_ARRAY && atom->type != MJ_OBJECT) {
531 return 0;
532 }
533 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append()", return 0);
534 va_start(args, type);
535 if (strcmp(type, "string") == 0) {
536 s = (char *)va_arg(args, char *);
537 len = (ssize_t)va_arg(args, ssize_t);
538 create_string(&atom->value.v[atom->c++], s, len);
539 } else if (strcmp(type, "integer") == 0) {
540 create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t));
541 } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) {
542 mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *));
543 } else {
544 (void) fprintf(stderr, "mj_append: weird type '%s'\n", type);
545 }
546 va_end(args);
547 return 1;
548 }
549
550 /* append a field to an object */
551 int
mj_append_field(mj_t * atom,const char * name,const char * type,...)552 mj_append_field(mj_t *atom, const char *name, const char *type, ...)
553 {
554 va_list args;
555 ssize_t len;
556 char *s;
557
558 if (atom->type != MJ_OBJECT) {
559 return 0;
560 }
561 mj_append(atom, "string", name, -1);
562 ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append_field()", return 0);
563 va_start(args, type);
564 if (strcmp(type, "string") == 0) {
565 s = (char *)va_arg(args, char *);
566 len = (ssize_t)va_arg(args, ssize_t);
567 create_string(&atom->value.v[atom->c++], s, len);
568 } else if (strcmp(type, "integer") == 0) {
569 create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t));
570 } else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) {
571 mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *));
572 } else {
573 (void) fprintf(stderr, "mj_append_field: weird type '%s'\n", type);
574 }
575 va_end(args);
576 return 1;
577 }
578
579 /* make sure a JSON object is politically correct */
580 int
mj_lint(mj_t * obj)581 mj_lint(mj_t *obj)
582 {
583 unsigned i;
584 int ret;
585
586 switch(obj->type) {
587 case MJ_NULL:
588 case MJ_FALSE:
589 case MJ_TRUE:
590 if (obj->value.s != NULL) {
591 (void) fprintf(stderr, "null/false/true: non zero string\n");
592 return 0;
593 }
594 return 1;
595 case MJ_NUMBER:
596 case MJ_STRING:
597 if (obj->c > obj->size) {
598 (void) fprintf(stderr, "string/number lint c (%u) > size (%u)\n", obj->c, obj->size);
599 return 0;
600 }
601 return 1;
602 case MJ_ARRAY:
603 case MJ_OBJECT:
604 if (obj->c > obj->size) {
605 (void) fprintf(stderr, "array/object lint c (%u) > size (%u)\n", obj->c, obj->size);
606 return 0;
607 }
608 for (ret = 1, i = 0 ; i < obj->c ; i++) {
609 if (!mj_lint(&obj->value.v[i])) {
610 (void) fprintf(stderr, "array/object lint found at %d of %p\n", i, obj);
611 ret = 0;
612 }
613 }
614 return ret;
615 default:
616 (void) fprintf(stderr, "problem type %d in %p\n", obj->type, obj);
617 return 0;
618 }
619 }
620
621 /* pretty-print a JSON struct - can be called recursively */
622 int
mj_pretty(mj_t * mj,void * vp,unsigned depth,const char * trailer)623 mj_pretty(mj_t *mj, void *vp, unsigned depth, const char *trailer)
624 {
625 unsigned i;
626 FILE *fp;
627 char *s;
628
629 fp = (FILE *)vp;
630 switch(mj->type) {
631 case MJ_NUMBER:
632 case MJ_TRUE:
633 case MJ_FALSE:
634 case MJ_NULL:
635 indent(fp, depth, mj->value.s);
636 break;
637 case MJ_STRING:
638 indent(fp, depth, NULL);
639 mj_asprint(&s, mj, MJ_HUMAN);
640 (void) fprintf(fp, "%s", s);
641 free(s);
642 break;
643 case MJ_ARRAY:
644 indent(fp, depth, "[\n");
645 for (i = 0 ; i < mj->c ; i++) {
646 mj_pretty(&mj->value.v[i], fp, depth + JSON_INDENT, (i < mj->c - 1) ? ",\n" : "\n");
647 }
648 indent(fp, depth, "]");
649 break;
650 case MJ_OBJECT:
651 indent(fp, depth, "{\n");
652 for (i = 0 ; i < mj->c ; i += 2) {
653 mj_pretty(&mj->value.v[i], fp, depth + JSON_INDENT, " : ");
654 mj_pretty(&mj->value.v[i + 1], fp, 0, (i < mj->c - 2) ? ",\n" : "\n");
655 }
656 indent(fp, depth, "}");
657 break;
658 }
659 indent(fp, 0, trailer);
660 return 1;
661 }
662
663 /* show the contents of the simple atom as a string representation */
664 const char *
mj_string_rep(mj_t * atom)665 mj_string_rep(mj_t *atom)
666 {
667 if (atom == NULL) {
668 return 0;
669 }
670 switch(atom->type) {
671 case MJ_STRING:
672 case MJ_NUMBER:
673 return atom->value.s;
674 case MJ_NULL:
675 return "null";
676 case MJ_FALSE:
677 return "false";
678 case MJ_TRUE:
679 return "true";
680 default:
681 return NULL;
682 }
683 }
684