Improve error reporting
[project/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 export_value(struct list_head *matches, const char *prefix)
103 {
104 int n, len;
105 bool first = true;
106 struct match_item *item;
107
108 if (list_empty(matches))
109 return;
110
111 if (prefix)
112 {
113 printf("export %s=", prefix);
114
115 list_for_each_entry(item, matches, list)
116 {
117 switch (json_object_get_type(item->jsobj))
118 {
119 case json_type_object:
120 ; /* a label can only be part of a statement */
121 json_object_object_foreach(item->jsobj, key, val)
122 {
123 if (!val)
124 continue;
125
126 if (!first)
127 printf("\\ ");
128
129 print_string(key);
130 first = false;
131 }
132 break;
133
134 case json_type_array:
135 for (n = 0, len = json_object_array_length(item->jsobj);
136 n < len; n++)
137 {
138 if (!first)
139 printf("\\ ");
140
141 printf("%d", n);
142 first = false;
143 }
144 break;
145
146 case json_type_boolean:
147 if (!first)
148 printf("\\ ");
149 printf("%d", json_object_get_boolean(item->jsobj));
150 break;
151
152 case json_type_int:
153 if (!first)
154 printf("\\ ");
155 printf("%d", json_object_get_int(item->jsobj));
156 break;
157
158 case json_type_double:
159 if (!first)
160 printf("\\ ");
161 printf("%f", json_object_get_double(item->jsobj));
162 break;
163
164 case json_type_string:
165 if (!first)
166 printf("\\ ");
167 print_string(json_object_get_string(item->jsobj));
168 break;
169
170 case json_type_null:
171 break;
172 }
173
174 first = false;
175 }
176
177 printf("; ");
178 }
179 else
180 {
181 list_for_each_entry(item, matches, list)
182 {
183 switch (json_object_get_type(item->jsobj))
184 {
185 case json_type_object:
186 case json_type_array:
187 case json_type_boolean:
188 case json_type_int:
189 case json_type_double:
190 printf("%s\n", json_object_to_json_string(item->jsobj));
191 break;
192
193 case json_type_string:
194 printf("%s\n", json_object_get_string(item->jsobj));
195 break;
196
197 case json_type_null:
198 break;
199 }
200 }
201 }
202 }
203
204 static void
205 export_type(struct list_head *matches, const char *prefix)
206 {
207 bool first = true;
208 struct match_item *item;
209 const char *types[] = {
210 "null",
211 "boolean",
212 "double",
213 "int",
214 "object",
215 "array",
216 "string"
217 };
218
219 if (list_empty(matches))
220 return;
221
222 if (prefix)
223 printf("export %s=", prefix);
224
225 list_for_each_entry(item, matches, list)
226 {
227 if (!first)
228 printf("\\ ");
229
230 printf("%s", types[json_object_get_type(item->jsobj)]);
231 first = false;
232 }
233
234 if (prefix)
235 printf("; ");
236 else
237 printf("\n");
238 }
239
240 static void
241 match_cb(struct json_object *res, void *priv)
242 {
243 struct list_head *h = priv;
244 struct match_item *i = calloc(1, sizeof(*i));
245
246 if (i)
247 {
248 i->jsobj = res;
249 list_add_tail(&i->list, h);
250 }
251 }
252
253 static void
254 print_error(struct jp_state *state, char *expr)
255 {
256 int i;
257 bool first = true;
258
259 fprintf(stderr, "Syntax error: ");
260
261 switch (state->error_code)
262 {
263 case -4:
264 fprintf(stderr, "Unexpected character\n");
265 break;
266
267 case -3:
268 fprintf(stderr, "String or label literal too long\n");
269 break;
270
271 case -2:
272 fprintf(stderr, "Invalid escape sequence\n");
273 break;
274
275 case -1:
276 fprintf(stderr, "Unterminated string\n");
277 break;
278
279 default:
280 for (i = 0; i < sizeof(state->error_code) * 8; i++)
281 {
282 if (state->error_code & (1 << i))
283 {
284 fprintf(stderr,
285 first ? "Expecting %s" : " or %s", tokennames[i]);
286
287 first = false;
288 }
289 }
290
291 fprintf(stderr, "\n");
292 break;
293 }
294
295 fprintf(stderr, "In expression %s\n", expr);
296 fprintf(stderr, "Near here ----");
297
298 for (i = 0; i < state->error_pos; i++)
299 fprintf(stderr, "-");
300
301 fprintf(stderr, "^\n");
302 }
303
304 static bool
305 filter_json(int opt, struct json_object *jsobj, char *expr)
306 {
307 struct jp_state *state;
308 const char *prefix = NULL;
309 struct list_head matches;
310 struct match_item *item, *tmp;
311 struct json_object *res = NULL;
312
313 state = jp_parse(expr);
314
315 if (!state)
316 {
317 fprintf(stderr, "Out of memory\n");
318 goto out;
319 }
320 else if (state->error_code)
321 {
322 print_error(state, expr);
323 goto out;
324 }
325
326 INIT_LIST_HEAD(&matches);
327
328 res = jp_match(state->path, jsobj, match_cb, &matches);
329 prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
330
331 switch (opt)
332 {
333 case 't':
334 export_type(&matches, prefix);
335 break;
336
337 default:
338 export_value(&matches, prefix);
339 break;
340 }
341
342 list_for_each_entry_safe(item, tmp, &matches, list)
343 free(item);
344
345 out:
346 if (state)
347 jp_free(state);
348
349 return !!res;
350 }
351
352 int main(int argc, char **argv)
353 {
354 int opt, rv = 0;
355 FILE *input = stdin;
356 struct json_object *jsobj = NULL;
357 const char *jserr = NULL, *source = NULL;
358
359 while ((opt = getopt(argc, argv, "i:s:e:t:q")) != -1)
360 {
361 switch (opt)
362 {
363 case 'i':
364 input = fopen(optarg, "r");
365
366 if (!input)
367 {
368 fprintf(stderr, "Failed to open %s: %s\n",
369 optarg, strerror(errno));
370
371 rv = 125;
372 goto out;
373 }
374
375 break;
376
377 case 's':
378 source = optarg;
379 break;
380
381 case 't':
382 case 'e':
383 if (!jsobj)
384 {
385 jsobj = parse_json(input, source, &jserr);
386
387 if (!jsobj)
388 {
389 fprintf(stderr, "Failed to parse json data: %s\n",
390 jserr);
391
392 rv = 126;
393 goto out;
394 }
395 }
396
397 if (!filter_json(opt, jsobj, optarg))
398 rv = 1;
399
400 break;
401
402 case 'q':
403 fclose(stderr);
404 break;
405 }
406 }
407
408 out:
409 if (jsobj)
410 json_object_put(jsobj);
411
412 if (input != stdin)
413 fclose(input);
414
415 return rv;
416 }