implement POSIX regexp support
[oweals/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)
150                                 break;
151
152                         if (err != json_tokener_continue)
153                                 break;
154                 }
155         }
156
157         json_tokener_free(tok);
158
159         if (err)
160         {
161                 if (err == json_tokener_continue)
162                         err = json_tokener_error_parse_eof;
163
164                 *error = json_tokener_error_desc(err);
165                 return NULL;
166         }
167
168         return array ? array : obj;
169 }
170
171 static void
172 print_string(const char *s)
173 {
174         const char *p;
175
176         printf("'");
177
178         for (p = s; *p; p++)
179         {
180                 if (*p == '\'')
181                         printf("'\"'\"'");
182                 else
183                         printf("%c", *p);
184         }
185
186         printf("'");
187 }
188
189 static void
190 print_separator(const char *sep, int *sc, int sl)
191 {
192         if (*sc > 0)
193         {
194                 switch (sep[(*sc - 1) % sl])
195                 {
196                 case '"':
197                         printf("'\"'");
198                         break;
199
200                 case '\'':
201                         printf("\"'\"");
202                         break;
203
204                 case ' ':
205                         printf("\\ ");
206                         break;
207
208                 default:
209                         printf("%c", sep[(*sc - 1) % sl]);
210                 }
211         }
212
213         (*sc)++;
214 }
215
216 static void
217 export_value(struct list_head *matches, const char *prefix, const char *sep,
218              int limit)
219 {
220         int n, len;
221         int sc = 0, sl = strlen(sep);
222         struct match_item *item;
223
224         if (list_empty(matches))
225                 return;
226
227         if (prefix)
228         {
229                 printf("export %s=", prefix);
230
231                 list_for_each_entry(item, matches, list)
232                 {
233                         if (limit-- <= 0)
234                                 break;
235
236                         switch (json_object_get_type(item->jsobj))
237                         {
238                         case json_type_object:
239                                 ; /* a label can only be part of a statement */
240                                 json_object_object_foreach(item->jsobj, key, val)
241                                 {
242                                         if (!val)
243                                                 continue;
244
245                                         print_separator(sep, &sc, sl);
246                                         print_string(key);
247                                 }
248                                 break;
249
250                         case json_type_array:
251                                 for (n = 0, len = json_object_array_length(item->jsobj);
252                                      n < len; n++)
253                                 {
254                                         print_separator(sep, &sc, sl);
255                                         printf("%d", n);
256                                 }
257                                 break;
258
259                         case json_type_boolean:
260                                 print_separator(sep, &sc, sl);
261                                 printf("%d", json_object_get_boolean(item->jsobj));
262                                 break;
263
264                         case json_type_int:
265                                 print_separator(sep, &sc, sl);
266                                 printf("%" PRId64, json_object_get_int64(item->jsobj));
267                                 break;
268
269                         case json_type_double:
270                                 print_separator(sep, &sc, sl);
271                                 printf("%f", json_object_get_double(item->jsobj));
272                                 break;
273
274                         case json_type_string:
275                                 print_separator(sep, &sc, sl);
276                                 print_string(json_object_get_string(item->jsobj));
277                                 break;
278
279                         case json_type_null:
280                                 break;
281                         }
282                 }
283
284                 printf("; ");
285         }
286         else
287         {
288                 list_for_each_entry(item, matches, list)
289                 {
290                         if (limit-- <= 0)
291                                 break;
292
293                         switch (json_object_get_type(item->jsobj))
294                         {
295                         case json_type_object:
296                         case json_type_array:
297                         case json_type_boolean:
298                         case json_type_int:
299                         case json_type_double:
300                                 printf("%s\n", json_object_to_json_string(item->jsobj));
301                                 break;
302
303                         case json_type_string:
304                                 printf("%s\n", json_object_get_string(item->jsobj));
305                                 break;
306
307                         case json_type_null:
308                                 break;
309                         }
310                 }
311         }
312 }
313
314 static void
315 export_type(struct list_head *matches, const char *prefix, int limit)
316 {
317         bool first = true;
318         struct match_item *item;
319         const char *types[] = {
320                 "null",
321                 "boolean",
322                 "double",
323                 "int",
324                 "object",
325                 "array",
326                 "string"
327         };
328
329         if (list_empty(matches))
330                 return;
331
332         if (prefix)
333                 printf("export %s=", prefix);
334
335         list_for_each_entry(item, matches, list)
336         {
337                 if (!first)
338                         printf("\\ ");
339
340                 if (limit-- <= 0)
341                         break;
342
343                 printf("%s", types[json_object_get_type(item->jsobj)]);
344                 first = false;
345         }
346
347         if (prefix)
348                 printf("; ");
349         else
350                 printf("\n");
351 }
352
353 static void
354 match_cb(struct json_object *res, void *priv)
355 {
356         struct list_head *h = priv;
357         struct match_item *i = calloc(1, sizeof(*i));
358
359         if (i)
360         {
361                 i->jsobj = res;
362                 list_add_tail(&i->list, h);
363         }
364 }
365
366 static void
367 print_error(struct jp_state *state, char *expr)
368 {
369         int i;
370         bool first = true;
371
372         fprintf(stderr, "Syntax error: ");
373
374         switch (state->error_code)
375         {
376         case -4:
377                 fprintf(stderr, "Unexpected character\n");
378                 break;
379
380         case -3:
381                 fprintf(stderr, "String or label literal too long\n");
382                 break;
383
384         case -2:
385                 fprintf(stderr, "Invalid escape sequence\n");
386                 break;
387
388         case -1:
389                 fprintf(stderr, "Unterminated string\n");
390                 break;
391
392         default:
393                 for (i = 0; i < sizeof(state->error_code) * 8; i++)
394                 {
395                         if (state->error_code & (1 << i))
396                         {
397                                 fprintf(stderr,
398                                         first ? "Expecting %s" : " or %s", tokennames[i]);
399
400                                 first = false;
401                         }
402                 }
403
404                 fprintf(stderr, "\n");
405                 break;
406         }
407
408         fprintf(stderr, "In expression %s\n", expr);
409         fprintf(stderr, "Near here ----");
410
411         for (i = 0; i < state->error_pos; i++)
412                 fprintf(stderr, "-");
413
414         fprintf(stderr, "^\n");
415 }
416
417 static bool
418 filter_json(int opt, struct json_object *jsobj, char *expr, const char *sep,
419             int limit)
420 {
421         struct jp_state *state;
422         const char *prefix = NULL;
423         struct list_head matches;
424         struct match_item *item, *tmp;
425         struct json_object *res = NULL;
426
427         state = jp_parse(expr);
428
429         if (!state)
430         {
431                 fprintf(stderr, "Out of memory\n");
432                 goto out;
433         }
434         else if (state->error_code)
435         {
436                 print_error(state, expr);
437                 goto out;
438         }
439
440         INIT_LIST_HEAD(&matches);
441
442         res = jp_match(state->path, jsobj, match_cb, &matches);
443         prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
444
445         switch (opt)
446         {
447         case 't':
448                 export_type(&matches, prefix, limit);
449                 break;
450
451         default:
452                 export_value(&matches, prefix, sep, limit);
453                 break;
454         }
455
456         list_for_each_entry_safe(item, tmp, &matches, list)
457                 free(item);
458
459 out:
460         if (state)
461                 jp_free(state);
462
463         return !!res;
464 }
465
466 int main(int argc, char **argv)
467 {
468         bool array_mode = false;
469         int opt, rv = 0, limit = 0x7FFFFFFF;
470         FILE *input = stdin;
471         struct json_object *jsobj = NULL;
472         const char *jserr = NULL, *source = NULL, *separator = " ";
473
474         if (argc == 1)
475         {
476                 print_usage(argv[0]);
477                 goto out;
478         }
479
480         while ((opt = getopt(argc, argv, "ahi:s:e:t:F:l:q")) != -1)
481         {
482                 switch (opt)
483                 {
484                 case 'a':
485                         array_mode = true;
486                         break;
487
488                 case 'h':
489                         print_usage(argv[0]);
490                         goto out;
491
492                 case 'i':
493                         input = fopen(optarg, "r");
494
495                         if (!input)
496                         {
497                                 fprintf(stderr, "Failed to open %s: %s\n",
498                                                 optarg, strerror(errno));
499
500                                 rv = 125;
501                                 goto out;
502                         }
503
504                         break;
505
506                 case 's':
507                         source = optarg;
508                         break;
509
510                 case 'F':
511                         if (optarg && *optarg)
512                                 separator = optarg;
513                         break;
514
515                 case 'l':
516                         limit = atoi(optarg);
517                         break;
518
519                 case 't':
520                 case 'e':
521                         if (!jsobj)
522                         {
523                                 jsobj = parse_json(input, source, &jserr, array_mode);
524
525                                 if (!jsobj)
526                                 {
527                                         fprintf(stderr, "Failed to parse json data: %s\n",
528                                                 jserr);
529
530                                         rv = 126;
531                                         goto out;
532                                 }
533                         }
534
535                         if (!filter_json(opt, jsobj, optarg, separator, limit))
536                                 rv = 1;
537
538                         break;
539
540                 case 'q':
541                         fclose(stderr);
542                         break;
543                 }
544         }
545
546 out:
547         if (jsobj)
548                 json_object_put(jsobj);
549
550         if (input && input != stdin)
551                 fclose(input);
552
553         return rv;
554 }