1/* JSON Printer
2 * ZZJSON - Copyright (C) 2008 by Ivo van Poorten
3 * License: GNU Lesser General Public License version 2.1
4 */
5
6#include "zzjson.h"
7
8#define PRINT(fmt...) if (config->print(config->ohandle, ##fmt) < 0) return -1;
9//#define PUTC(c)       if (config->putchar(c, config->ohandle) < 0) return -1;
10#define PUTC(c)       PRINT("%c",c)
11#define INC 4
12
13static int print_string(ZZJSON_CONFIG *config, char *s) {
14    int c, bs;
15    if (!s) return 0;
16    while ((c = *s++)) {
17        bs = 1;
18        switch (c) {
19//            case '/':                 // useless escape of forward slash
20            case '\\':
21                if (*s == 'u') bs = 0;  // copy \uHHHH verbatim
22                break;
23            case '"':               break;
24            case '\b':  c = 'b';    break;
25            case '\f':  c = 'f';    break;
26            case '\n':  c = 'n';    break;
27            case '\r':  c = 'r';    break;
28            case '\t':  c = 't';    break;
29            default:    bs = 0;     break;
30        }
31        if (bs) PUTC('\\');
32        PUTC(c);
33    }
34    return 0;
35}
36
37static int zzjson_print2(ZZJSON_CONFIG *config, ZZJSON *zzjson,
38                 unsigned int indent, unsigned int objval) {
39    char c = 0, d = 0;
40    if (!zzjson) return -1;
41
42    switch(zzjson->type) {
43        case ZZJSON_OBJECT: c = '{'; d = '}'; break;
44        case ZZJSON_ARRAY:  c = '['; d = ']'; break;
45        default: break;
46    }
47
48    if (c) PRINT("%s%*s%c", indent ? "\n" : "", indent, "", c);
49
50    while (zzjson) {
51        switch(zzjson->type) {
52        case ZZJSON_OBJECT:
53            if (zzjson->value.object.val) {
54                PRINT("\n%*s\"", indent+INC, "");
55                if (print_string(config, zzjson->value.object.label) < 0)
56                    return -1;
57                PRINT("\" :");
58                if (zzjson_print2(config, zzjson->value.object.val,
59                                                indent+INC, 1) < 0) return -1;
60            }
61            break;
62        case ZZJSON_ARRAY:
63            if (zzjson->value.array.val)
64                if (zzjson_print2(config, zzjson->value.array.val,
65                                                indent+INC, 0) < 0) return -1;
66            break;
67        case ZZJSON_STRING:
68            PRINT(objval ? " \"" : "\n%*s\"", indent, "");
69            if (print_string(config, zzjson->value.string.string)<0) return -1;
70            PUTC('"');
71            break;
72        case ZZJSON_FALSE:
73            PRINT(objval ? " false" : "\n%*sfalse", indent, "");
74            break;
75        case ZZJSON_NULL:
76            PRINT(objval ? " null" : "\n%*snull", indent, "");
77            break;
78        case ZZJSON_TRUE:
79            PRINT(objval ? " true" : "\n%*strue", indent, "");
80            break;
81        case ZZJSON_NUMBER_NEGINT:
82        case ZZJSON_NUMBER_POSINT:
83        case ZZJSON_NUMBER_DOUBLE:
84            PRINT(objval ? " " : "\n%*s", indent, "");
85            if (zzjson->type == ZZJSON_NUMBER_DOUBLE) {
86                PRINT("%16.16e", zzjson->value.number.val.dval);
87            } else {
88                if (zzjson->type == ZZJSON_NUMBER_NEGINT) PUTC('-');
89                PRINT("%llu", zzjson->value.number.val.ival);
90            }
91        default:
92            break;
93        }
94        zzjson = zzjson->next;
95        if (zzjson) PUTC(',');
96    }
97
98    if (d) PRINT("\n%*s%c", indent, "", d);
99
100    return 0;
101}
102
103int zzjson_print(ZZJSON_CONFIG *config, ZZJSON *zzjson) {
104    int retval = zzjson_print2(config, zzjson, 0, 0);
105//    if (retval >= 0) retval = config->putchar('\n', config->ohandle);
106#ifndef CONFIG_NO_ERROR_MESSAGES
107    if (retval <  0) config->error(config->ehandle, "print: unable to print");
108#endif
109    return retval;
110}
111