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