main: fix spurious premature parse aborts in array mode
[project/jsonpath.git] / main.c
1 /*
2 * Copyright (C) 2013-2014 Jo-Philipp Wich <jo@mein.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 <stdio.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <unistd.h>
21 #include <errno.h>
22
23 #ifdef JSONC
24 #include <json.h>
25 #else
26 #include <json-c/json.h>
27 #endif
28
29 #include <libubox/list.h>
30
31 #include "lexer.h"
32 #include "parser.h"
33 #include "matcher.h"
34
35
36 struct match_item {
37 struct json_object *jsobj;
38 struct list_head list;
39 };
40
41 static void
42 print_usage(char *app)
43 {
44 printf(
45 "== Usage ==\n\n"
46 " # %s [-a] [-i <file> | -s \"json...\"] {-t <pattern> | -e <pattern>}\n"
47 " -q Quiet, no errors are printed\n"
48 " -h, --help Print this help\n"
49 " -a Implicitely treat input as array, useful for JSON logs\n"
50 " -i path Specify a JSON file to parse\n"
51 " -s \"json\" Specify a JSON string to parse\n"
52 " -l limit Specify max number of results to show\n"
53 " -F separator Specify a field separator when using export\n"
54 " -t <pattern> Print the type of values matched by pattern\n"
55 " -e <pattern> Print the values matched by pattern\n"
56 " -e VAR=<pat> Serialize matched value for shell \"eval\"\n\n"
57 "== Patterns ==\n\n"
58 " Patterns are JsonPath: http://goessner.net/articles/JsonPath/\n"
59 " This tool implements $, @, [], * and the union operator ','\n"
60 " plus the usual expressions and literals.\n"
61 " It does not support the recursive child search operator '..' or\n"
62 " the '?()' and '()' filter expressions as those would require a\n"
63 " complete JavaScript engine to support them.\n\n"
64 "== Examples ==\n\n"
65 " Display the first IPv4 address on lan:\n"
66 " # ifstatus lan | %s -e '@[\"ipv4-address\"][0].address'\n\n"
67 " Extract the release string from the board information:\n"
68 " # ubus call system board | %s -e '@.release.description'\n\n"
69 " Find all interfaces which are up:\n"
70 " # ubus call network.interface dump | \\\n"
71 " %s -e '@.interface[@.up=true].interface'\n\n"
72 " Export br-lan traffic counters for shell eval:\n"
73 " # devstatus br-lan | %s -e 'RX=@.statistics.rx_bytes' \\\n"
74 " -e 'TX=@.statistics.tx_bytes'\n",
75 app, app, app, app, app);
76 }
77
78 static struct json_object *
79 parse_json_chunk(struct json_tokener *tok, struct json_object *array,
80 const char *buf, size_t len, enum json_tokener_error *err)
81 {
82 struct json_object *obj = NULL;
83
84 while (len)
85 {
86 obj = json_tokener_parse_ex(tok, buf, len);
87 *err = json_tokener_get_error(tok);
88
89 if (*err == json_tokener_success)
90 {
91 if (array)
92 {
93 json_object_array_add(array, obj);
94 }
95 else
96 {
97 break;
98 }
99 }
100 else if (*err != json_tokener_continue)
101 {
102 break;
103 }
104
105 buf += tok->char_offset;
106 len -= tok->char_offset;
107 }
108
109 return obj;
110 }
111
112 static struct json_object *
113 parse_json(FILE *fd, const char *source, const char **error, bool array_mode)
114 {
115 size_t len;
116 char buf[256];
117 struct json_object *obj = NULL, *array = NULL;
118 struct json_tokener *tok = json_tokener_new();
119 enum json_tokener_error err = json_tokener_continue;
120
121 if (!tok)
122 {
123 *error = "Out of memory";
124 return NULL;
125 }
126
127 if (array_mode)
128 {
129 array = json_object_new_array();
130
131 if (!array)
132 {
133 json_tokener_free(tok);
134 *error = "Out of memory";
135 return NULL;
136 }
137 }
138
139 if (source)
140 {
141 obj = parse_json_chunk(tok, array, source, strlen(source), &err);
142 }
143 else
144 {
145 while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
146 {
147 obj = parse_json_chunk(tok, array, buf, len, &err);
148
149 if ((err == json_tokener_success && array_mode == false) ||
150 (err != json_tokener_continue && err != json_tokener_success))
151 break;
152 }
153 }
154
155 json_tokener_free(tok);
156
157 if (err)
158 {
159 if (err == json_tokener_continue)
160 err = json_tokener_error_parse_eof;
161
162 *error = json_tokener_error_desc(err);
163 return NULL;
164 }
165
166 return array ? array : obj;
167 }
168
169 static void
170 print_string(const char *s)
171 {
172 const char *p;
173
174 printf("'");
175
176 for (p = s; *p; p++)
177 {
178 if (*p == '\'')
179 printf("'\"'\"'");
180 else
181 printf("%c", *p);
182 }
183
184 printf("'");
185 }
186
187 static void
188 print_separator(const char *sep, int *sc, int sl)
189 {
190 if (*sc > 0)
191 {
192 switch (sep[(*sc - 1) % sl])
193 {
194 case '"':
195 printf("'\"'");
196 break;
197
198 case '\'':
199 printf("\"'\"");
200 break;
201
202 case ' ':
203 printf("\\ ");
204 break;
205
206 default:
207 printf("%c", sep[(*sc - 1) % sl]);
208 }
209 }
210
211 (*sc)++;
212 }
213
214 static void
215 export_value(struct list_head *matches, const char *prefix, const char *sep,
216 int limit)
217 {
218 int n, len;
219 int sc = 0, sl = strlen(sep);
220 struct match_item *item;
221
222 if (list_empty(matches))
223 return;
224
225 if (prefix)
226 {
227 printf("export %s=", prefix);
228
229 list_for_each_entry(item, matches, list)
230 {
231 if (limit-- <= 0)
232 break;
233
234 switch (json_object_get_type(item->jsobj))
235 {
236 case json_type_object:
237 ; /* a label can only be part of a statement */
238 json_object_object_foreach(item->jsobj, key, val)
239 {
240 if (!val)
241 continue;
242
243 print_separator(sep, &sc, sl);
244 print_string(key);
245 }
246 break;
247
248 case json_type_array:
249 for (n = 0, len = json_object_array_length(item->jsobj);
250 n < len; n++)
251 {
252 print_separator(sep, &sc, sl);
253 printf("%d", n);
254 }
255 break;
256
257 case json_type_boolean:
258 print_separator(sep, &sc, sl);
259 printf("%d", json_object_get_boolean(item->jsobj));
260 break;
261
262 case json_type_int:
263 print_separator(sep, &sc, sl);
264 printf("%" PRId64, json_object_get_int64(item->jsobj));
265 break;
266
267 case json_type_double:
268 print_separator(sep, &sc, sl);
269 printf("%f", json_object_get_double(item->jsobj));
270 break;
271
272 case json_type_string:
273 print_separator(sep, &sc, sl);
274 print_string(json_object_get_string(item->jsobj));
275 break;
276
277 case json_type_null:
278 break;
279 }
280 }
281
282 printf("; ");
283 }
284 else
285 {
286 list_for_each_entry(item, matches, list)
287 {
288 if (limit-- <= 0)
289 break;
290
291 switch (json_object_get_type(item->jsobj))
292 {
293 case json_type_object:
294 case json_type_array:
295 case json_type_boolean:
296 case json_type_int:
297 case json_type_double:
298 printf("%s\n", json_object_to_json_string(item->jsobj));
299 break;
300
301 case json_type_string:
302 printf("%s\n", json_object_get_string(item->jsobj));
303 break;
304
305 case json_type_null:
306 break;
307 }
308 }
309 }
310 }
311
312 static void
313 export_type(struct list_head *matches, const char *prefix, int limit)
314 {
315 bool first = true;
316 struct match_item *item;
317 const char *types[] = {
318 "null",
319 "boolean",
320 "double",
321 "int",
322 "object",
323 "array",
324 "string"
325 };
326
327 if (list_empty(matches))
328 return;
329
330 if (prefix)
331 printf("export %s=", prefix);
332
333 list_for_each_entry(item, matches, list)
334 {
335 if (!first)
336 printf("\\ ");
337
338 if (limit-- <= 0)
339 break;
340
341 printf("%s", types[json_object_get_type(item->jsobj)]);
342 first = false;
343 }
344
345 if (prefix)
346 printf("; ");
347 else
348 printf("\n");
349 }
350
351 static void
352 match_cb(struct json_object *res, void *priv)
353 {
354 struct list_head *h = priv;
355 struct match_item *i = calloc(1, sizeof(*i));
356
357 if (i)
358 {
359 i->jsobj = res;
360 list_add_tail(&i->list, h);
361 }
362 }
363
364 static void
365 print_error(struct jp_state *state, char *expr)
366 {
367 int i;
368 bool first = true;
369
370 fprintf(stderr, "Syntax error: ");
371
372 switch (state->error_code)
373 {
374 case -4:
375 fprintf(stderr, "Unexpected character\n");
376 break;
377
378 case -3:
379 fprintf(stderr, "String or label literal too long\n");
380 break;
381
382 case -2:
383 fprintf(stderr, "Invalid escape sequence\n");
384 break;
385
386 case -1:
387 fprintf(stderr, "Unterminated string\n");
388 break;
389
390 default:
391 for (i = 0; i < sizeof(state->error_code) * 8; i++)
392 {
393 if (state->error_code & (1 << i))
394 {
395 fprintf(stderr,
396 first ? "Expecting %s" : " or %s", tokennames[i]);
397
398 first = false;
399 }
400 }
401
402 fprintf(stderr, "\n");
403 break;
404 }
405
406 fprintf(stderr, "In expression %s\n", expr);
407 fprintf(stderr, "Near here ----");
408
409 for (i = 0; i < state->error_pos; i++)
410 fprintf(stderr, "-");
411
412 fprintf(stderr, "^\n");
413 }
414
415 static bool
416 filter_json(int opt, struct json_object *jsobj, char *expr, const char *sep,
417 int limit)
418 {
419 struct jp_state *state;
420 const char *prefix = NULL;
421 struct list_head matches;
422 struct match_item *item, *tmp;
423 struct json_object *res = NULL;
424
425 state = jp_parse(expr);
426
427 if (!state)
428 {
429 fprintf(stderr, "Out of memory\n");
430 goto out;
431 }
432 else if (state->error_code)
433 {
434 print_error(state, expr);
435 goto out;
436 }
437
438 INIT_LIST_HEAD(&matches);
439
440 res = jp_match(state->path, jsobj, match_cb, &matches);
441 prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
442
443 switch (opt)
444 {
445 case 't':
446 export_type(&matches, prefix, limit);
447 break;
448
449 default:
450 export_value(&matches, prefix, sep, limit);
451 break;
452 }
453
454 list_for_each_entry_safe(item, tmp, &matches, list)
455 free(item);
456
457 out:
458 if (state)
459 jp_free(state);
460
461 return !!res;
462 }
463
464 int main(int argc, char **argv)
465 {
466 bool array_mode = false;
467 int opt, rv = 0, limit = 0x7FFFFFFF;
468 FILE *input = stdin;
469 struct json_object *jsobj = NULL;
470 const char *jserr = NULL, *source = NULL, *separator = " ";
471
472 if (argc == 1)
473 {
474 print_usage(argv[0]);
475 goto out;
476 }
477
478 while ((opt = getopt(argc, argv, "ahi:s:e:t:F:l:q")) != -1)
479 {
480 switch (opt)
481 {
482 case 'a':
483 array_mode = true;
484 break;
485
486 case 'h':
487 print_usage(argv[0]);
488 goto out;
489
490 case 'i':
491 input = fopen(optarg, "r");
492
493 if (!input)
494 {
495 fprintf(stderr, "Failed to open %s: %s\n",
496 optarg, strerror(errno));
497
498 rv = 125;
499 goto out;
500 }
501
502 break;
503
504 case 's':
505 source = optarg;
506 break;
507
508 case 'F':
509 if (optarg && *optarg)
510 separator = optarg;
511 break;
512
513 case 'l':
514 limit = atoi(optarg);
515 break;
516
517 case 't':
518 case 'e':
519 if (!jsobj)
520 {
521 jsobj = parse_json(input, source, &jserr, array_mode);
522
523 if (!jsobj)
524 {
525 fprintf(stderr, "Failed to parse json data: %s\n",
526 jserr);
527
528 rv = 126;
529 goto out;
530 }
531 }
532
533 if (!filter_json(opt, jsobj, optarg, separator, limit))
534 rv = 1;
535
536 break;
537
538 case 'q':
539 fclose(stderr);
540 break;
541 }
542 }
543
544 out:
545 if (jsobj)
546 json_object_put(jsobj);
547
548 if (input && input != stdin)
549 fclose(input);
550
551 return rv;
552 }