Line data Source code
1 : /*
2 : * Copyright (c) 2010 Kungliga Tekniska Högskolan
3 : * (Royal Institute of Technology, Stockholm, Sweden).
4 : * All rights reserved.
5 : *
6 : * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 : *
8 : * Redistribution and use in source and binary forms, with or without
9 : * modification, are permitted provided that the following conditions
10 : * are met:
11 : *
12 : * 1. Redistributions of source code must retain the above copyright
13 : * notice, this list of conditions and the following disclaimer.
14 : *
15 : * 2. Redistributions in binary form must reproduce the above copyright
16 : * notice, this list of conditions and the following disclaimer in the
17 : * documentation and/or other materials provided with the distribution.
18 : *
19 : * 3. Neither the name of the Institute nor the names of its contributors
20 : * may be used to endorse or promote products derived from this software
21 : * without specific prior written permission.
22 : *
23 : * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 : * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 : * SUCH DAMAGE.
34 : */
35 :
36 : #include "baselocl.h"
37 : #include <ctype.h>
38 : #include <base64.h>
39 :
40 : static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
41 : static heim_string_t heim_tid_data_uuid_key = NULL;
42 : static const char base64_chars[] =
43 : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
44 :
45 : static void
46 0 : json_init_once(void *arg)
47 : {
48 0 : heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
49 0 : }
50 :
51 : struct twojson {
52 : void *ctx;
53 : void (*out)(void *, const char *);
54 : size_t indent;
55 : heim_json_flags_t flags;
56 : int ret;
57 : int first;
58 : };
59 :
60 : struct heim_strbuf {
61 : char *str;
62 : size_t len;
63 : size_t alloced;
64 : int enomem;
65 : heim_json_flags_t flags;
66 : };
67 :
68 : static int
69 : base2json(heim_object_t, struct twojson *);
70 :
71 : static void
72 0 : indent(struct twojson *j)
73 : {
74 0 : size_t i = j->indent;
75 0 : if (j->flags & HEIM_JSON_F_ONE_LINE)
76 0 : return;
77 0 : while (i--)
78 0 : j->out(j->ctx, "\t");
79 : }
80 :
81 : static void
82 0 : array2json(heim_object_t value, void *ctx, int *stop)
83 : {
84 0 : struct twojson *j = ctx;
85 0 : if (j->ret)
86 0 : return;
87 0 : if (j->first) {
88 0 : j->first = 0;
89 : } else {
90 0 : j->out(j->ctx, NULL); /* eat previous '\n' if possible */
91 0 : j->out(j->ctx, ",\n");
92 : }
93 0 : j->ret = base2json(value, j);
94 : }
95 :
96 : static void
97 0 : dict2json(heim_object_t key, heim_object_t value, void *ctx)
98 : {
99 0 : struct twojson *j = ctx;
100 0 : if (j->ret)
101 0 : return;
102 0 : if (j->first) {
103 0 : j->first = 0;
104 : } else {
105 0 : j->out(j->ctx, NULL); /* eat previous '\n' if possible */
106 0 : j->out(j->ctx, ",\n");
107 : }
108 0 : j->ret = base2json(key, j);
109 0 : if (j->ret)
110 0 : return;
111 0 : j->out(j->ctx, " : \n");
112 0 : j->indent++;
113 0 : j->ret = base2json(value, j);
114 0 : if (j->ret)
115 0 : return;
116 0 : j->indent--;
117 : }
118 :
119 : static int
120 0 : base2json(heim_object_t obj, struct twojson *j)
121 : {
122 : heim_tid_t type;
123 0 : int first = 0;
124 :
125 0 : if (obj == NULL) {
126 0 : if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
127 0 : obj = heim_null_create();
128 0 : } else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
129 0 : return EINVAL;
130 : } else {
131 0 : indent(j);
132 0 : j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
133 0 : return 0;
134 : }
135 : }
136 :
137 0 : type = heim_get_tid(obj);
138 0 : switch (type) {
139 0 : case HEIM_TID_ARRAY:
140 0 : indent(j);
141 0 : j->out(j->ctx, "[\n");
142 0 : j->indent++;
143 0 : first = j->first;
144 0 : j->first = 1;
145 0 : heim_array_iterate_f(obj, j, array2json);
146 0 : j->indent--;
147 0 : if (!j->first)
148 0 : j->out(j->ctx, "\n");
149 0 : indent(j);
150 0 : j->out(j->ctx, "]\n");
151 0 : j->first = first;
152 0 : break;
153 :
154 0 : case HEIM_TID_DICT:
155 0 : indent(j);
156 0 : j->out(j->ctx, "{\n");
157 0 : j->indent++;
158 0 : first = j->first;
159 0 : j->first = 1;
160 0 : heim_dict_iterate_f(obj, j, dict2json);
161 0 : j->indent--;
162 0 : if (!j->first)
163 0 : j->out(j->ctx, "\n");
164 0 : indent(j);
165 0 : j->out(j->ctx, "}\n");
166 0 : j->first = first;
167 0 : break;
168 :
169 0 : case HEIM_TID_STRING:
170 0 : indent(j);
171 0 : j->out(j->ctx, "\"");
172 0 : j->out(j->ctx, heim_string_get_utf8(obj));
173 0 : j->out(j->ctx, "\"");
174 0 : break;
175 :
176 0 : case HEIM_TID_DATA: {
177 : heim_dict_t d;
178 : heim_string_t v;
179 : const heim_octet_string *data;
180 0 : char *b64 = NULL;
181 : int ret;
182 :
183 0 : if (j->flags & HEIM_JSON_F_NO_DATA)
184 0 : return EINVAL; /* JSON doesn't do binary */
185 :
186 0 : data = heim_data_get_data(obj);
187 0 : ret = rk_base64_encode(data->data, data->length, &b64);
188 0 : if (ret < 0 || b64 == NULL)
189 0 : return ENOMEM;
190 :
191 0 : if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
192 0 : indent(j);
193 0 : j->out(j->ctx, "\"");
194 0 : j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
195 0 : j->out(j->ctx, "\"");
196 0 : free(b64);
197 : } else {
198 : /*
199 : * JSON has no way to represent binary data, therefore the
200 : * following is a Heimdal-specific convention.
201 : *
202 : * We encode binary data as a dict with a single very magic
203 : * key with a base64-encoded value. The magic key includes
204 : * a uuid, so we're not likely to alias accidentally.
205 : */
206 0 : d = heim_dict_create(2);
207 0 : if (d == NULL) {
208 0 : free(b64);
209 0 : return ENOMEM;
210 : }
211 0 : v = heim_string_ref_create(b64, free);
212 0 : if (v == NULL) {
213 0 : free(b64);
214 0 : heim_release(d);
215 0 : return ENOMEM;
216 : }
217 0 : ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
218 0 : heim_release(v);
219 0 : if (ret) {
220 0 : heim_release(d);
221 0 : return ENOMEM;
222 : }
223 0 : ret = base2json(d, j);
224 0 : heim_release(d);
225 0 : if (ret)
226 0 : return ret;
227 : }
228 0 : break;
229 : }
230 :
231 0 : case HEIM_TID_NUMBER: {
232 : char num[32];
233 0 : indent(j);
234 0 : snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
235 0 : j->out(j->ctx, num);
236 0 : break;
237 : }
238 0 : case HEIM_TID_NULL:
239 0 : indent(j);
240 0 : j->out(j->ctx, "null");
241 0 : break;
242 0 : case HEIM_TID_BOOL:
243 0 : indent(j);
244 0 : j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
245 0 : break;
246 0 : default:
247 0 : return 1;
248 : }
249 0 : return 0;
250 : }
251 :
252 : static int
253 0 : heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
254 : void (*out)(void *, const char *))
255 : {
256 : struct twojson j;
257 :
258 0 : if (flags & HEIM_JSON_F_STRICT_STRINGS)
259 0 : return ENOTSUP; /* Sorry, not yet! */
260 :
261 0 : heim_base_once_f(&heim_json_once, NULL, json_init_once);
262 :
263 0 : j.indent = 0;
264 0 : j.ctx = ctx;
265 0 : j.out = out;
266 0 : j.flags = flags;
267 0 : j.ret = 0;
268 0 : j.first = 1;
269 :
270 0 : return base2json(obj, &j);
271 : }
272 :
273 :
274 : /*
275 : *
276 : */
277 :
278 : struct parse_ctx {
279 : unsigned long lineno;
280 : const uint8_t *p;
281 : const uint8_t *pstart;
282 : const uint8_t *pend;
283 : heim_error_t error;
284 : size_t depth;
285 : heim_json_flags_t flags;
286 : };
287 :
288 :
289 : static heim_object_t
290 : parse_value(struct parse_ctx *ctx);
291 :
292 : /*
293 : * This function eats whitespace, but, critically, it also succeeds
294 : * only if there's anything left to parse.
295 : */
296 : static int
297 0 : white_spaces(struct parse_ctx *ctx)
298 : {
299 0 : while (ctx->p < ctx->pend) {
300 0 : uint8_t c = *ctx->p;
301 0 : if (c == ' ' || c == '\t' || c == '\r') {
302 :
303 0 : } else if (c == '\n') {
304 0 : ctx->lineno++;
305 : } else
306 0 : return 0;
307 0 : (ctx->p)++;
308 : }
309 0 : return -1;
310 : }
311 :
312 : static int
313 0 : is_number(uint8_t n)
314 : {
315 0 : return ('0' <= n && n <= '9');
316 : }
317 :
318 : static heim_number_t
319 0 : parse_number(struct parse_ctx *ctx)
320 : {
321 0 : int number = 0, neg = 1;
322 :
323 0 : if (ctx->p >= ctx->pend)
324 0 : return NULL;
325 :
326 0 : if (*ctx->p == '-') {
327 0 : if (ctx->p + 1 >= ctx->pend)
328 0 : return NULL;
329 0 : neg = -1;
330 0 : ctx->p += 1;
331 : }
332 :
333 0 : while (ctx->p < ctx->pend) {
334 0 : if (is_number(*ctx->p)) {
335 0 : number = (number * 10) + (*ctx->p - '0');
336 : } else {
337 0 : break;
338 : }
339 0 : ctx->p += 1;
340 : }
341 :
342 0 : return heim_number_create(number * neg);
343 : }
344 :
345 : static heim_string_t
346 0 : parse_string(struct parse_ctx *ctx)
347 : {
348 : const uint8_t *start;
349 0 : int quote = 0;
350 :
351 0 : if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
352 0 : ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
353 : "not yet supported");
354 0 : return NULL;
355 : }
356 :
357 0 : if (*ctx->p != '"') {
358 0 : ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
359 : "found something else at line %lu",
360 : ctx->lineno);
361 0 : return NULL;
362 : }
363 0 : start = ++ctx->p;
364 :
365 0 : while (ctx->p < ctx->pend) {
366 0 : if (*ctx->p == '\n') {
367 0 : ctx->lineno++;
368 0 : } else if (*ctx->p == '\\') {
369 0 : if (ctx->p + 1 == ctx->pend)
370 0 : goto out;
371 0 : ctx->p++;
372 0 : quote = 1;
373 0 : } else if (*ctx->p == '"') {
374 : heim_object_t o;
375 :
376 0 : if (quote) {
377 : char *p0, *p;
378 0 : p = p0 = malloc(ctx->p - start);
379 0 : if (p == NULL)
380 0 : goto out;
381 0 : while (start < ctx->p) {
382 0 : if (*start == '\\') {
383 0 : start++;
384 : /* XXX validate quoted char */
385 : }
386 0 : *p++ = *start++;
387 : }
388 0 : o = heim_string_create_with_bytes(p0, p - p0);
389 0 : free(p0);
390 : } else {
391 0 : o = heim_string_create_with_bytes(start, ctx->p - start);
392 0 : if (o == NULL) {
393 0 : ctx->error = heim_error_create_enomem();
394 0 : return NULL;
395 : }
396 :
397 : /* If we can decode as base64, then let's */
398 0 : if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
399 : void *buf;
400 : size_t len;
401 : const char *s;
402 :
403 0 : s = heim_string_get_utf8(o);
404 0 : len = strlen(s);
405 :
406 0 : if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
407 0 : buf = malloc(len);
408 0 : if (buf == NULL) {
409 0 : heim_release(o);
410 0 : ctx->error = heim_error_create_enomem();
411 0 : return NULL;
412 : }
413 0 : len = rk_base64_decode(s, buf);
414 0 : if (len == -1) {
415 0 : free(buf);
416 0 : return o;
417 : }
418 0 : heim_release(o);
419 0 : o = heim_data_ref_create(buf, len, free);
420 : }
421 : }
422 : }
423 0 : ctx->p += 1;
424 :
425 0 : return o;
426 : }
427 0 : ctx->p += 1;
428 : }
429 0 : out:
430 0 : ctx->error = heim_error_create(EINVAL, "ran out of string");
431 0 : return NULL;
432 : }
433 :
434 : static int
435 0 : parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
436 : {
437 : heim_string_t key;
438 : heim_object_t value;
439 :
440 0 : if (white_spaces(ctx))
441 0 : return -1;
442 :
443 0 : if (*ctx->p == '}') {
444 0 : ctx->p++;
445 0 : return 0;
446 : }
447 :
448 0 : if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
449 : /* JSON allows only string keys */
450 0 : key = parse_string(ctx);
451 : else
452 : /* heim_dict_t allows any heim_object_t as key */
453 0 : key = parse_value(ctx);
454 0 : if (key == NULL)
455 : /* Even heim_dict_t does not allow C NULLs as keys though! */
456 0 : return -1;
457 :
458 0 : if (white_spaces(ctx)) {
459 0 : heim_release(key);
460 0 : return -1;
461 : }
462 :
463 0 : if (*ctx->p != ':') {
464 0 : heim_release(key);
465 0 : return -1;
466 : }
467 :
468 0 : ctx->p += 1; /* safe because we call white_spaces() next */
469 :
470 0 : if (white_spaces(ctx)) {
471 0 : heim_release(key);
472 0 : return -1;
473 : }
474 :
475 0 : value = parse_value(ctx);
476 0 : if (value == NULL &&
477 0 : (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
478 0 : if (ctx->error == NULL)
479 0 : ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
480 0 : heim_release(key);
481 0 : return -1;
482 : }
483 0 : heim_dict_set_value(dict, key, value);
484 0 : heim_release(key);
485 0 : heim_release(value);
486 :
487 0 : if (white_spaces(ctx))
488 0 : return -1;
489 :
490 0 : if (*ctx->p == '}') {
491 : /*
492 : * Return 1 but don't consume the '}' so we can count the one
493 : * pair in a one-pair dict
494 : */
495 0 : return 1;
496 0 : } else if (*ctx->p == ',') {
497 0 : ctx->p++;
498 0 : return 1;
499 : }
500 0 : return -1;
501 : }
502 :
503 : static heim_dict_t
504 0 : parse_dict(struct parse_ctx *ctx)
505 : {
506 : heim_dict_t dict;
507 0 : size_t count = 0;
508 : int ret;
509 :
510 0 : heim_assert(*ctx->p == '{', "string doesn't start with {");
511 :
512 0 : dict = heim_dict_create(11);
513 0 : if (dict == NULL) {
514 0 : ctx->error = heim_error_create_enomem();
515 0 : return NULL;
516 : }
517 :
518 0 : ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
519 :
520 0 : while ((ret = parse_pair(dict, ctx)) > 0)
521 0 : count++;
522 0 : if (ret < 0) {
523 0 : heim_release(dict);
524 0 : return NULL;
525 : }
526 0 : if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
527 0 : heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
528 :
529 : /*
530 : * Binary data encoded as a dict with a single magic key with
531 : * base64-encoded value? Decode as heim_data_t.
532 : */
533 0 : if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
534 : void *buf;
535 : size_t len;
536 :
537 0 : buf = malloc(strlen(heim_string_get_utf8(v)));
538 0 : if (buf == NULL) {
539 0 : heim_release(dict);
540 0 : heim_release(v);
541 0 : ctx->error = heim_error_create_enomem();
542 0 : return NULL;
543 : }
544 0 : len = rk_base64_decode(heim_string_get_utf8(v), buf);
545 0 : heim_release(v);
546 0 : if (len == -1) {
547 0 : free(buf);
548 0 : return dict; /* assume aliasing accident */
549 : }
550 0 : heim_release(dict);
551 0 : return (heim_dict_t)heim_data_ref_create(buf, len, free);
552 : }
553 : }
554 0 : return dict;
555 : }
556 :
557 : static int
558 0 : parse_item(heim_array_t array, struct parse_ctx *ctx)
559 : {
560 : heim_object_t value;
561 :
562 0 : if (white_spaces(ctx))
563 0 : return -1;
564 :
565 0 : if (*ctx->p == ']') {
566 0 : ctx->p++; /* safe because parse_value() calls white_spaces() first */
567 0 : return 0;
568 : }
569 :
570 0 : value = parse_value(ctx);
571 0 : if (value == NULL &&
572 0 : (ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
573 0 : return -1;
574 :
575 0 : heim_array_append_value(array, value);
576 0 : heim_release(value);
577 :
578 0 : if (white_spaces(ctx))
579 0 : return -1;
580 :
581 0 : if (*ctx->p == ']') {
582 0 : ctx->p++;
583 0 : return 0;
584 0 : } else if (*ctx->p == ',') {
585 0 : ctx->p++;
586 0 : return 1;
587 : }
588 0 : return -1;
589 : }
590 :
591 : static heim_array_t
592 0 : parse_array(struct parse_ctx *ctx)
593 : {
594 0 : heim_array_t array = heim_array_create();
595 : int ret;
596 :
597 0 : heim_assert(*ctx->p == '[', "array doesn't start with [");
598 0 : ctx->p += 1;
599 :
600 0 : while ((ret = parse_item(array, ctx)) > 0)
601 : ;
602 0 : if (ret < 0) {
603 0 : heim_release(array);
604 0 : return NULL;
605 : }
606 0 : return array;
607 : }
608 :
609 : static heim_object_t
610 0 : parse_value(struct parse_ctx *ctx)
611 : {
612 : size_t len;
613 : heim_object_t o;
614 :
615 0 : if (white_spaces(ctx))
616 0 : return NULL;
617 :
618 0 : if (*ctx->p == '"') {
619 0 : return parse_string(ctx);
620 0 : } else if (*ctx->p == '{') {
621 0 : if (ctx->depth-- == 1) {
622 0 : ctx->error = heim_error_create(EINVAL, "JSON object too deep");
623 0 : return NULL;
624 : }
625 0 : o = parse_dict(ctx);
626 0 : ctx->depth++;
627 0 : return o;
628 0 : } else if (*ctx->p == '[') {
629 0 : if (ctx->depth-- == 1) {
630 0 : ctx->error = heim_error_create(EINVAL, "JSON object too deep");
631 0 : return NULL;
632 : }
633 0 : o = parse_array(ctx);
634 0 : ctx->depth++;
635 0 : return o;
636 0 : } else if (is_number(*ctx->p) || *ctx->p == '-') {
637 0 : return parse_number(ctx);
638 : }
639 :
640 0 : len = ctx->pend - ctx->p;
641 :
642 0 : if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
643 0 : len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
644 0 : ctx->p += 6;
645 0 : return heim_null_create();
646 0 : } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
647 0 : ctx->p += 4;
648 0 : return heim_null_create();
649 0 : } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
650 0 : ctx->p += 4;
651 0 : return heim_bool_create(1);
652 0 : } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
653 0 : ctx->p += 5;
654 0 : return heim_bool_create(0);
655 : }
656 :
657 0 : ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
658 0 : (char)*ctx->p,
659 0 : (unsigned long)(ctx->p - ctx->pstart),
660 : ctx->lineno);
661 0 : return NULL;
662 : }
663 :
664 :
665 : heim_object_t
666 0 : heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
667 : heim_error_t *error)
668 : {
669 0 : return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
670 : error);
671 : }
672 :
673 : heim_object_t
674 0 : heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
675 : heim_json_flags_t flags, heim_error_t *error)
676 : {
677 : struct parse_ctx ctx;
678 : heim_object_t o;
679 :
680 0 : heim_base_once_f(&heim_json_once, NULL, json_init_once);
681 :
682 0 : ctx.lineno = 1;
683 0 : ctx.p = data;
684 0 : ctx.pstart = data;
685 0 : ctx.pend = ((uint8_t *)data) + length;
686 0 : ctx.error = NULL;
687 0 : ctx.flags = flags;
688 0 : ctx.depth = max_depth;
689 :
690 0 : o = parse_value(&ctx);
691 :
692 0 : if (o == NULL && error) {
693 0 : *error = ctx.error;
694 0 : } else if (ctx.error) {
695 0 : heim_release(ctx.error);
696 : }
697 :
698 0 : return o;
699 : }
700 :
701 :
702 : static void
703 0 : show_printf(void *ctx, const char *str)
704 : {
705 0 : if (str == NULL)
706 0 : return;
707 0 : fprintf(ctx, "%s", str);
708 : }
709 :
710 : /**
711 : * Dump a heimbase object to stderr (useful from the debugger!)
712 : *
713 : * @param obj object to dump using JSON or JSON-like format
714 : *
715 : * @addtogroup heimbase
716 : */
717 : void
718 0 : heim_show(heim_object_t obj)
719 : {
720 0 : heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
721 0 : }
722 :
723 : static void
724 0 : strbuf_add(void *ctx, const char *str)
725 : {
726 0 : struct heim_strbuf *strbuf = ctx;
727 : size_t len;
728 :
729 0 : if (strbuf->enomem)
730 0 : return;
731 :
732 0 : if (str == NULL) {
733 : /*
734 : * Eat the last '\n'; this is used when formatting dict pairs
735 : * and array items so that the ',' separating them is never
736 : * preceded by a '\n'.
737 : */
738 0 : if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
739 0 : strbuf->len--;
740 0 : return;
741 : }
742 :
743 0 : len = strlen(str);
744 0 : if ((len + 1) > (strbuf->alloced - strbuf->len)) {
745 0 : size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
746 : char *s;
747 :
748 0 : s = realloc(strbuf->str, new_len);
749 0 : if (s == NULL) {
750 0 : strbuf->enomem = 1;
751 0 : return;
752 : }
753 0 : strbuf->str = s;
754 0 : strbuf->alloced = new_len;
755 : }
756 : /* +1 so we copy the NUL */
757 0 : (void) memcpy(strbuf->str + strbuf->len, str, len + 1);
758 0 : strbuf->len += len;
759 0 : if (strbuf->str[strbuf->len - 1] == '\n' &&
760 0 : strbuf->flags & HEIM_JSON_F_ONE_LINE)
761 0 : strbuf->len--;
762 : }
763 :
764 : #define STRBUF_INIT_SZ 64
765 :
766 : heim_string_t
767 0 : heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
768 : {
769 : heim_string_t str;
770 : struct heim_strbuf strbuf;
771 : int ret;
772 :
773 0 : if (error)
774 0 : *error = NULL;
775 :
776 0 : memset(&strbuf, 0, sizeof (strbuf));
777 0 : strbuf.str = malloc(STRBUF_INIT_SZ);
778 0 : if (strbuf.str == NULL) {
779 0 : if (error)
780 0 : *error = heim_error_create_enomem();
781 0 : return NULL;
782 : }
783 0 : strbuf.len = 0;
784 0 : strbuf.alloced = STRBUF_INIT_SZ;
785 0 : strbuf.str[0] = '\0';
786 0 : strbuf.flags = flags;
787 :
788 0 : ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
789 0 : if (ret || strbuf.enomem) {
790 0 : if (error) {
791 0 : if (strbuf.enomem || ret == ENOMEM)
792 0 : *error = heim_error_create_enomem();
793 : else
794 0 : *error = heim_error_create(1, "Impossible to JSON-encode "
795 : "object");
796 : }
797 0 : free(strbuf.str);
798 0 : return NULL;
799 : }
800 0 : if (flags & HEIM_JSON_F_ONE_LINE) {
801 0 : strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
802 0 : strbuf_add(&strbuf, "\n");
803 : }
804 0 : str = heim_string_ref_create(strbuf.str, free);
805 0 : if (str == NULL) {
806 0 : if (error)
807 0 : *error = heim_error_create_enomem();
808 0 : free(strbuf.str);
809 : }
810 0 : return str;
811 : }
|