cli: additional flags and cleanup
[project/jsonpath.git] / main.c
1 /*
2  * Copyright (C) 2013 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 "lexer.h"
29 #include "parser.h"
30 #include "matcher.h"
31
32 static struct json_object *
33 parse_json(FILE *fd, const char **error)
34 {
35         int len;
36         char buf[256];
37         struct json_object *obj = NULL;
38         struct json_tokener *tok = json_tokener_new();
39         enum json_tokener_error err = json_tokener_continue;
40
41         if (!tok)
42                 return NULL;
43
44         while ((len = fread(buf, 1, sizeof(buf), fd)) > 0)
45         {
46                 obj = json_tokener_parse_ex(tok, buf, len);
47                 err = json_tokener_get_error(tok);
48
49                 if (!err || err != json_tokener_continue)
50                         break;
51         }
52
53         json_tokener_free(tok);
54
55         if (err)
56         {
57                 if (err == json_tokener_continue)
58                         err = json_tokener_error_parse_eof;
59
60                 *error = json_tokener_error_desc(err);
61                 return NULL;
62         }
63
64         return obj;
65 }
66
67 static void
68 print_string(const char *s)
69 {
70         const char *p;
71
72         printf("'");
73
74         for (p = s; *p; p++)
75         {
76                 if (*p == '\'')
77                         printf("'\"'\"'");
78                 else
79                         printf("%c", *p);
80         }
81
82         printf("'");
83 }
84
85 static void
86 export_value(struct json_object *jsobj, const char *prefix)
87 {
88         int n, len;
89         bool first = true;
90
91         if (prefix)
92         {
93                 switch (json_object_get_type(jsobj))
94                 {
95                 case json_type_object:
96                         printf("export %s=", prefix);
97                         json_object_object_foreach(jsobj, key, val)
98                         {
99                                 if (!val)
100                                         continue;
101
102                                 if (!first)
103                                         printf("\\ ");
104
105                                 print_string(key);
106                                 first = false;
107                         }
108                         printf("; ");
109                         break;
110
111                 case json_type_array:
112                         printf("export %s=", prefix);
113                         for (n = 0, len = json_object_array_length(jsobj); n < len; n++)
114                         {
115                                 if (!first)
116                                         printf("\\ ");
117
118                                 printf("%d", n);
119                                 first = false;
120                         }
121                         printf("; ");
122                         break;
123
124                 case json_type_boolean:
125                         printf("export %s=%d; ", prefix, json_object_get_boolean(jsobj));
126                         break;
127
128                 case json_type_int:
129                         printf("export %s=%d; ", prefix, json_object_get_int(jsobj));
130                         break;
131
132                 case json_type_double:
133                         printf("export %s=%f; ", prefix, json_object_get_double(jsobj));
134                         break;
135
136                 case json_type_string:
137                         printf("export %s=", prefix);
138                         print_string(json_object_get_string(jsobj));
139                         printf("; ");
140                         break;
141
142                 case json_type_null:
143                         break;
144                 }
145         }
146         else
147         {
148                 printf("%s\n", json_object_to_json_string(jsobj));
149         }
150 }
151
152 static void
153 export_type(struct json_object *jsobj, const char *prefix)
154 {
155         const char *types[] = {
156                 "null",
157                 "boolean",
158                 "double",
159                 "int",
160                 "object",
161                 "array",
162                 "string"
163         };
164
165         if (prefix)
166                 printf("export %s=%s; ", prefix, types[json_object_get_type(jsobj)]);
167         else
168                 printf("%s\n", types[json_object_get_type(jsobj)]);
169 }
170
171 static bool
172 filter_json(int opt, struct json_object *jsobj, char *expr)
173 {
174         struct jp_state *state;
175         struct json_object *res = NULL;
176         const char *prefix = NULL;
177
178         state = jp_parse(expr);
179
180         if (!state || state->error)
181         {
182                 fprintf(stderr, "In expression '%s': %s\n",
183                         expr, state ? state->error : "Out of memory");
184
185                 goto out;
186         }
187
188         res = jp_match(state->path, jsobj);
189
190         if (res)
191         {
192                 prefix = (state->path->type == T_LABEL) ? state->path->str : NULL;
193
194                 switch (opt)
195                 {
196                 case 't':
197                         export_type(res, prefix);
198                         break;
199
200                 default:
201                         export_value(res, prefix);
202                         break;
203                 }
204         }
205
206 out:
207         if (state)
208                 jp_free(state);
209
210         return !!res;
211 }
212
213 int main(int argc, char **argv)
214 {
215         int opt, rv = 0;
216         FILE *input = stdin;
217         struct json_object *jsobj = NULL;
218         const char *jserr = NULL;
219
220         while ((opt = getopt(argc, argv, "i:e:t:q")) != -1)
221         {
222                 switch (opt)
223                 {
224                 case 'i':
225                         input = fopen(optarg, "r");
226
227                         if (!input)
228                         {
229                                 fprintf(stderr, "Failed to open %s: %s\n",
230                                                 optarg, strerror(errno));
231
232                                 rv = 125;
233                                 goto out;
234                         }
235
236                         break;
237
238                 case 't':
239                 case 'e':
240                         if (!jsobj)
241                         {
242                                 jsobj = parse_json(input, &jserr);
243
244                                 if (!jsobj)
245                                 {
246                                         fprintf(stderr, "Failed to parse json data: %s\n",
247                                                 jserr);
248
249                                         rv = 126;
250                                         goto out;
251                                 }
252                         }
253
254                         if (!filter_json(opt, jsobj, optarg))
255                                 rv = 1;
256
257                         break;
258
259                 case 'q':
260                         fclose(stderr);
261                         break;
262                 }
263         }
264
265 out:
266         if (jsobj)
267                 json_object_put(jsobj);
268
269         if (input != stdin)
270                 fclose(input);
271
272         return rv;
273 }