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