• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/jsonpath/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 }
553 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt