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