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 } 555
This page was automatically generated by LXR 0.3.1. • OpenWrt