main: Add basic help output
[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 }