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