cli: supress printing "null" in non-export mode, output strings unescaped
[project/jsonpath.git] / matcher.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 "matcher.h"
18
19 static struct json_object *
20 jp_match_next(struct jp_opcode *ptr,
21               struct json_object *root, struct json_object *cur,
22               jp_match_cb_t cb, void *priv);
23
24 static bool
25 jp_json_to_op(struct json_object *obj, struct jp_opcode *op)
26 {
27         switch (json_object_get_type(obj))
28         {
29         case json_type_boolean:
30                 op->type = T_BOOL;
31                 op->num = json_object_get_boolean(obj);
32                 return true;
33
34         case json_type_int:
35                 op->type = T_NUMBER;
36                 op->num = json_object_get_int(obj);
37                 return true;
38
39         case json_type_string:
40                 op->type = T_STRING;
41                 op->str = (char *)json_object_get_string(obj);
42                 return true;
43
44         default:
45                 return false;
46         }
47 }
48
49 static bool
50 jp_resolve(struct json_object *root, struct json_object *cur,
51            struct jp_opcode *op, struct jp_opcode *res)
52 {
53         struct json_object *val;
54
55         switch (op->type)
56         {
57         case T_THIS:
58                 val = jp_match(op, cur, NULL, NULL);
59
60                 if (val)
61                         return jp_json_to_op(val, res);
62
63                 return false;
64
65         case T_ROOT:
66                 val = jp_match(op, root, NULL, NULL);
67
68                 if (val)
69                         return jp_json_to_op(val, res);
70
71                 return false;
72
73         default:
74                 *res = *op;
75                 return true;
76         }
77 }
78
79 static bool
80 jp_cmp(struct jp_opcode *op, struct json_object *root, struct json_object *cur)
81 {
82         int delta;
83         struct jp_opcode left, right;
84
85         if (!jp_resolve(root, cur, op->down, &left) ||
86         !jp_resolve(root, cur, op->down->sibling, &right))
87                 return false;
88
89         if (left.type != right.type)
90                 return false;
91
92         switch (left.type)
93         {
94         case T_BOOL:
95         case T_NUMBER:
96                 delta = left.num - right.num;
97                 break;
98
99         case T_STRING:
100                 delta = strcmp(left.str, right.str);
101                 break;
102
103         default:
104                 return false;
105         }
106
107         switch (op->type)
108         {
109         case T_EQ:
110                 return (delta == 0);
111
112         case T_LT:
113                 return (delta < 0);
114
115         case T_LE:
116                 return (delta <= 0);
117
118         case T_GT:
119                 return (delta > 0);
120
121         case T_GE:
122                 return (delta >= 0);
123
124         case T_NE:
125                 return (delta != 0);
126
127         default:
128                 return false;
129         }
130 }
131
132 static bool
133 jp_expr(struct jp_opcode *op, struct json_object *root, struct json_object *cur,
134         int idx, const char *key, jp_match_cb_t cb, void *priv)
135 {
136         struct jp_opcode *sop;
137
138         switch (op->type)
139         {
140         case T_WILDCARD:
141                 return true;
142
143         case T_EQ:
144         case T_NE:
145         case T_LT:
146         case T_LE:
147         case T_GT:
148         case T_GE:
149                 return jp_cmp(op, root, cur);
150
151         case T_ROOT:
152                 return !!jp_match(op, root, NULL, NULL);
153
154         case T_THIS:
155                 return !!jp_match(op, cur, NULL, NULL);
156
157         case T_NOT:
158                 return !jp_expr(op->down, root, cur, idx, key, cb, priv);
159
160         case T_AND:
161                 for (sop = op->down; sop; sop = sop->sibling)
162                         if (!jp_expr(sop, root, cur, idx, key, cb, priv))
163                                 return false;
164                 return true;
165
166         case T_OR:
167         case T_UNION:
168                 for (sop = op->down; sop; sop = sop->sibling)
169                         if (jp_expr(sop, root, cur, idx, key, cb, priv))
170                                 return true;
171                 return false;
172
173         case T_STRING:
174                 return (key && !strcmp(op->str, key));
175
176         case T_NUMBER:
177                 return (idx == op->num);
178
179         default:
180                 return false;
181         }
182 }
183
184 static struct json_object *
185 jp_match_expr(struct jp_opcode *ptr,
186               struct json_object *root, struct json_object *cur,
187               jp_match_cb_t cb, void *priv)
188 {
189         int idx, len;
190         struct json_object *tmp, *res = NULL;
191
192         switch (json_object_get_type(cur))
193         {
194         case json_type_object:
195                 ; /* a label can only be part of a statement and a declaration is not a statement */
196                 json_object_object_foreach(cur, key, val)
197                 {
198                         if (jp_expr(ptr, root, val, -1, key, cb, priv))
199                         {
200                                 tmp = jp_match_next(ptr->sibling, root, val, cb, priv);
201
202                                 if (tmp && !res)
203                                         res = tmp;
204                         }
205                 }
206
207                 break;
208
209         case json_type_array:
210                 len = json_object_array_length(cur);
211
212                 for (idx = 0; idx < len; idx++)
213                 {
214                         tmp = json_object_array_get_idx(cur, idx);
215
216                         if (jp_expr(ptr, root, tmp, idx, NULL, cb, priv))
217                         {
218                                 tmp = jp_match_next(ptr->sibling, root, tmp, cb, priv);
219
220                                 if (tmp && !res)
221                                         res = tmp;
222                         }
223                 }
224
225                 break;
226
227         default:
228                 break;
229         }
230
231         return res;
232 }
233
234 static struct json_object *
235 jp_match_next(struct jp_opcode *ptr,
236               struct json_object *root, struct json_object *cur,
237               jp_match_cb_t cb, void *priv)
238 {
239         struct json_object *next;
240
241         if (!ptr)
242         {
243                 if (cb)
244                         cb(cur, priv);
245
246                 return cur;
247         }
248
249         switch (ptr->type)
250         {
251         case T_STRING:
252         case T_LABEL:
253                 if (json_object_object_get_ex(cur, ptr->str, &next))
254                         return jp_match_next(ptr->sibling, root, next, cb, priv);
255
256                 break;
257
258         case T_NUMBER:
259                 next = json_object_array_get_idx(cur, ptr->num);
260
261                 if (next)
262                         return jp_match_next(ptr->sibling, root, next, cb, priv);
263
264                 break;
265
266         default:
267                 return jp_match_expr(ptr, root, cur, cb, priv);
268         }
269
270         return NULL;
271 }
272
273 struct json_object *
274 jp_match(struct jp_opcode *path, json_object *jsobj,
275          jp_match_cb_t cb, void *priv)
276 {
277         if (path->type == T_LABEL)
278                 path = path->down;
279
280         return jp_match_next(path->down, jsobj, jsobj, cb, priv);
281 }