xref: /netbsd/crypto/external/bsd/netpgp/dist/src/libmj/mj.c (revision 95593e56)
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