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