main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  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. #include <stdio.h>
  17. #include <stdbool.h>
  18. #include <stdint.h>
  19. #include <unistd.h>
  20. #include <errno.h>
  21. #ifdef JSONC
  22. #include <json.h>
  23. #else
  24. #include <json-c/json.h>
  25. #endif
  26. #include <libubox/list.h>
  27. #include "lexer.h"
  28. #include "parser.h"
  29. #include "matcher.h"
  30. struct match_item {
  31. struct json_object *jsobj;
  32. struct list_head list;
  33. };
  34. static void
  35. print_usage(char *app)
  36. {
  37. printf(
  38. "== Usage ==\n\n"
  39. " # %s [-a] [-i <file> | -s \"json...\"] {-t <pattern> | -e <pattern>}\n"
  40. " -q Quiet, no errors are printed\n"
  41. " -h, --help Print this help\n"
  42. " -a Implicitely treat input as array, useful for JSON logs\n"
  43. " -i path Specify a JSON file to parse\n"
  44. " -s \"json\" Specify a JSON string to parse\n"
  45. " -l limit Specify max number of results to show\n"
  46. " -F separator Specify a field separator when using export\n"
  47. " -t <pattern> Print the type of values matched by pattern\n"
  48. " -e <pattern> Print the values matched by pattern\n"
  49. " -e VAR=<pat> Serialize matched value for shell \"eval\"\n\n"
  50. "== Patterns ==\n\n"
  51. " Patterns are JsonPath: http://goessner.net/articles/JsonPath/\n"
  52. " This tool implements $, @, [], * and the union operator ','\n"
  53. " plus the usual expressions and literals.\n"
  54. " It does not support the recursive child search operator '..' or\n"
  55. " the '?()' and '()' filter expressions as those would require a\n"
  56. " complete JavaScript engine to support them.\n\n"
  57. "== Examples ==\n\n"
  58. " Display the first IPv4 address on lan:\n"
  59. " # ifstatus lan | %s -e '@[\"ipv4-address\"][0].address'\n\n"
  60. " Extract the release string from the board information:\n"
  61. " # ubus call system board | %s -e '@.release.description'\n\n"
  62. " Find all interfaces which are up:\n"
  63. " # ubus call network.interface dump | \\\n"
  64. " %s -e '@.interface[@.up=true].interface'\n\n"
  65. " Export br-lan traffic counters for shell eval:\n"
  66. " # devstatus br-lan | %s -e 'RX=@.statistics.rx_bytes' \\\n"
  67. " -e 'TX=@.statistics.tx_bytes'\n",
  68. app, app, app, app, app);
  69. }
  70. static struct json_object *
  71. parse_json_chunk(struct json_tokener *tok, struct json_object *array,
  72. const char *buf, size_t len, enum json_tokener_error *err)
  73. {
  74. struct json_object *obj = NULL;
  75. while (len)
  76. {
  77. obj = json_tokener_parse_ex(tok, buf, len);
  78. *err = json_tokener_get_error(tok);
  79. if (*err == json_tokener_success)
  80. {
  81. if (array)
  82. {
  83. json_object_array_add(array, obj);
  84. }
  85. else
  86. {
  87. break;
  88. }
  89. }
  90. else if (*err != json_tokener_continue)
  91. {
  92. break;
  93. }
  94. buf += tok->char_offset;
  95. len -= tok->char_offset;
  96. }
  97. return obj;
  98. }
  99. static struct json_object *
  100. parse_json(FILE *fd, const char *source, const char **error, bool array_mode)
  101. {
  102. size_t len;
  103. char buf[256];
  104. struct json_object *obj = NULL, *array = NULL;
  105. struct json_tokener *tok = json_tokener_new();
  106. enum json_tokener_error err = json_tokener_continue;
  107. if (!tok)
  108. {
  109. *error = "Out of memory";
  110. return NULL;
  111. }
  112. if (array_mode)
  113. {
  114. array = json_object_new_array();
  115. if (!array)
  116. {
  117. json_tokener_free(tok);
  118. *error = "Out of memory";
  119. return NULL;
  120. }
  121. }
  122. if (source)
  123. {
  124. obj = parse_json_chunk(tok, array, source, strlen(source), &err);
  125. }
  126. else
  127. {
  128. while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
  129. {
  130. obj = parse_json_chunk(tok, array, buf, len, &err);
  131. if ((err == json_tokener_success && array_mode == false) ||
  132. (err != json_tokener_continue && err != json_tokener_success))
  133. break;
  134. }
  135. }
  136. json_tokener_free(tok);
  137. if (err)
  138. {
  139. if (err == json_tokener_continue)
  140. err = json_tokener_error_parse_eof;
  141. *error = json_tokener_error_desc(err);
  142. return NULL;
  143. }
  144. return array ? array : obj;
  145. }
  146. static void
  147. print_string(const char *s)
  148. {
  149. const char *p;
  150. printf("'");
  151. for (p = s; *p; p++)
  152. {
  153. if (*p == '\'')
  154. printf("'\"'\"'");
  155. else
  156. printf("%c", *p);
  157. }
  158. printf("'");
  159. }
  160. static void
  161. print_separator(const char *sep, int *sc, int sl)
  162. {
  163. if (*sc > 0)
  164. {
  165. switch (sep[(*sc - 1) % sl])
  166. {
  167. case '"':
  168. printf("'\"'");
  169. break;
  170. case '\'':
  171. printf("\"'\"");
  172. break;
  173. case ' ':
  174. printf("\\ ");
  175. break;
  176. default:
  177. printf("%c", sep[(*sc - 1) % sl]);
  178. }
  179. }
  180. (*sc)++;
  181. }
  182. static void
  183. export_value(struct list_head *matches, const char *prefix, const char *sep,
  184. int limit)
  185. {
  186. int n, len;
  187. int sc = 0, sl = strlen(sep);
  188. struct match_item *item;
  189. if (list_empty(matches))
  190. return;
  191. if (prefix)
  192. {
  193. printf("export %s=", prefix);
  194. list_for_each_entry(item, matches, list)
  195. {
  196. if (limit-- <= 0)
  197. break;
  198. switch (json_object_get_type(item->jsobj))
  199. {
  200. case json_type_object:
  201. ; /* a label can only be part of a statement */
  202. json_object_object_foreach(item->jsobj, key, val)
  203. {
  204. if (!val)
  205. continue;
  206. print_separator(sep, &sc, sl);
  207. print_string(key);
  208. }
  209. break;
  210. case json_type_array:
  211. for (n = 0, len = json_object_array_length(item->jsobj);
  212. n < len; n++)
  213. {
  214. print_separator(sep, &sc, sl);
  215. printf("%d", n);
  216. }
  217. break;
  218. case json_type_boolean:
  219. print_separator(sep, &sc, sl);
  220. printf("%d", json_object_get_boolean(item->jsobj));
  221. break;
  222. case json_type_int:
  223. print_separator(sep, &sc, sl);
  224. printf("%" PRId64, json_object_get_int64(item->jsobj));
  225. break;
  226. case json_type_double:
  227. print_separator(sep, &sc, sl);
  228. printf("%f", json_object_get_double(item->jsobj));
  229. break;
  230. case json_type_string:
  231. print_separator(sep, &sc, sl);
  232. print_string(json_object_get_string(item->jsobj));
  233. break;
  234. case json_type_null:
  235. break;
  236. }
  237. }
  238. printf("; ");
  239. }
  240. else
  241. {
  242. list_for_each_entry(item, matches, list)
  243. {
  244. if (limit-- <= 0)
  245. break;
  246. switch (json_object_get_type(item->jsobj))
  247. {
  248. case json_type_object:
  249. case json_type_array:
  250. case json_type_boolean:
  251. case json_type_int:
  252. case json_type_double:
  253. printf("%s\n", json_object_to_json_string(item->jsobj));
  254. break;
  255. case json_type_string:
  256. printf("%s\n", json_object_get_string(item->jsobj));
  257. break;
  258. case json_type_null:
  259. break;
  260. }
  261. }
  262. }
  263. }
  264. static void
  265. export_type(struct list_head *matches, const char *prefix, int limit)
  266. {
  267. bool first = true;
  268. struct match_item *item;
  269. const char *types[] = {
  270. "null",
  271. "boolean",
  272. "double",
  273. "int",
  274. "object",
  275. "array",
  276. "string"
  277. };
  278. if (list_empty(matches))
  279. return;
  280. if (prefix)
  281. printf("export %s=", prefix);
  282. list_for_each_entry(item, matches, list)
  283. {
  284. if (!first)
  285. printf("\\ ");
  286. if (limit-- <= 0)
  287. break;
  288. printf("%s", types[json_object_get_type(item->jsobj)]);
  289. first = false;
  290. }
  291. if (prefix)
  292. printf("; ");
  293. else
  294. printf("\n");
  295. }
  296. static void
  297. match_cb(struct json_object *res, void *priv)
  298. {
  299. struct list_head *h = priv;
  300. struct match_item *i = calloc(1, sizeof(*i));
  301. if (i)
  302. {
  303. i->jsobj = res;
  304. list_add_tail(&i->list, h);
  305. }
  306. }
  307. static void
  308. print_error(struct jp_state *state, char *expr)
  309. {
  310. int i;
  311. bool first = true;
  312. fprintf(stderr, "Syntax error: ");
  313. switch (state->error_code)
  314. {
  315. case -4:
  316. fprintf(stderr, "Unexpected character\n");
  317. break;
  318. case -3:
  319. fprintf(stderr, "String or label literal too long\n");
  320. break;
  321. case -2:
  322. fprintf(stderr, "Invalid escape sequence\n");
  323. break;
  324. case -1:
  325. fprintf(stderr, "Unterminated string\n");
  326. break;
  327. default:
  328. for (i = 0; i < sizeof(state->error_code) * 8; i++)
  329. {
  330. if (state->error_code & (1 << i))
  331. {
  332. fprintf(stderr,
  333. first ? "Expecting %s" : " or %s", tokennames[i]);
  334. first = false;
  335. }
  336. }
  337. fprintf(stderr, "\n");
  338. break;
  339. }
  340. fprintf(stderr, "In expression %s\n", expr);
  341. fprintf(stderr, "Near here ----");
  342. for (i = 0; i < state->error_pos; i++)
  343. fprintf(stderr, "-");
  344. fprintf(stderr, "^\n");
  345. }
  346. static bool
  347. filter_json(int opt, struct json_object *jsobj, char *expr, const char *sep,
  348. int limit)
  349. {
  350. struct jp_state *state;
  351. const char *prefix = NULL;
  352. struct list_head matches;
  353. struct match_item *item, *tmp;
  354. struct json_object *res = NULL;
  355. state = jp_parse(expr);
  356. if (!state)
  357. {
  358. fprintf(stderr, "Out of memory\n");
  359. goto out;
  360. }
  361. else if (state->error_code)
  362. {
  363. print_error(state, expr);
  364. goto out;
  365. }
  366. INIT_LIST_HEAD(&matches);
  367. res = jp_match(state->path, jsobj, match_cb, &matches);
  368. prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
  369. switch (opt)
  370. {
  371. case 't':
  372. export_type(&matches, prefix, limit);
  373. break;
  374. default:
  375. export_value(&matches, prefix, sep, limit);
  376. break;
  377. }
  378. list_for_each_entry_safe(item, tmp, &matches, list)
  379. free(item);
  380. out:
  381. if (state)
  382. jp_free(state);
  383. return !!res;
  384. }
  385. int main(int argc, char **argv)
  386. {
  387. bool array_mode = false;
  388. int opt, rv = 0, limit = 0x7FFFFFFF;
  389. FILE *input = stdin;
  390. struct json_object *jsobj = NULL;
  391. const char *jserr = NULL, *source = NULL, *separator = " ";
  392. if (argc == 1)
  393. {
  394. print_usage(argv[0]);
  395. goto out;
  396. }
  397. while ((opt = getopt(argc, argv, "ahi:s:e:t:F:l:q")) != -1)
  398. {
  399. switch (opt)
  400. {
  401. case 'a':
  402. array_mode = true;
  403. break;
  404. case 'h':
  405. print_usage(argv[0]);
  406. goto out;
  407. case 'i':
  408. input = fopen(optarg, "r");
  409. if (!input)
  410. {
  411. fprintf(stderr, "Failed to open %s: %s\n",
  412. optarg, strerror(errno));
  413. rv = 125;
  414. goto out;
  415. }
  416. break;
  417. case 's':
  418. source = optarg;
  419. break;
  420. case 'F':
  421. if (optarg && *optarg)
  422. separator = optarg;
  423. break;
  424. case 'l':
  425. limit = atoi(optarg);
  426. break;
  427. case 't':
  428. case 'e':
  429. if (!jsobj)
  430. {
  431. jsobj = parse_json(input, source, &jserr, array_mode);
  432. if (!jsobj)
  433. {
  434. fprintf(stderr, "Failed to parse json data: %s\n",
  435. jserr);
  436. rv = 126;
  437. goto out;
  438. }
  439. }
  440. if (!filter_json(opt, jsobj, optarg, separator, limit))
  441. rv = 1;
  442. break;
  443. case 'q':
  444. fclose(stderr);
  445. break;
  446. }
  447. }
  448. out:
  449. if (jsobj)
  450. json_object_put(jsobj);
  451. if (input && input != stdin)
  452. fclose(input);
  453. return rv;
  454. }