cli: prevent segfault if input file failed to open
[project/jsonpath.git] / main.c
1 /*
2 * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
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 <stdio.h>
18 #include <stdbool.h>
19 #include <unistd.h>
20 #include <errno.h>
21
22 #ifdef JSONC
23 #include <json.h>
24 #else
25 #include <json-c/json.h>
26 #endif
27
28 #include <libubox/list.h>
29
30 #include "lexer.h"
31 #include "parser.h"
32 #include "matcher.h"
33
34
35 struct match_item {
36 struct json_object *jsobj;
37 struct list_head list;
38 };
39
40 static struct json_object *
41 parse_json(FILE *fd, const char *source, const char **error)
42 {
43 int len;
44 char buf[256];
45 struct json_object *obj = NULL;
46 struct json_tokener *tok = json_tokener_new();
47 enum json_tokener_error err = json_tokener_continue;
48
49 if (!tok)
50 return NULL;
51
52 if (source)
53 {
54 obj = json_tokener_parse_ex(tok, source, strlen(source));
55 err = json_tokener_get_error(tok);
56 }
57 else
58 {
59 while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
60 {
61 obj = json_tokener_parse_ex(tok, buf, len);
62 err = json_tokener_get_error(tok);
63
64 if (!err || err != json_tokener_continue)
65 break;
66 }
67 }
68
69 json_tokener_free(tok);
70
71 if (err)
72 {
73 if (err == json_tokener_continue)
74 err = json_tokener_error_parse_eof;
75
76 *error = json_tokener_error_desc(err);
77 return NULL;
78 }
79
80 return obj;
81 }
82
83 static void
84 print_string(const char *s)
85 {
86 const char *p;
87
88 printf("'");
89
90 for (p = s; *p; p++)
91 {
92 if (*p == '\'')
93 printf("'\"'\"'");
94 else
95 printf("%c", *p);
96 }
97
98 printf("'");
99 }
100
101 static void
102 print_separator(const char *sep, int *sc, int sl)
103 {
104 if (*sc > 0)
105 {
106 switch (sep[(*sc - 1) % sl])
107 {
108 case '"':
109 printf("'\"'");
110 break;
111
112 case '\'':
113 printf("\"'\"");
114 break;
115
116 case ' ':
117 printf("\\ ");
118 break;
119
120 default:
121 printf("%c", sep[(*sc - 1) % sl]);
122 }
123 }
124
125 (*sc)++;
126 }
127
128 static void
129 export_value(struct list_head *matches, const char *prefix, const char *sep,
130 int limit)
131 {
132 int n, len;
133 int sc = 0, sl = strlen(sep);
134 struct match_item *item;
135
136 if (list_empty(matches))
137 return;
138
139 if (prefix)
140 {
141 printf("export %s=", prefix);
142
143 list_for_each_entry(item, matches, list)
144 {
145 if (limit-- <= 0)
146 break;
147
148 switch (json_object_get_type(item->jsobj))
149 {
150 case json_type_object:
151 ; /* a label can only be part of a statement */
152 json_object_object_foreach(item->jsobj, key, val)
153 {
154 if (!val)
155 continue;
156
157 print_separator(sep, &sc, sl);
158 print_string(key);
159 }
160 break;
161
162 case json_type_array:
163 for (n = 0, len = json_object_array_length(item->jsobj);
164 n < len; n++)
165 {
166 print_separator(sep, &sc, sl);
167 printf("%d", n);
168 }
169 break;
170
171 case json_type_boolean:
172 print_separator(sep, &sc, sl);
173 printf("%d", json_object_get_boolean(item->jsobj));
174 break;
175
176 case json_type_int:
177 print_separator(sep, &sc, sl);
178 printf("%d", json_object_get_int(item->jsobj));
179 break;
180
181 case json_type_double:
182 print_separator(sep, &sc, sl);
183 printf("%f", json_object_get_double(item->jsobj));
184 break;
185
186 case json_type_string:
187 print_separator(sep, &sc, sl);
188 print_string(json_object_get_string(item->jsobj));
189 break;
190
191 case json_type_null:
192 break;
193 }
194 }
195
196 printf("; ");
197 }
198 else
199 {
200 list_for_each_entry(item, matches, list)
201 {
202 if (limit-- <= 0)
203 break;
204
205 switch (json_object_get_type(item->jsobj))
206 {
207 case json_type_object:
208 case json_type_array:
209 case json_type_boolean:
210 case json_type_int:
211 case json_type_double:
212 printf("%s\n", json_object_to_json_string(item->jsobj));
213 break;
214
215 case json_type_string:
216 printf("%s\n", json_object_get_string(item->jsobj));
217 break;
218
219 case json_type_null:
220 break;
221 }
222 }
223 }
224 }
225
226 static void
227 export_type(struct list_head *matches, const char *prefix, int limit)
228 {
229 bool first = true;
230 struct match_item *item;
231 const char *types[] = {
232 "null",
233 "boolean",
234 "double",
235 "int",
236 "object",
237 "array",
238 "string"
239 };
240
241 if (list_empty(matches))
242 return;
243
244 if (prefix)
245 printf("export %s=", prefix);
246
247 list_for_each_entry(item, matches, list)
248 {
249 if (!first)
250 printf("\\ ");
251
252 if (limit-- <= 0)
253 break;
254
255 printf("%s", types[json_object_get_type(item->jsobj)]);
256 first = false;
257 }
258
259 if (prefix)
260 printf("; ");
261 else
262 printf("\n");
263 }
264
265 static void
266 match_cb(struct json_object *res, void *priv)
267 {
268 struct list_head *h = priv;
269 struct match_item *i = calloc(1, sizeof(*i));
270
271 if (i)
272 {
273 i->jsobj = res;
274 list_add_tail(&i->list, h);
275 }
276 }
277
278 static void
279 print_error(struct jp_state *state, char *expr)
280 {
281 int i;
282 bool first = true;
283
284 fprintf(stderr, "Syntax error: ");
285
286 switch (state->error_code)
287 {
288 case -4:
289 fprintf(stderr, "Unexpected character\n");
290 break;
291
292 case -3:
293 fprintf(stderr, "String or label literal too long\n");
294 break;
295
296 case -2:
297 fprintf(stderr, "Invalid escape sequence\n");
298 break;
299
300 case -1:
301 fprintf(stderr, "Unterminated string\n");
302 break;
303
304 default:
305 for (i = 0; i < sizeof(state->error_code) * 8; i++)
306 {
307 if (state->error_code & (1 << i))
308 {
309 fprintf(stderr,
310 first ? "Expecting %s" : " or %s", tokennames[i]);
311
312 first = false;
313 }
314 }
315
316 fprintf(stderr, "\n");
317 break;
318 }
319
320 fprintf(stderr, "In expression %s\n", expr);
321 fprintf(stderr, "Near here ----");
322
323 for (i = 0; i < state->error_pos; i++)
324 fprintf(stderr, "-");
325
326 fprintf(stderr, "^\n");
327 }
328
329 static bool
330 filter_json(int opt, struct json_object *jsobj, char *expr, const char *sep,
331 int limit)
332 {
333 struct jp_state *state;
334 const char *prefix = NULL;
335 struct list_head matches;
336 struct match_item *item, *tmp;
337 struct json_object *res = NULL;
338
339 state = jp_parse(expr);
340
341 if (!state)
342 {
343 fprintf(stderr, "Out of memory\n");
344 goto out;
345 }
346 else if (state->error_code)
347 {
348 print_error(state, expr);
349 goto out;
350 }
351
352 INIT_LIST_HEAD(&matches);
353
354 res = jp_match(state->path, jsobj, match_cb, &matches);
355 prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
356
357 switch (opt)
358 {
359 case 't':
360 export_type(&matches, prefix, limit);
361 break;
362
363 default:
364 export_value(&matches, prefix, sep, limit);
365 break;
366 }
367
368 list_for_each_entry_safe(item, tmp, &matches, list)
369 free(item);
370
371 out:
372 if (state)
373 jp_free(state);
374
375 return !!res;
376 }
377
378 int main(int argc, char **argv)
379 {
380 int opt, rv = 0, limit = 0x7FFFFFFF;
381 FILE *input = stdin;
382 struct json_object *jsobj = NULL;
383 const char *jserr = NULL, *source = NULL, *separator = " ";
384
385 while ((opt = getopt(argc, argv, "i:s:e:t:F:l:q")) != -1)
386 {
387 switch (opt)
388 {
389 case 'i':
390 input = fopen(optarg, "r");
391
392 if (!input)
393 {
394 fprintf(stderr, "Failed to open %s: %s\n",
395 optarg, strerror(errno));
396
397 rv = 125;
398 goto out;
399 }
400
401 break;
402
403 case 's':
404 source = optarg;
405 break;
406
407 case 'F':
408 if (optarg && *optarg)
409 separator = optarg;
410 break;
411
412 case 'l':
413 limit = atoi(optarg);
414 break;
415
416 case 't':
417 case 'e':
418 if (!jsobj)
419 {
420 jsobj = parse_json(input, source, &jserr);
421
422 if (!jsobj)
423 {
424 fprintf(stderr, "Failed to parse json data: %s\n",
425 jserr);
426
427 rv = 126;
428 goto out;
429 }
430 }
431
432 if (!filter_json(opt, jsobj, optarg, separator, limit))
433 rv = 1;
434
435 break;
436
437 case 'q':
438 fclose(stderr);
439 break;
440 }
441 }
442
443 out:
444 if (jsobj)
445 json_object_put(jsobj);
446
447 if (input && input != stdin)
448 fclose(input);
449
450 return rv;
451 }