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 
reformat_null(void * ctx)36 static int reformat_null(void * ctx)
37 {
38     yajl_gen g = (yajl_gen) ctx;
39     GEN_AND_RETURN(yajl_gen_null(g));
40 }
41 
reformat_boolean(void * ctx,int boolean)42 static int reformat_boolean(void * ctx, int boolean)
43 {
44     yajl_gen g = (yajl_gen) ctx;
45     GEN_AND_RETURN(yajl_gen_bool(g, boolean));
46 }
47 
reformat_number(void * ctx,const char * s,size_t l)48 static int reformat_number(void * ctx, const char * s, size_t l)
49 {
50     yajl_gen g = (yajl_gen) ctx;
51     GEN_AND_RETURN(yajl_gen_number(g, s, l));
52 }
53 
reformat_string(void * ctx,const unsigned char * stringVal,size_t stringLen)54 static int reformat_string(void * ctx, const unsigned char * stringVal,
55                            size_t stringLen)
56 {
57     yajl_gen g = (yajl_gen) ctx;
58     GEN_AND_RETURN(yajl_gen_string(g, stringVal, stringLen));
59 }
60 
reformat_map_key(void * ctx,const unsigned char * stringVal,size_t stringLen)61 static int reformat_map_key(void * ctx, const unsigned char * stringVal,
62                             size_t stringLen)
63 {
64     yajl_gen g = (yajl_gen) ctx;
65     GEN_AND_RETURN(yajl_gen_string(g, stringVal, stringLen));
66 }
67 
reformat_start_map(void * ctx)68 static int reformat_start_map(void * ctx)
69 {
70     yajl_gen g = (yajl_gen) ctx;
71     GEN_AND_RETURN(yajl_gen_map_open(g));
72 }
73 
74 
reformat_end_map(void * ctx)75 static int reformat_end_map(void * ctx)
76 {
77     yajl_gen g = (yajl_gen) ctx;
78     GEN_AND_RETURN(yajl_gen_map_close(g));
79 }
80 
reformat_start_array(void * ctx)81 static int reformat_start_array(void * ctx)
82 {
83     yajl_gen g = (yajl_gen) ctx;
84     GEN_AND_RETURN(yajl_gen_array_open(g));
85 }
86 
reformat_end_array(void * ctx)87 static int reformat_end_array(void * ctx)
88 {
89     yajl_gen g = (yajl_gen) ctx;
90     GEN_AND_RETURN(yajl_gen_array_close(g));
91 }
92 
93 static yajl_callbacks callbacks = {
94     reformat_null,
95     reformat_boolean,
96     NULL,
97     NULL,
98     reformat_number,
99     reformat_string,
100     reformat_start_map,
101     reformat_map_key,
102     reformat_end_map,
103     reformat_start_array,
104     reformat_end_array
105 };
106 
107 static void
usage(const char * progname)108 usage(const char * progname)
109 {
110     fprintf(stderr, "%s: reformat json from stdin\n"
111             "usage:  json_reformat [options]\n"
112             "    -e escape any forward slashes (for embedding in HTML)\n"
113             "    -m minimize json rather than beautify (default)\n"
114             "    -s reformat a stream of multiple json entites\n"
115             "    -u allow invalid UTF8 inside strings during parsing\n",
116             progname);
117     exit(1);
118 }
119 
120 int
main(int argc,char ** argv)121 main(int argc, char ** argv)
122 {
123     yajl_handle hand;
124     static unsigned char fileData[65536];
125     /* generator config */
126     yajl_gen g;
127     yajl_status stat;
128     size_t rd;
129     int retval = 0;
130     int a = 1;
131 
132     g = yajl_gen_alloc(NULL);
133     yajl_gen_config(g, yajl_gen_beautify, 1);
134     yajl_gen_config(g, yajl_gen_validate_utf8, 1);
135 
136     /* ok.  open file.  let's read and parse */
137     hand = yajl_alloc(&callbacks, NULL, (void *) g);
138     /* and let's allow comments by default */
139     yajl_config(hand, yajl_allow_comments, 1);
140 
141     /* check arguments.*/
142     while ((a < argc) && (argv[a][0] == '-') && (strlen(argv[a]) > 1)) {
143         unsigned int i;
144         for ( i=1; i < strlen(argv[a]); i++) {
145             switch (argv[a][i]) {
146                 case 'm':
147                     yajl_gen_config(g, yajl_gen_beautify, 0);
148                     break;
149                 case 's':
150                     yajl_config(hand, yajl_allow_multiple_values, 1);
151                     s_streamReformat = 1;
152                     break;
153                 case 'u':
154                     yajl_config(hand, yajl_dont_validate_strings, 1);
155                     break;
156                 case 'e':
157                     yajl_gen_config(g, yajl_gen_escape_solidus, 1);
158                     break;
159                 default:
160                     fprintf(stderr, "unrecognized option: '%c'\n\n",
161                             argv[a][i]);
162                     usage(argv[0]);
163             }
164         }
165         ++a;
166     }
167     if (a < argc) {
168         usage(argv[0]);
169     }
170 
171 
172     for (;;) {
173         rd = fread((void *) fileData, 1, sizeof(fileData) - 1, stdin);
174 
175         if (rd == 0) {
176             if (!feof(stdin)) {
177                 fprintf(stderr, "error on file read.\n");
178                 retval = 1;
179             }
180             break;
181         }
182         fileData[rd] = 0;
183 
184         stat = yajl_parse(hand, fileData, rd);
185 
186         if (stat != yajl_status_ok) break;
187 
188         {
189             const unsigned char * buf;
190             size_t len;
191             yajl_gen_get_buf(g, &buf, &len);
192             fwrite(buf, 1, len, stdout);
193             yajl_gen_clear(g);
194         }
195     }
196 
197     stat = yajl_complete_parse(hand);
198 
199     if (stat != yajl_status_ok) {
200         unsigned char * str = yajl_get_error(hand, 1, fileData, rd);
201         fprintf(stderr, "%s", (const char *) str);
202         yajl_free_error(hand, str);
203         retval = 1;
204     }
205 
206     yajl_gen_free(g);
207     yajl_free(hand);
208 
209     return retval;
210 }
211