1    | /*
2    |  * Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
3    |  *
4    |  * Permission to use, copy, modify, and/or distribute this software for any
5    |  * purpose with or without fee is hereby granted, provided that the above
6    |  * copyright notice and this permission notice appear in all copies.
7    |  *
8    |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9    |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15   |  */
16   | 
17   | #include <yajl/yajl_parse.h>
18   | #include <yajl/yajl_gen.h>
19   | 
20   | #include <stdio.h>
21   | #include <stdlib.h>
22   | #include <string.h>
23   | 
24   | /* non-zero when we're reformatting a stream */
25   | static int s_streamReformat = 0;
26   | 
27   | #define GEN_AND_RETURN(func)                                          \
28   |     {                                                                 \
29   |         yajl_gen_status __stat = func;                                \
30   |         if (__stat == yajl_gen_generation_complete && s_streamReformat) { \
31   |             yajl_gen_reset(g, "\n");                                    \
32   |             __stat = func;                                              \
33   |         }                                                               \
34   |         return __stat == yajl_gen_status_ok;                            \
35   |     }
36   | 
37   | static int reformat_null(void * ctx)
38   | {
39   |     yajl_gen g = (yajl_gen) ctx;
40   |     GEN_AND_RETURN(yajl_gen_null(g));
41   | }
42   | 
43   | static int reformat_boolean(void * ctx, int boolean)
44   | {
45   |     yajl_gen g = (yajl_gen) ctx;
46   |     GEN_AND_RETURN(yajl_gen_bool(g, boolean));
47   | }
48   | 
49   | static int reformat_number(void * ctx, const char * s, size_t l)
50   | {
51   |     yajl_gen g = (yajl_gen) ctx;
52   |     GEN_AND_RETURN(yajl_gen_number(g, s, l));
53   | }
54   | 
55   | static int reformat_string(void * ctx, const unsigned char * stringVal,
56   |                            size_t stringLen)
57   | {
58   |     yajl_gen g = (yajl_gen) ctx;
59   |     GEN_AND_RETURN(yajl_gen_string(g, stringVal, stringLen));
60   | }
61   | 
62   | static int reformat_map_key(void * ctx, const unsigned char * stringVal,
63   |                             size_t stringLen)
64   | {
65   |     yajl_gen g = (yajl_gen) ctx;
66   |     GEN_AND_RETURN(yajl_gen_string(g, stringVal, stringLen));
67   | }
68   | 
69   | static int reformat_start_map(void * ctx)
70   | {
71   |     yajl_gen g = (yajl_gen) ctx;
72   |     GEN_AND_RETURN(yajl_gen_map_open(g));
73   | }
74   | 
75   | 
76   | static int reformat_end_map(void * ctx)
77   | {
78   |     yajl_gen g = (yajl_gen) ctx;
79   |     GEN_AND_RETURN(yajl_gen_map_close(g));
80   | }
81   | 
82   | static int reformat_start_array(void * ctx)
83   | {
84   |     yajl_gen g = (yajl_gen) ctx;
85   |     GEN_AND_RETURN(yajl_gen_array_open(g));
86   | }
87   | 
88   | static int reformat_end_array(void * ctx)
89   | {
90   |     yajl_gen g = (yajl_gen) ctx;
91   |     GEN_AND_RETURN(yajl_gen_array_close(g));
92   | }
93   | 
94   | static yajl_callbacks callbacks = {
95   |     reformat_null,
96   |     reformat_boolean,
97   |     NULL,
98   |     NULL,
99   |     reformat_number,
100  |     reformat_string,
101  |     reformat_start_map,
102  |     reformat_map_key,
103  |     reformat_end_map,
104  |     reformat_start_array,
105  |     reformat_end_array
106  | };
107  | 
108  | #ifndef EXIT_USAGE
109  | # define EXIT_USAGE	2
110  | #endif
111  | 
112  | static void
113  | usage(const char * progname)
114  | {
115  |     fprintf(stderr, "%s: reformat json from stdin\n"
116  |             "usage:  json_reformat [options]\n"
117  |             "    -e escape any forward slashes (for embedding in HTML)\n"
118  |             "    -m minimize json rather than beautify (default)\n"
119  |             "    -s reformat a stream of multiple json entites\n"
120  |             "    -u allow invalid UTF8 inside strings during parsing\n",
121  |             progname);
122  |     exit(EXIT_USAGE);
123  | }
124  | 
125  | int
126  | main(int argc, char ** argv)
127  | {
128  |     yajl_handle hand;
129  |     static unsigned char fileData[65536];
130  |     /* generator config */
131  |     yajl_gen g;
132  |     yajl_status stat;
133  |     size_t rd;
134  |     int retval = 0;
135  |     int a = 1;
136  | 
137  |     g = yajl_gen_alloc(NULL);
138  |     yajl_gen_config(g, yajl_gen_beautify, 1);
139  |     yajl_gen_config(g, yajl_gen_validate_utf8, 1);
140  | 
141  |     /* ok.  open file.  let's read and parse */
142  |     hand = yajl_alloc(&callbacks, NULL, (void *) g);
143  |     /* and let's allow comments by default */
144  |     yajl_config(hand, yajl_allow_comments, 1);
145  | 
146  |     /* check arguments.*/
147  |     while ((a < argc) && (argv[a][0] == '-') && (strlen(argv[a]) > 1)) {
148  |         unsigned int i;
149  |         for ( i=1; i < strlen(argv[a]); i++) {
150  |             switch (argv[a][i]) {
151  |                 case 'm':
152  |                     yajl_gen_config(g, yajl_gen_beautify, 0);
153  |                     break;
154  |                 case 's':
155  |                     yajl_config(hand, yajl_allow_multiple_values, 1);
156  |                     s_streamReformat = 1;
157  |                     break;
158  |                 case 'u':
159  |                     yajl_config(hand, yajl_dont_validate_strings, 1);
160  |                     break;
161  |                 case 'e':
162  |                     yajl_gen_config(g, yajl_gen_escape_solidus, 1);
163  |                     break;
164  |                 default:
165  |                     fprintf(stderr, "unrecognized option: '%c'\n\n",
166  |                             argv[a][i]);
167  |                     usage(argv[0]);
168  |             }
169  |         }
170  |         ++a;
171  |     }
172  |     if (a < argc) {
173  |         usage(argv[0]);
174  |     }
175  | 
176  |     for (;;) {
177  |         rd = fread((void *) fileData, (size_t) 1, sizeof(fileData) - 1, stdin);
178  | 
179  |         if (rd == 0) {
180  |             if (!feof(stdin)) {
181  |                 fprintf(stderr, "error on file read.\n");
182  |                 retval = 1;
183  |             }
184  |             break;
185  |         }
186  |         fileData[rd] = 0;
187  | 
188  |         stat = yajl_parse(hand, fileData, rd);
189  | 
190  |         if (stat != yajl_status_ok) {
191  |             break;
192  |         } else {
193  |             const unsigned char * buf;
194  |             size_t len;
195  |             yajl_gen_get_buf(g, &buf, &len);
196  |             fwrite(buf, (size_t) 1, len, stdout);
197  |             yajl_gen_clear(g);
198  |         }
199  |     }
200  | 
201  |     if (stat != yajl_status_ok) {
202  |         unsigned char * str = yajl_get_error(hand, 1, fileData, rd);
203  |         fprintf(stderr, "%s", (const char *) str);
204  |         yajl_free_error(hand, str);
205  |         retval = 1;
206  |     } else {
207  |         stat = yajl_complete_parse(hand);
208  |     }
209  | 
210  |     yajl_gen_free(g);
211  |     yajl_free(hand);
212  | 
213  |     return retval;
214  | }