xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/libmj/mj.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
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 *
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
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
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
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
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
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
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
192 mj_arraycount(mj_t *atom)
193 {
194 	return atom->c;
195 }
196 
197 /* create a new JSON node */
198 int
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
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 		break;
303 	case MJ_ARRAY:
304 		cc = snprintf(buf, size, "[ ");
305 		for (i = 0 ; i < atom->c ; i++) {
306 			const char *sep = i+1 < atom->c ? ", " : " ";
307 
308 			cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i], encoded);
309 			cc += snprintf(&buf[cc], size - cc, "%s", sep);
310 		}
311 		return cc + snprintf(&buf[cc], size - cc, "]\n");
312 	case MJ_OBJECT:
313 		cc = snprintf(buf, size, "{ ");
314 		for (i = 0 ; i < atom->c - 1; i += 2) {
315 			const char *sep = i+2 < atom->c ? ", " : " ";
316 
317 			cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i], encoded);
318 			cc += snprintf(&buf[cc], size - cc, ":");
319 			cc += mj_snprint(&buf[cc], size - cc, &atom->value.v[i + 1], encoded);
320 			cc += snprintf(&buf[cc], size - cc, "%s", sep);
321 		}
322 		return cc + snprintf(&buf[cc], size - cc, "}\n");
323 	default:
324 		(void) fprintf(stderr, "mj_snprint: weird type %d\n", atom->type);
325 		return 0;
326 	}
327 }
328 
329 /* allocate and print the atom */
330 int
331 mj_asprint(char **buf, mj_t *atom, int encoded)
332 {
333 	size_t	size;
334 
335 	size = mj_string_size(atom) + 1;
336 	if ((*buf = calloc(1, size)) == NULL) {
337 		return -1;
338 	}
339 	return mj_snprint(*buf, size, atom, encoded);
340 }
341 
342 /* read into a JSON tree from a string */
343 int
344 mj_parse(mj_t *atom, const char *s, int *from, int *to, int *tok)
345 {
346 	int	i;
347 
348 	switch(atom->type = *tok = gettok(s, from, to, tok)) {
349 	case MJ_NUMBER:
350 		atom->value.s = strnsave(&s[*from], *to - *from, MJ_HUMAN);
351 		atom->c = atom->size = (unsigned)strlen(atom->value.s);
352 		return gettok(s, from, to, tok);
353 	case MJ_STRING:
354 		atom->value.s = strnsave(&s[*from + 1], *to - *from - 2, MJ_JSON_ENCODE);
355 		atom->c = atom->size = (unsigned)strlen(atom->value.s);
356 		return gettok(s, from, to, tok);
357 	case MJ_NULL:
358 	case MJ_FALSE:
359 	case MJ_TRUE:
360 		atom->c = (unsigned)*to;
361 		return gettok(s, from, to, tok);
362 	case MJ_OPEN_BRACKET:
363 		mj_create(atom, "array");
364 		ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
365 		while (mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACKET) {
366 			if (*tok != MJ_COMMA) {
367 				(void) fprintf(stderr, "1. expected comma (got %d) at '%s'\n", *tok, &s[*from]);
368 				break;
369 			}
370 			ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
371 		}
372 		return gettok(s, from, to, tok);
373 	case MJ_OPEN_BRACE:
374 		mj_create(atom, "object");
375 		ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
376 		for (i = 0 ; mj_parse(&atom->value.v[atom->c++], s, from, to, tok) >= 0 && *tok != MJ_CLOSE_BRACE ; i++) {
377 			if (((i % 2) == 0 && *tok != MJ_COLON) || ((i % 2) == 1 && *tok != MJ_COMMA)) {
378 				(void) fprintf(stderr, "2. expected comma (got %d) at '%s'\n", *tok, &s[*from]);
379 				break;
380 			}
381 			ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_parse()", return 0);
382 		}
383 		return gettok(s, from, to, tok);
384 	default:
385 		return *tok;
386 	}
387 }
388 
389 /* return the index of the item which corresponds to the name in the array */
390 int
391 mj_object_find(mj_t *atom, const char *name, const unsigned from, const unsigned incr)
392 {
393 	return findentry(atom, name, from, incr);
394 }
395 
396 /* find an atom in a composite mj JSON node */
397 mj_t *
398 mj_get_atom(mj_t *atom, ...)
399 {
400 	unsigned	 i;
401 	va_list		 args;
402 	char		*name;
403 	int		 n;
404 
405 	switch(atom->type) {
406 	case MJ_ARRAY:
407 		va_start(args, atom);
408 		i = va_arg(args, int);
409 		va_end(args);
410 		return (i < atom->c) ? &atom->value.v[i] : NULL;
411 	case MJ_OBJECT:
412 		va_start(args, atom);
413 		name = va_arg(args, char *);
414 		va_end(args);
415 		return ((n = findentry(atom, name, 0, 2)) >= 0) ? &atom->value.v[n + 1] : NULL;
416 	default:
417 		return NULL;
418 	}
419 }
420 
421 /* perform a deep copy on an mj JSON atom */
422 int
423 mj_deepcopy(mj_t *dst, mj_t *src)
424 {
425 	unsigned	i;
426 
427 	switch(src->type) {
428 	case MJ_FALSE:
429 	case MJ_TRUE:
430 	case MJ_NULL:
431 		(void) memcpy(dst, src, sizeof(*dst));
432 		return 1;
433 	case MJ_STRING:
434 	case MJ_NUMBER:
435 		(void) memcpy(dst, src, sizeof(*dst));
436 		dst->value.s = strnsave(src->value.s, -1, MJ_HUMAN);
437 		dst->c = dst->size = (unsigned)strlen(dst->value.s);
438 		return 1;
439 	case MJ_ARRAY:
440 	case MJ_OBJECT:
441 		(void) memcpy(dst, src, sizeof(*dst));
442 		NEWARRAY(mj_t, dst->value.v, dst->size, "mj_deepcopy()", return 0);
443 		for (i = 0 ; i < src->c ; i++) {
444 			if (!mj_deepcopy(&dst->value.v[i], &src->value.v[i])) {
445 				return 0;
446 			}
447 		}
448 		return 1;
449 	default:
450 		(void) fprintf(stderr, "weird type '%d'\n", src->type);
451 		return 0;
452 	}
453 }
454 
455 /* do a deep delete on the object */
456 void
457 mj_delete(mj_t *atom)
458 {
459 	unsigned	i;
460 
461 	switch(atom->type) {
462 	case MJ_STRING:
463 	case MJ_NUMBER:
464 		free(atom->value.s);
465 		break;
466 	case MJ_ARRAY:
467 	case MJ_OBJECT:
468 		for (i = 0 ; i < atom->c ; i++) {
469 			mj_delete(&atom->value.v[i]);
470 		}
471 		/* XXX - agc - causing problems? free(atom->value.v); */
472 		break;
473 	default:
474 		break;
475 	}
476 }
477 
478 /* return the string size needed for the textual output of the JSON node */
479 int
480 mj_string_size(mj_t *atom)
481 {
482 	unsigned	i;
483 	int		cc;
484 
485 	switch(atom->type) {
486 	case MJ_NULL:
487 	case MJ_TRUE:
488 		/* true */
489 		return 4;
490 	case MJ_FALSE:
491 		/* false */
492 		return 5;
493 	case MJ_NUMBER:
494 		return atom->c;
495 	case MJ_STRING:
496 		/* "string" */
497 		return atom->c + 2;
498 	case MJ_ARRAY:
499 		/* start '[ ' */
500 		for (cc = 2, i = 0 ; i < atom->c ; i++) {
501 			cc += mj_string_size(&atom->value.v[i]);
502 			/* separator ', ' or ' ' */
503 			cc += (i < atom->c - 1) ? 2 : 1;
504 		}
505 		/* end ']' */
506 		return cc + 1;
507 	case MJ_OBJECT:
508 		/* start '{ ' */
509 		for (cc = 2, i = 0 ; i < atom->c ; i += 2) {
510 			/* key:value */
511 			cc += mj_string_size(&atom->value.v[i]) + 1 + mj_string_size(&atom->value.v[i + 1]);
512 			/* separator ', ' or ' ' */
513 			cc += (i < atom->c - 1) ? 2 : 1;
514 		}
515 		/* end '}' */
516 		return cc + 1;
517 	default:
518 		(void) fprintf(stderr, "mj_string_size: weird type %d\n", atom->type);
519 		return 0;
520 	}
521 }
522 
523 /* create a new atom, and append it to the array or object */
524 int
525 mj_append(mj_t *atom, const char *type, ...)
526 {
527 	va_list	 args;
528 	ssize_t	 len;
529 	char	*s;
530 
531 	if (atom->type != MJ_ARRAY && atom->type != MJ_OBJECT) {
532 		return 0;
533 	}
534 	ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append()", return 0);
535 	va_start(args, type);
536 	if (strcmp(type, "string") == 0) {
537 		s = (char *)va_arg(args, char *);
538 		len = (ssize_t)va_arg(args, ssize_t);
539 		create_string(&atom->value.v[atom->c++], s, len);
540 	} else if (strcmp(type, "integer") == 0) {
541 		create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t));
542 	} else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) {
543 		mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *));
544 	} else {
545 		(void) fprintf(stderr, "mj_append: weird type '%s'\n", type);
546 	}
547 	va_end(args);
548 	return 1;
549 }
550 
551 /* append a field to an object */
552 int
553 mj_append_field(mj_t *atom, const char *name, const char *type, ...)
554 {
555 	va_list	 args;
556 	ssize_t	 len;
557 	char	*s;
558 
559 	if (atom->type != MJ_OBJECT) {
560 		return 0;
561 	}
562 	mj_append(atom, "string", name, -1);
563 	ALLOC(mj_t, atom->value.v, atom->size, atom->c, 10, 10, "mj_append_field()", return 0);
564 	va_start(args, type);
565 	if (strcmp(type, "string") == 0) {
566 		s = (char *)va_arg(args, char *);
567 		len = (ssize_t)va_arg(args, ssize_t);
568 		create_string(&atom->value.v[atom->c++], s, len);
569 	} else if (strcmp(type, "integer") == 0) {
570 		create_integer(&atom->value.v[atom->c++], (int64_t)va_arg(args, int64_t));
571 	} else if (strcmp(type, "object") == 0 || strcmp(type, "array") == 0) {
572 		mj_deepcopy(&atom->value.v[atom->c++], (mj_t *)va_arg(args, mj_t *));
573 	} else {
574 		(void) fprintf(stderr, "mj_append_field: weird type '%s'\n", type);
575 	}
576 	va_end(args);
577 	return 1;
578 }
579 
580 /* make sure a JSON object is politically correct */
581 int
582 mj_lint(mj_t *obj)
583 {
584 	unsigned	i;
585 	int		ret;
586 
587 	switch(obj->type) {
588 	case MJ_NULL:
589 	case MJ_FALSE:
590 	case MJ_TRUE:
591 		if (obj->value.s != NULL) {
592 			(void) fprintf(stderr, "null/false/true: non zero string\n");
593 			return 0;
594 		}
595 		return 1;
596 	case MJ_NUMBER:
597 	case MJ_STRING:
598 		if (obj->c > obj->size) {
599 			(void) fprintf(stderr, "string/number lint c (%u) > size (%u)\n", obj->c, obj->size);
600 			return 0;
601 		}
602 		return 1;
603 	case MJ_ARRAY:
604 	case MJ_OBJECT:
605 		if (obj->c > obj->size) {
606 			(void) fprintf(stderr, "array/object lint c (%u) > size (%u)\n", obj->c, obj->size);
607 			return 0;
608 		}
609 		for (ret = 1, i = 0 ; i < obj->c ; i++) {
610 			if (!mj_lint(&obj->value.v[i])) {
611 				(void) fprintf(stderr, "array/object lint found at %d of %p\n", i, obj);
612 				ret = 0;
613 			}
614 		}
615 		return ret;
616 	default:
617 		(void) fprintf(stderr, "problem type %d in %p\n", obj->type, obj);
618 		return 0;
619 	}
620 }
621 
622 /* pretty-print a JSON struct - can be called recursively */
623 int
624 mj_pretty(mj_t *mj, void *vp, unsigned depth, const char *trailer)
625 {
626 	unsigned	 i;
627 	FILE		*fp;
628 	char		*s;
629 
630 	fp = (FILE *)vp;
631 	switch(mj->type) {
632 	case MJ_NUMBER:
633 	case MJ_TRUE:
634 	case MJ_FALSE:
635 	case MJ_NULL:
636 		indent(fp, depth, mj->value.s);
637 		break;
638 	case MJ_STRING:
639 		indent(fp, depth, NULL);
640 		mj_asprint(&s, mj, MJ_HUMAN);
641 		(void) fprintf(fp, "%s", s);
642 		free(s);
643 		break;
644 	case MJ_ARRAY:
645 		indent(fp, depth, "[\n");
646 		for (i = 0 ; i < mj->c ; i++) {
647 			mj_pretty(&mj->value.v[i], fp, depth + JSON_INDENT, (i < mj->c - 1) ? ",\n" : "\n");
648 		}
649 		indent(fp, depth, "]");
650 		break;
651 	case MJ_OBJECT:
652 		indent(fp, depth, "{\n");
653 		for (i = 0 ; i < mj->c ; i += 2) {
654 			mj_pretty(&mj->value.v[i], fp, depth + JSON_INDENT, " : ");
655 			mj_pretty(&mj->value.v[i + 1], fp, 0, (i < mj->c - 2) ? ",\n" : "\n");
656 		}
657 		indent(fp, depth, "}");
658 		break;
659 	}
660 	indent(fp, 0, trailer);
661 	return 1;
662 }
663 
664 /* show the contents of the simple atom as a string representation */
665 const char *
666 mj_string_rep(mj_t *atom)
667 {
668 	if (atom == NULL) {
669 		return 0;
670 	}
671 	switch(atom->type) {
672 	case MJ_STRING:
673 	case MJ_NUMBER:
674 		return atom->value.s;
675 	case MJ_NULL:
676 		return "null";
677 	case MJ_FALSE:
678 		return "false";
679 	case MJ_TRUE:
680 		return "true";
681 	default:
682 		return NULL;
683 	}
684 }
685