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 | }