]>
Commit | Line | Data |
---|---|---|
846b4cbe MY |
1 | /* |
2 | * lsfd-filter.c - filtering engine for lsfd | |
3 | * | |
ef70cdd9 MY |
4 | * Copyright (C) 2021 Red Hat, Inc. |
5 | * Copyright (C) 2021 Masatake YAMATO <yamato@redhat.com> | |
846b4cbe | 6 | * |
ef70cdd9 MY |
7 | * This file may be redistributed under the terms of the |
8 | * GNU Lesser General Public License. | |
846b4cbe MY |
9 | */ |
10 | ||
11 | #include "lsfd-filter.h" | |
12 | ||
13 | #include "nls.h" | |
14 | #include "strutils.h" | |
15 | #include "xalloc.h" | |
16 | ||
17 | #include <string.h> | |
18 | #include <ctype.h> | |
abaf378c | 19 | #include <regex.h> /* regcomp(), regexec() */ |
846b4cbe MY |
20 | |
21 | /* | |
22 | * Definitions | |
23 | */ | |
546b26b1 | 24 | #define COL_HEADER_EXTRA_CHARS ":-_%." /* ??? */ |
846b4cbe MY |
25 | #define GOT_ERROR(PARSERorFILTER)(*((PARSERorFILTER)->errmsg)) |
26 | ||
27 | /* | |
28 | * Types | |
29 | */ | |
30 | ||
31 | enum token_type { | |
546b26b1 | 32 | TOKEN_NAME, /* [A-Za-z_][-_:%.A-Za-z0-9]* */ |
846b4cbe | 33 | TOKEN_STR, /* "...", '...' */ |
326e4be4 | 34 | TOKEN_DEC, /* [1-9][0-9]+, NOTE: negative value has not handled. */ |
1fa18564 | 35 | TOKEN_FDEC, /* [1-9][0-9]+\.[0-9]+ */ |
846b4cbe MY |
36 | TOKEN_HEX, /* 0x[0-9a-f]+ not implemented */ |
37 | TOKEN_OCT, /* 0[1-7]+ not implemented */ | |
38 | TOKEN_TRUE, /* true */ | |
39 | TOKEN_FALSE, /* false */ | |
40 | TOKEN_OPEN, /* ( */ | |
41 | TOKEN_CLOSE, /* ) */ | |
42 | TOKEN_OP1, /* !, not */ | |
abaf378c | 43 | TOKEN_OP2, /* TODO: =*, !* (glob match with fnmatch() */ |
846b4cbe MY |
44 | TOKEN_EOF, |
45 | }; | |
46 | ||
47 | enum op1_type { | |
48 | OP1_NOT, | |
49 | }; | |
50 | ||
51 | enum op2_type { | |
52 | OP2_EQ, | |
53 | OP2_NE, | |
54 | OP2_AND, | |
55 | OP2_OR, | |
56 | OP2_LT, | |
57 | OP2_LE, | |
58 | OP2_GT, | |
59 | OP2_GE, | |
abaf378c | 60 | OP2_RE_MATCH, |
f439236f | 61 | OP2_RE_UNMATCH, |
846b4cbe MY |
62 | }; |
63 | ||
64 | struct token { | |
65 | enum token_type type; | |
66 | union { | |
67 | char *str; | |
68 | unsigned long long num; | |
1fa18564 | 69 | long double fnum; |
846b4cbe MY |
70 | enum op1_type op1; |
71 | enum op2_type op2; | |
72 | } val; | |
73 | }; | |
74 | ||
75 | struct token_class { | |
9b6ad16f | 76 | const char * const name; |
846b4cbe MY |
77 | void (*free)(struct token *); |
78 | void (*dump)(struct token *, FILE *); | |
79 | }; | |
80 | ||
81 | struct parameter { | |
82 | struct libscols_column *cl; | |
83 | bool has_value; | |
564d954a | 84 | bool floating_point_num; |
846b4cbe MY |
85 | union { |
86 | const char *str; | |
87 | unsigned long long num; | |
564d954a | 88 | long double fnum; |
846b4cbe MY |
89 | bool boolean; |
90 | } val; | |
91 | }; | |
92 | ||
23a4b2b2 MY |
93 | struct parser { |
94 | const char *expr; | |
95 | const char *cursor; | |
96 | int paren_level; | |
97 | struct libscols_table *tb; | |
98 | int (*column_name_to_id)(const char *, void *); | |
99 | struct libscols_column *(*add_column_by_id)(struct libscols_table *, int, void*); | |
100 | void *data; | |
101 | struct parameter *parameters; | |
91a484fe | 102 | char errmsg[128]; |
23a4b2b2 MY |
103 | }; |
104 | ||
846b4cbe MY |
105 | enum node_type { |
106 | NODE_STR, | |
107 | NODE_NUM, | |
1fa18564 | 108 | NODE_FNUM, |
846b4cbe | 109 | NODE_BOOL, |
abaf378c | 110 | NODE_RE, |
846b4cbe MY |
111 | NODE_OP1, |
112 | NODE_OP2, | |
113 | }; | |
114 | ||
115 | struct node { | |
116 | enum node_type type; | |
117 | }; | |
118 | ||
119 | struct op1_class { | |
9b6ad16f | 120 | const char * const name; |
846b4cbe MY |
121 | /* Return true if acceptable. */ |
122 | bool (*is_acceptable)(struct node *, struct parameter *, struct libscols_line *); | |
123 | /* Return true if o.k. */ | |
9b6ad16f | 124 | bool (*check_type)(struct parser *, const struct op1_class *, struct node *); |
846b4cbe MY |
125 | }; |
126 | ||
127 | struct op2_class { | |
9b6ad16f | 128 | const char * const name; |
846b4cbe MY |
129 | /* Return true if acceptable. */ |
130 | bool (*is_acceptable)(struct node *, struct node *, struct parameter *, struct libscols_line *); | |
131 | /* Return true if o.k. */ | |
9b6ad16f | 132 | bool (*check_type)(struct parser *, const struct op2_class *, struct node *, struct node *); |
846b4cbe MY |
133 | }; |
134 | ||
135 | #define VAL(NODE,FIELD) (((struct node_val *)(NODE))->val.FIELD) | |
136 | #define PINDEX(NODE) (((struct node_val *)(NODE))->pindex) | |
137 | struct node_val { | |
138 | struct node base; | |
139 | int pindex; | |
140 | union { | |
141 | char *str; | |
142 | unsigned long long num; | |
1fa18564 | 143 | long double fnum; |
846b4cbe | 144 | bool boolean; |
abaf378c | 145 | regex_t re; |
846b4cbe MY |
146 | } val; |
147 | }; | |
148 | ||
149 | struct node_op1 { | |
150 | struct node base; | |
9b6ad16f | 151 | const struct op1_class *opclass; |
846b4cbe MY |
152 | struct node *arg; |
153 | }; | |
154 | ||
155 | struct node_op2 { | |
156 | struct node base; | |
9b6ad16f | 157 | const struct op2_class *opclass; |
846b4cbe MY |
158 | struct node *args[2]; |
159 | }; | |
160 | ||
161 | struct node_class { | |
9b6ad16f | 162 | const char * const name; |
846b4cbe MY |
163 | void (*free)(struct node *); |
164 | void (*dump)(struct node *, struct parameter*, int, FILE *); | |
165 | }; | |
166 | ||
167 | struct lsfd_filter { | |
168 | struct libscols_table *table; | |
169 | struct node *node; | |
170 | struct parameter *parameters; | |
171 | int nparams; | |
91a484fe | 172 | char errmsg[ sizeof_member(struct parser, errmsg) ]; |
846b4cbe MY |
173 | }; |
174 | ||
175 | /* | |
176 | * Prototypes | |
177 | */ | |
178 | static struct node *node_val_new(enum node_type, int pindex); | |
179 | static void node_free (struct node *); | |
180 | static bool node_apply(struct node *, struct parameter *, struct libscols_line *); | |
181 | static void node_dump (struct node *, struct parameter *, int, FILE *); | |
182 | ||
183 | static struct token *token_new (void); | |
184 | static void token_free(struct token *); | |
ae6ccf51 | 185 | #ifdef DEBUG |
846b4cbe | 186 | static void token_dump(struct token *, FILE *); |
ae6ccf51 | 187 | #endif /* DEBUG */ |
846b4cbe MY |
188 | |
189 | static void token_free_str(struct token *); | |
190 | ||
191 | static void token_dump_str(struct token *, FILE *); | |
192 | static void token_dump_num(struct token *, FILE *); | |
1fa18564 | 193 | static void token_dump_fnum(struct token *, FILE *); |
846b4cbe MY |
194 | static void token_dump_op1(struct token *, FILE *); |
195 | static void token_dump_op2(struct token *, FILE *); | |
196 | ||
197 | static bool op1_not(struct node *, struct parameter*, struct libscols_line *); | |
9b6ad16f | 198 | static bool op1_check_type_bool_or_op(struct parser *, const struct op1_class *, struct node *); |
846b4cbe MY |
199 | |
200 | static bool op2_eq (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
201 | static bool op2_ne (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
202 | static bool op2_and(struct node *, struct node *, struct parameter*, struct libscols_line *); | |
203 | static bool op2_or (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
204 | static bool op2_lt (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
205 | static bool op2_le (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
206 | static bool op2_gt (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
207 | static bool op2_ge (struct node *, struct node *, struct parameter*, struct libscols_line *); | |
abaf378c | 208 | static bool op2_re_match (struct node *, struct node *, struct parameter*, struct libscols_line *); |
f439236f | 209 | static bool op2_re_unmatch (struct node *, struct node *, struct parameter*, struct libscols_line *); |
abaf378c | 210 | |
9b6ad16f TW |
211 | static bool op2_check_type_eq_or_bool_or_op(struct parser *, const struct op2_class *, struct node *, struct node *); |
212 | static bool op2_check_type_boolean_or_op (struct parser *, const struct op2_class *, struct node *, struct node *); | |
213 | static bool op2_check_type_num (struct parser *, const struct op2_class *, struct node *, struct node *); | |
214 | static bool op2_check_type_re (struct parser *, const struct op2_class *, struct node *, struct node *); | |
846b4cbe MY |
215 | |
216 | static void node_str_free(struct node *); | |
abaf378c | 217 | static void node_re_free (struct node *); |
846b4cbe MY |
218 | static void node_op1_free(struct node *); |
219 | static void node_op2_free(struct node *); | |
220 | ||
221 | static void node_str_dump (struct node *, struct parameter*, int, FILE *); | |
222 | static void node_num_dump (struct node *, struct parameter*, int, FILE *); | |
1fa18564 | 223 | static void node_fnum_dump (struct node *, struct parameter*, int, FILE *); |
846b4cbe | 224 | static void node_bool_dump(struct node *, struct parameter*, int, FILE *); |
abaf378c | 225 | static void node_re_dump (struct node *, struct parameter*, int, FILE *); |
846b4cbe MY |
226 | static void node_op1_dump (struct node *, struct parameter*, int, FILE *); |
227 | static void node_op2_dump (struct node *, struct parameter*, int, FILE *); | |
228 | ||
229 | static struct node *dparser_compile(struct parser *); | |
230 | ||
231 | /* | |
232 | * Data | |
233 | */ | |
234 | #define TOKEN_CLASS(TOKEN) (&token_classes[(TOKEN)->type]) | |
9b6ad16f | 235 | static const struct token_class token_classes [] = { |
846b4cbe MY |
236 | [TOKEN_NAME] = { |
237 | .name = "NAME", | |
238 | .free = token_free_str, | |
239 | .dump = token_dump_str, | |
240 | }, | |
241 | [TOKEN_STR] = { | |
242 | .name = "STR", | |
243 | .free = token_free_str, | |
244 | .dump = token_dump_str, | |
245 | }, | |
246 | [TOKEN_DEC] = { | |
247 | .name = "DEC", | |
248 | .dump = token_dump_num, | |
249 | }, | |
1fa18564 MY |
250 | [TOKEN_FDEC] = { |
251 | .name = "FDEC", | |
252 | .dump = token_dump_fnum, | |
253 | }, | |
846b4cbe MY |
254 | [TOKEN_TRUE] = { |
255 | .name = "true", | |
256 | }, | |
257 | [TOKEN_FALSE] = { | |
258 | .name = "false", | |
259 | }, | |
260 | [TOKEN_OPEN] = { | |
261 | .name = "OPEN", | |
262 | }, | |
263 | [TOKEN_CLOSE] = { | |
264 | .name = "CLOSE", | |
265 | }, | |
266 | [TOKEN_OP1] = { | |
267 | .name = "OP1", | |
268 | .dump = token_dump_op1, | |
269 | }, | |
270 | [TOKEN_OP2] = { | |
271 | .name = "OP2", | |
272 | .dump = token_dump_op2, | |
273 | }, | |
274 | [TOKEN_EOF] = { | |
275 | .name = "TOKEN_EOF", | |
276 | }, | |
277 | }; | |
278 | ||
279 | #define TOKEN_OP1_CLASS(TOKEN) (&(op1_classes[(TOKEN)->val.op1])) | |
9b6ad16f | 280 | static const struct op1_class op1_classes [] = { |
846b4cbe MY |
281 | [OP1_NOT] = { |
282 | .name = "!", | |
283 | .is_acceptable = op1_not, | |
284 | .check_type = op1_check_type_bool_or_op, | |
285 | }, | |
286 | }; | |
287 | ||
288 | #define TOKEN_OP2_CLASS(TOKEN) (&(op2_classes[(TOKEN)->val.op2])) | |
9b6ad16f | 289 | static const struct op2_class op2_classes [] = { |
846b4cbe MY |
290 | [OP2_EQ] = { |
291 | .name = "==", | |
292 | .is_acceptable = op2_eq, | |
293 | .check_type = op2_check_type_eq_or_bool_or_op | |
294 | }, | |
295 | [OP2_NE] = { | |
296 | .name = "!=", | |
297 | .is_acceptable = op2_ne, | |
298 | .check_type = op2_check_type_eq_or_bool_or_op, | |
299 | }, | |
300 | [OP2_AND] = { | |
301 | .name = "&&", | |
302 | .is_acceptable = op2_and, | |
303 | .check_type = op2_check_type_boolean_or_op, | |
304 | }, | |
305 | [OP2_OR] = { | |
306 | .name = "||", | |
307 | .is_acceptable = op2_or, | |
308 | .check_type = op2_check_type_boolean_or_op, | |
309 | }, | |
310 | [OP2_LT] = { | |
311 | .name = "<", | |
312 | .is_acceptable = op2_lt, | |
313 | .check_type = op2_check_type_num, | |
314 | }, | |
315 | [OP2_LE] = { | |
316 | .name = "<=", | |
317 | .is_acceptable = op2_le, | |
318 | .check_type = op2_check_type_num, | |
319 | }, | |
320 | [OP2_GT] = { | |
321 | .name = ">", | |
322 | .is_acceptable = op2_gt, | |
323 | .check_type = op2_check_type_num, | |
324 | }, | |
325 | [OP2_GE] = { | |
326 | .name = ">=", | |
327 | .is_acceptable = op2_ge, | |
328 | .check_type = op2_check_type_num, | |
329 | }, | |
abaf378c MY |
330 | [OP2_RE_MATCH] = { |
331 | .name = "=~", | |
332 | .is_acceptable = op2_re_match, | |
333 | .check_type = op2_check_type_re, | |
334 | }, | |
f439236f MY |
335 | [OP2_RE_UNMATCH] = { |
336 | .name = "!~", | |
337 | .is_acceptable = op2_re_unmatch, | |
338 | .check_type = op2_check_type_re, | |
339 | }, | |
846b4cbe MY |
340 | }; |
341 | ||
342 | #define NODE_CLASS(NODE) (&node_classes[(NODE)->type]) | |
9b6ad16f | 343 | static const struct node_class node_classes[] = { |
846b4cbe MY |
344 | [NODE_STR] = { |
345 | .name = "STR", | |
346 | .free = node_str_free, | |
347 | .dump = node_str_dump, | |
348 | }, | |
349 | [NODE_NUM] = { | |
350 | .name = "NUM", | |
351 | .dump = node_num_dump, | |
352 | }, | |
1fa18564 MY |
353 | [NODE_FNUM] = { |
354 | .name = "FNUM", | |
355 | .dump = node_fnum_dump, | |
356 | }, | |
846b4cbe MY |
357 | [NODE_BOOL] = { |
358 | .name = "BOOL", | |
359 | .dump = node_bool_dump, | |
360 | }, | |
abaf378c MY |
361 | [NODE_RE] = { |
362 | .name = "STR", | |
363 | .free = node_re_free, | |
364 | .dump = node_re_dump, | |
365 | }, | |
846b4cbe MY |
366 | [NODE_OP1] = { |
367 | .name = "OP1", | |
368 | .free = node_op1_free, | |
369 | .dump = node_op1_dump, | |
370 | }, | |
371 | [NODE_OP2] = { | |
372 | .name = "OP2", | |
373 | .free = node_op2_free, | |
374 | .dump = node_op2_dump, | |
375 | } | |
376 | }; | |
377 | ||
378 | /* | |
379 | * Functions | |
380 | */ | |
381 | static int strputc(char **a, const char b) | |
382 | { | |
383 | return strappend(a, (char [2]){b, '\0'}); | |
384 | } | |
385 | ||
386 | static void xstrputc(char **a, const char b) | |
387 | { | |
388 | int rc = strputc(a, b); | |
389 | if (rc < 0) | |
390 | errx(EXIT_FAILURE, _("failed to allocate memory")); | |
391 | } | |
392 | ||
393 | static void parser_init(struct parser *parser, const char *const expr, struct libscols_table *tb, | |
394 | int ncols, | |
395 | int (*column_name_to_id)(const char *, void *), | |
396 | struct libscols_column *(*add_column_by_id)(struct libscols_table *, int, void*), | |
397 | void *data) | |
398 | { | |
399 | parser->expr = expr; | |
400 | parser->cursor = parser->expr; | |
401 | parser->paren_level = 0; | |
402 | parser->tb = tb; | |
403 | parser->column_name_to_id = column_name_to_id; | |
404 | parser->add_column_by_id = add_column_by_id; | |
405 | parser->data = data; | |
406 | parser->parameters = xcalloc(ncols, sizeof(struct parameter)); | |
407 | parser->errmsg[0] = '\0'; | |
408 | } | |
409 | ||
410 | static char parser_getc(struct parser *parser) | |
411 | { | |
412 | char c = *parser->cursor; | |
413 | if (c != '\0') | |
414 | parser->cursor++; | |
415 | return c; | |
416 | } | |
417 | ||
418 | static void parser_ungetc(struct parser *parser, char c) | |
419 | { | |
420 | assert(parser->cursor > parser->expr); | |
421 | if (c != '\0') | |
422 | parser->cursor--; | |
423 | } | |
424 | ||
425 | static void parser_read_str(struct parser *parser, struct token *token, char delimiter) | |
426 | { | |
427 | bool escape = false; | |
428 | while (1) { | |
429 | char c = parser_getc(parser); | |
430 | ||
431 | if (c == '\0') { | |
91a484fe | 432 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
433 | _("error: string literal is not terminated: %s"), |
434 | token->val.str? : ""); | |
435 | return; | |
436 | } else if (escape) { | |
437 | switch (c) { | |
438 | case '\\': | |
439 | case '\'': | |
440 | case '"': | |
441 | xstrputc(&token->val.str, c); | |
442 | break; | |
443 | case 'n': | |
444 | xstrputc(&token->val.str, '\n'); | |
445 | break; | |
446 | case 't': | |
447 | xstrputc(&token->val.str, '\t'); | |
448 | break; | |
449 | /* TODO: \f, \r, ... */ | |
450 | default: | |
451 | xstrputc(&token->val.str, '\\'); | |
452 | xstrputc(&token->val.str, c); | |
453 | return; | |
454 | } | |
455 | escape = false; | |
cdbb34d0 MY |
456 | } else if (c == delimiter) { |
457 | if (token->val.str == NULL) | |
458 | token->val.str = xstrdup(""); | |
846b4cbe | 459 | return; |
cdbb34d0 | 460 | } else if (c == '\\') |
846b4cbe MY |
461 | escape = true; |
462 | else | |
463 | xstrputc(&token->val.str, c); | |
464 | } | |
465 | } | |
466 | ||
467 | static void parser_read_name(struct parser *parser, struct token *token) | |
468 | { | |
469 | while (1) { | |
470 | char c = parser_getc(parser); | |
471 | if (c == '\0') | |
472 | break; | |
473 | if (strchr(COL_HEADER_EXTRA_CHARS, c) || isalnum((unsigned char)c)) { | |
474 | xstrputc(&token->val.str, c); | |
475 | continue; | |
476 | } | |
477 | parser_ungetc(parser, c); | |
478 | break; | |
479 | } | |
480 | } | |
481 | ||
482 | static int parser_read_dec(struct parser *parser, struct token *token) | |
483 | { | |
484 | int rc = 0; | |
1fa18564 | 485 | int found_point = 0; |
846b4cbe MY |
486 | while (1) { |
487 | char c = parser_getc(parser); | |
488 | if (c == '\0') | |
489 | break; | |
1fa18564 MY |
490 | if (isdigit((unsigned char)c) |
491 | || (found_point == 0 && c == '.')) { | |
846b4cbe | 492 | xstrputc(&token->val.str, c); |
1fa18564 MY |
493 | if (c == '.') |
494 | found_point++; | |
846b4cbe MY |
495 | continue; |
496 | } | |
497 | parser_ungetc(parser, c); | |
498 | break; | |
499 | } | |
500 | ||
1fa18564 | 501 | char *endptr = NULL; |
846b4cbe | 502 | errno = 0; |
1fa18564 | 503 | unsigned long long num = strtoull(token->val.str, &endptr, 10); |
846b4cbe | 504 | rc = errno; |
70b1623c MY |
505 | if (rc) |
506 | return rc; | |
507 | ||
1fa18564 MY |
508 | if (endptr && *endptr == '.') { |
509 | errno = 0; | |
510 | long double fnum = strtold(endptr, NULL); | |
511 | rc = errno; | |
512 | if (rc) | |
513 | return rc; | |
514 | free(token->val.str); | |
515 | token->type = TOKEN_FDEC; | |
516 | token->val.fnum = ((long double)num) + fnum; | |
517 | } else { | |
518 | free(token->val.str); | |
519 | token->type = TOKEN_DEC; | |
520 | token->val.num = num; | |
521 | } | |
522 | ||
523 | return 0; | |
846b4cbe MY |
524 | } |
525 | ||
526 | static struct token *parser_read(struct parser *parser) | |
527 | { | |
528 | struct token *t = token_new(); | |
529 | char c, c0; | |
530 | ||
531 | do | |
532 | c = parser_getc(parser); | |
533 | while (isspace((unsigned char)c)); | |
534 | ||
535 | switch (c) { | |
536 | case '\0': | |
537 | t->type = TOKEN_EOF; | |
538 | break; | |
539 | case '(': | |
540 | t->type = TOKEN_OPEN; | |
541 | parser->paren_level++; | |
542 | break; | |
543 | case ')': | |
544 | t->type = TOKEN_CLOSE; | |
545 | parser->paren_level--; | |
546 | if (parser->paren_level < 0) | |
91a484fe | 547 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
548 | _("error: unbalanced parenthesis: %s"), parser->cursor - 1); |
549 | break; | |
550 | case '!': | |
551 | c0 = parser_getc(parser); | |
552 | if (c0 == '=') { | |
553 | t->type = TOKEN_OP2; | |
554 | t->val.op2 = OP2_NE; | |
555 | break; | |
f439236f MY |
556 | } else if (c0 == '~') { |
557 | t->type = TOKEN_OP2; | |
558 | t->val.op2 = OP2_RE_UNMATCH; | |
559 | break; | |
846b4cbe MY |
560 | } |
561 | parser_ungetc(parser, c0); | |
562 | t->type = TOKEN_OP1; | |
563 | t->val.op1 = OP1_NOT; | |
564 | break; | |
565 | case '<': | |
566 | t->type = TOKEN_OP2; | |
567 | c0 = parser_getc(parser); | |
568 | if (c0 == '=') { | |
569 | t->val.op2 = OP2_LE; | |
570 | break; | |
571 | } | |
572 | parser_ungetc(parser, c0); | |
573 | t->val.op2 = OP2_LT; | |
574 | break; | |
575 | case '>': | |
576 | t->type = TOKEN_OP2; | |
577 | c0 = parser_getc(parser); | |
578 | if (c0 == '=') { | |
579 | t->val.op2 = OP2_GE; | |
580 | break; | |
581 | } | |
582 | parser_ungetc(parser, c0); | |
583 | t->val.op2 = OP2_GT; | |
584 | break; | |
585 | case '=': | |
586 | c0 = parser_getc(parser); | |
587 | if (c0 == '=') { | |
588 | t->type = TOKEN_OP2; | |
589 | t->val.op2 = OP2_EQ; | |
590 | break; | |
abaf378c MY |
591 | } else if (c0 == '~') { |
592 | t->type = TOKEN_OP2; | |
593 | t->val.op2 = OP2_RE_MATCH; | |
594 | break; | |
846b4cbe | 595 | } |
91a484fe | 596 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
597 | _("error: unexpected character %c after ="), c0); |
598 | break; | |
599 | case '&': | |
600 | c0 = parser_getc(parser); | |
601 | if (c0 == '&') { | |
602 | t->type = TOKEN_OP2; | |
603 | t->val.op2 = OP2_AND; | |
604 | break; | |
605 | } | |
91a484fe | 606 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
607 | _("error: unexpected character %c after ="), c0); |
608 | break; | |
609 | case '|': | |
610 | c0 = parser_getc(parser); | |
611 | if (c0 == '|') { | |
612 | t->type = TOKEN_OP2; | |
a938f9f0 | 613 | t->val.op2 = OP2_OR; |
846b4cbe MY |
614 | break; |
615 | } | |
91a484fe | 616 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
617 | _("error: unexpected character %c after ="), c0); |
618 | break; | |
619 | case '"': | |
620 | case '\'': | |
621 | t->type = TOKEN_STR; | |
622 | parser_read_str(parser, t, c); | |
623 | break; | |
624 | default: | |
d0238cb7 | 625 | if (isalpha((unsigned char)c) || c == '_') { |
846b4cbe MY |
626 | xstrputc(&t->val.str, c); |
627 | parser_read_name(parser, t); | |
628 | if (strcmp(t->val.str, "true") == 0) { | |
629 | free(t->val.str); | |
630 | t->type = TOKEN_TRUE; | |
631 | } else if (strcmp(t->val.str, "false") == 0) { | |
632 | free(t->val.str); | |
633 | t->type = TOKEN_FALSE; | |
634 | } else if (strcmp(t->val.str, "or") == 0) { | |
635 | free(t->val.str); | |
636 | t->type = TOKEN_OP2; | |
a938f9f0 | 637 | t->val.op2 = OP2_OR; |
846b4cbe MY |
638 | } else if (strcmp(t->val.str, "and") == 0) { |
639 | free(t->val.str); | |
640 | t->type = TOKEN_OP2; | |
a938f9f0 | 641 | t->val.op2 = OP2_AND; |
846b4cbe MY |
642 | } else if (strcmp(t->val.str, "eq") == 0) { |
643 | free(t->val.str); | |
644 | t->type = TOKEN_OP2; | |
a938f9f0 | 645 | t->val.op2 = OP2_EQ; |
846b4cbe MY |
646 | } else if (strcmp(t->val.str, "ne") == 0) { |
647 | free(t->val.str); | |
648 | t->type = TOKEN_OP2; | |
a938f9f0 | 649 | t->val.op2 = OP2_NE; |
846b4cbe MY |
650 | } else if (strcmp(t->val.str, "lt") == 0) { |
651 | free(t->val.str); | |
652 | t->type = TOKEN_OP2; | |
653 | t->val.op2 = OP2_LT; | |
654 | } else if (strcmp(t->val.str, "le") == 0) { | |
655 | free(t->val.str); | |
656 | t->type = TOKEN_OP2; | |
657 | t->val.op2 = OP2_LE; | |
658 | } else if (strcmp(t->val.str, "gt") == 0) { | |
659 | free(t->val.str); | |
660 | t->type = TOKEN_OP2; | |
661 | t->val.op2 = OP2_GT; | |
662 | } else if (strcmp(t->val.str, "ge") == 0) { | |
663 | free(t->val.str); | |
664 | t->type = TOKEN_OP2; | |
665 | t->val.op2 = OP2_GE; | |
666 | } else if (strcmp(t->val.str, "not") == 0) { | |
667 | free(t->val.str); | |
668 | t->type = TOKEN_OP1; | |
669 | t->val.op1 = OP1_NOT; | |
670 | } else | |
671 | t->type = TOKEN_NAME; | |
672 | break; | |
673 | } else if (isdigit((unsigned char)c)) { | |
846b4cbe | 674 | xstrputc(&t->val.str, c); |
70b1623c | 675 | if (parser_read_dec(parser, t) != 0) { |
91a484fe | 676 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
70b1623c MY |
677 | _("error: failed to convert input to number: %s"), |
678 | t->val.str); | |
679 | free(t->val.str); | |
680 | } | |
846b4cbe MY |
681 | break; |
682 | } | |
91a484fe | 683 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
684 | _("error: unexpected character %c"), c); |
685 | break; | |
686 | } | |
687 | return t; | |
688 | } | |
689 | ||
690 | static void parameter_init(struct parameter *param, struct libscols_column *cl) | |
691 | { | |
692 | param->cl = cl; | |
693 | param->has_value = false; | |
564d954a | 694 | param->floating_point_num = false; |
846b4cbe MY |
695 | } |
696 | ||
846b4cbe MY |
697 | static struct node *dparser_compile1(struct parser *parser, struct node *last) |
698 | { | |
699 | struct token *t = parser_read(parser); | |
700 | ||
701 | if (GOT_ERROR(parser)) { | |
702 | token_free(t); | |
703 | return NULL; | |
704 | } | |
705 | ||
706 | if (t->type == TOKEN_EOF) { | |
707 | token_free(t); | |
708 | return last; | |
709 | } | |
710 | if (t->type == TOKEN_CLOSE) { | |
711 | token_free(t); | |
712 | return last; | |
713 | } | |
714 | ||
715 | if (last) { | |
716 | switch (t->type) { | |
717 | case TOKEN_NAME: | |
718 | case TOKEN_STR: | |
719 | case TOKEN_DEC: | |
1fa18564 | 720 | case TOKEN_FDEC: |
846b4cbe MY |
721 | case TOKEN_TRUE: |
722 | case TOKEN_FALSE: | |
723 | case TOKEN_OPEN: | |
724 | case TOKEN_OP1: | |
91a484fe | 725 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
726 | _("error: unexpected token: %s after %s"), t->val.str, |
727 | NODE_CLASS(last)->name); | |
728 | token_free(t); | |
729 | return NULL; | |
730 | default: | |
731 | break; | |
732 | } | |
733 | } else { | |
734 | switch (t->type) { | |
735 | case TOKEN_OP2: | |
91a484fe | 736 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
737 | _("error: empty left side expression: %s"), |
738 | TOKEN_OP2_CLASS(t)->name); | |
739 | token_free(t); | |
740 | return NULL; | |
741 | default: | |
742 | break; | |
743 | } | |
744 | } | |
745 | ||
746 | struct node *node = NULL; | |
747 | switch (t->type) { | |
eb83898a | 748 | case TOKEN_NAME: { |
846b4cbe | 749 | int col_id = parser->column_name_to_id(t->val.str, parser->data); |
87ffe87f | 750 | if (col_id == LSFD_FILTER_UNKNOWN_COL_ID) { |
91a484fe | 751 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
752 | _("error: no such column: %s"), t->val.str); |
753 | token_free(t); | |
754 | return NULL; | |
755 | ||
756 | } | |
757 | ||
0cc8e82a | 758 | struct libscols_column *cl = scols_table_get_column_by_name(parser->tb, t->val.str); |
846b4cbe MY |
759 | if (!cl) { |
760 | cl = parser->add_column_by_id(parser->tb, col_id, parser->data); | |
761 | if (!cl) { | |
91a484fe | 762 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
763 | _("error: cannot add a column to table: %s"), t->val.str); |
764 | token_free(t); | |
765 | return NULL; | |
766 | } | |
767 | scols_column_set_flags(cl, SCOLS_FL_HIDDEN); | |
768 | } | |
769 | parameter_init(parser->parameters + col_id, cl); | |
846b4cbe MY |
770 | |
771 | int jtype = scols_column_get_json_type(cl); | |
772 | int ntype; | |
773 | switch (jtype) { | |
774 | case SCOLS_JSON_STRING: | |
de7e1149 MY |
775 | case SCOLS_JSON_ARRAY_STRING: |
776 | case SCOLS_JSON_ARRAY_NUMBER: | |
777 | /* We handles SCOLS_JSON_ARRAY_* as a string | |
778 | * till we implement operators for arrays. */ | |
846b4cbe MY |
779 | ntype = NODE_STR; |
780 | break; | |
781 | case SCOLS_JSON_NUMBER: | |
782 | ntype = NODE_NUM; | |
783 | break; | |
784 | case SCOLS_JSON_BOOLEAN: | |
785 | ntype = NODE_BOOL; | |
786 | break; | |
787 | default: | |
91a484fe | 788 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
789 | _("error: unsupported column data type: %d, column: %s"), |
790 | jtype, t->val.str); | |
791 | return NULL; | |
792 | } | |
793 | node = node_val_new(ntype, col_id); | |
f22ef0fe | 794 | token_free(t); |
846b4cbe | 795 | return node; |
eb83898a | 796 | } |
846b4cbe MY |
797 | |
798 | case TOKEN_STR: | |
799 | node = node_val_new(NODE_STR, -1); | |
800 | VAL(node, str) = xstrdup(t->val.str); | |
801 | token_free(t); | |
802 | return node; | |
803 | ||
804 | case TOKEN_DEC: | |
805 | node = node_val_new(NODE_NUM, -1); | |
806 | VAL(node, num) = t->val.num; | |
807 | token_free(t); | |
808 | return node; | |
1fa18564 MY |
809 | case TOKEN_FDEC: |
810 | node = node_val_new(NODE_FNUM, -1); | |
811 | VAL(node, fnum) = t->val.fnum; | |
812 | token_free(t); | |
813 | return node; | |
846b4cbe MY |
814 | |
815 | case TOKEN_TRUE: | |
816 | case TOKEN_FALSE: | |
817 | node = node_val_new(NODE_BOOL, -1); | |
818 | VAL(node, boolean) = (t->type == TOKEN_TRUE); | |
819 | token_free(t); | |
820 | return node; | |
821 | ||
822 | case TOKEN_OPEN: | |
823 | token_free(t); | |
824 | return dparser_compile(parser); | |
825 | ||
eb83898a | 826 | case TOKEN_OP1: { |
846b4cbe | 827 | struct node *op1_right = dparser_compile1(parser, NULL); |
9b6ad16f | 828 | const struct op1_class *op1_class = TOKEN_OP1_CLASS(t); |
f22ef0fe | 829 | |
846b4cbe MY |
830 | token_free(t); |
831 | ||
832 | if (GOT_ERROR(parser)) { | |
833 | node_free(op1_right); | |
834 | return NULL; | |
835 | } | |
836 | ||
837 | if (op1_right == NULL) { | |
91a484fe | 838 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
839 | _("error: empty right side expression: %s"), |
840 | op1_class->name); | |
841 | return NULL; | |
842 | } | |
843 | ||
23a4b2b2 | 844 | if (!op1_class->check_type(parser, op1_class, op1_right)) { |
846b4cbe MY |
845 | node_free(op1_right); |
846 | return NULL; | |
847 | } | |
848 | ||
849 | node = xmalloc(sizeof(struct node_op1)); | |
850 | node->type = NODE_OP1; | |
851 | ((struct node_op1 *)node)->opclass = op1_class; | |
852 | ((struct node_op1 *)node)->arg = op1_right; | |
853 | ||
854 | return node; | |
eb83898a | 855 | } |
846b4cbe | 856 | |
eb83898a | 857 | case TOKEN_OP2: { |
846b4cbe | 858 | struct node *op2_right = dparser_compile1(parser, NULL); |
9b6ad16f | 859 | const struct op2_class *op2_class = TOKEN_OP2_CLASS(t); |
f22ef0fe | 860 | |
846b4cbe MY |
861 | token_free(t); |
862 | ||
863 | if (GOT_ERROR(parser)) { | |
864 | node_free(op2_right); | |
865 | return NULL; | |
866 | } | |
867 | if (op2_right == NULL) { | |
91a484fe | 868 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
846b4cbe MY |
869 | _("error: empty right side expression: %s"), |
870 | op2_class->name); | |
871 | return NULL; | |
872 | } | |
873 | ||
23a4b2b2 | 874 | if (!op2_class->check_type(parser, op2_class, last, op2_right)) { |
846b4cbe MY |
875 | node_free(op2_right); |
876 | return NULL; | |
877 | } | |
878 | ||
879 | node = xmalloc(sizeof(struct node_op2)); | |
880 | node->type = NODE_OP2; | |
881 | ((struct node_op2 *)node)->opclass = op2_class; | |
882 | ((struct node_op2 *)node)->args[0] = last; | |
883 | ((struct node_op2 *)node)->args[1] = op2_right; | |
884 | ||
885 | return node; | |
eb83898a | 886 | } |
846b4cbe MY |
887 | |
888 | default: | |
889 | warnx("unexpected token type: %d", t->type); | |
f22ef0fe | 890 | token_free(t); |
846b4cbe MY |
891 | return NULL; |
892 | } | |
893 | } | |
894 | ||
895 | static struct node *dparser_compile(struct parser *parser) | |
896 | { | |
897 | struct node *node = NULL; | |
898 | ||
899 | while (true) { | |
900 | struct node *node0 = dparser_compile1(parser, node); | |
901 | if (GOT_ERROR(parser)) { | |
902 | node_free(node); | |
903 | return NULL; | |
904 | } | |
905 | ||
28c20d30 MY |
906 | if (node == node0) { |
907 | if (node == NULL) | |
91a484fe | 908 | xstrncpy(parser->errmsg, |
28c20d30 | 909 | _("error: empty filter expression"), |
91a484fe | 910 | sizeof(parser->errmsg)); |
846b4cbe | 911 | return node; |
28c20d30 | 912 | } |
846b4cbe MY |
913 | node = node0; |
914 | } | |
915 | } | |
916 | ||
917 | static struct token *token_new(void) | |
918 | { | |
919 | return xcalloc(1, sizeof(struct token)); | |
920 | } | |
921 | ||
922 | static void token_free(struct token *token) | |
923 | { | |
924 | if (TOKEN_CLASS(token)->free) | |
925 | TOKEN_CLASS(token)->free(token); | |
926 | free(token); | |
927 | } | |
928 | ||
ae6ccf51 | 929 | #ifdef DEBUG |
846b4cbe MY |
930 | static void token_dump(struct token *token, FILE *stream) |
931 | { | |
932 | fprintf(stream, "<%s>", TOKEN_CLASS(token)->name); | |
933 | if (TOKEN_CLASS(token)->dump) | |
934 | TOKEN_CLASS(token)->dump(token, stream); | |
935 | fputc('\n', stream); | |
936 | } | |
ae6ccf51 | 937 | #endif /* DEBUG */ |
846b4cbe MY |
938 | |
939 | static void token_free_str(struct token *token) | |
940 | { | |
941 | free(token->val.str); | |
942 | } | |
943 | ||
944 | static void token_dump_str(struct token *token, FILE *stream) | |
945 | { | |
946 | fputs(token->val.str, stream); | |
947 | } | |
948 | ||
949 | static void token_dump_num(struct token *token, FILE *stream) | |
950 | { | |
951 | fprintf(stream, "%llu", token->val.num); | |
952 | } | |
953 | ||
1fa18564 MY |
954 | static void token_dump_fnum(struct token *token, FILE *stream) |
955 | { | |
956 | fprintf(stream, "%Lf", token->val.fnum); | |
957 | } | |
958 | ||
846b4cbe MY |
959 | static void token_dump_op1(struct token *token, FILE *stream) |
960 | { | |
961 | fputs(TOKEN_OP1_CLASS(token)->name, stream); | |
962 | } | |
963 | ||
964 | static void token_dump_op2(struct token *token, FILE *stream) | |
965 | { | |
966 | fputs(TOKEN_OP2_CLASS(token)->name, stream); | |
967 | } | |
968 | ||
969 | static struct node *node_val_new(enum node_type type, int pindex) | |
970 | { | |
971 | struct node *node = xmalloc(sizeof(struct node_val)); | |
972 | node->type = type; | |
973 | PINDEX(node) = pindex; | |
974 | return node; | |
975 | } | |
976 | ||
977 | static void node_free(struct node *node) | |
978 | { | |
979 | if (node == NULL) | |
980 | return; | |
981 | if (NODE_CLASS(node)->free) | |
982 | NODE_CLASS(node)->free(node); | |
983 | free(node); | |
984 | } | |
985 | ||
986 | static bool node_apply(struct node *node, struct parameter *params, struct libscols_line *ln) | |
987 | { | |
988 | if (!node) | |
989 | return true; | |
990 | ||
991 | switch (node->type) { | |
eb83898a | 992 | case NODE_OP1: { |
846b4cbe MY |
993 | struct node_op1 *node_op1 = (struct node_op1*)node; |
994 | return node_op1->opclass->is_acceptable(node_op1->arg, params, ln); | |
eb83898a MY |
995 | } |
996 | case NODE_OP2: { | |
846b4cbe MY |
997 | struct node_op2 *node_op2 = (struct node_op2*)node; |
998 | return node_op2->opclass->is_acceptable(node_op2->args[0], node_op2->args[1], params, ln); | |
eb83898a | 999 | } |
846b4cbe MY |
1000 | case NODE_BOOL: |
1001 | if (PINDEX(node) < 0) | |
1002 | return VAL(node,boolean); | |
1003 | ||
1004 | if (!params[PINDEX(node)].has_value) { | |
f8fa2c2a | 1005 | const char *data = scols_line_get_column_data(ln, params[PINDEX(node)].cl); |
846b4cbe MY |
1006 | if (data == NULL) |
1007 | return false; | |
1008 | params[PINDEX(node)].val.boolean = !*data ? false : | |
1009 | *data == '0' ? false : | |
1010 | *data == 'N' || *data == 'n' ? false : true; | |
1011 | params[PINDEX(node)].has_value = true; | |
1012 | } | |
1013 | return params[PINDEX(node)].val.boolean; | |
1014 | default: | |
1015 | warnx(_("unexpected type in filter application: %s"), NODE_CLASS(node)->name); | |
1016 | return false; | |
1017 | } | |
1018 | } | |
1019 | ||
1020 | static void node_dump(struct node *node, struct parameter *param, int depth, FILE *stream) | |
1021 | { | |
024ca9ba MY |
1022 | int i; |
1023 | ||
846b4cbe MY |
1024 | if (!node) |
1025 | return; | |
1026 | ||
024ca9ba | 1027 | for (i = 0; i < depth; i++) |
846b4cbe MY |
1028 | fputc(' ', stream); |
1029 | fputs(NODE_CLASS(node)->name, stream); | |
1030 | if (NODE_CLASS(node)->dump) | |
1031 | NODE_CLASS(node)->dump(node, param, depth, stream); | |
1032 | } | |
1033 | ||
1034 | static void node_str_dump(struct node *node, struct parameter* params, int depth __attribute__((__unused__)), FILE *stream) | |
1035 | { | |
1036 | if (PINDEX(node) >= 0) | |
39679ea0 | 1037 | fprintf(stream, ": |%s|\n", scols_column_get_name(params[PINDEX(node)].cl)); |
846b4cbe MY |
1038 | else |
1039 | fprintf(stream, ": '%s'\n", VAL(node,str)); | |
1040 | } | |
1041 | ||
1042 | static void node_num_dump(struct node *node, struct parameter* params, int depth __attribute__((__unused__)), FILE *stream) | |
1043 | { | |
1044 | if (PINDEX(node) >= 0) | |
39679ea0 | 1045 | fprintf(stream, ": |%s|\n", scols_column_get_name(params[PINDEX(node)].cl)); |
846b4cbe MY |
1046 | else |
1047 | fprintf(stream, ": %llu\n", VAL(node,num)); | |
1048 | } | |
1049 | ||
1fa18564 MY |
1050 | static void node_fnum_dump(struct node *node, struct parameter* params, int depth __attribute__((__unused__)), FILE *stream) |
1051 | { | |
1052 | if (PINDEX(node) >= 0) | |
1053 | fprintf(stream, ": |%s|\n", scols_column_get_name(params[PINDEX(node)].cl)); | |
1054 | else | |
1055 | fprintf(stream, ": %Lf\n", VAL(node,fnum)); | |
1056 | } | |
1057 | ||
846b4cbe MY |
1058 | static void node_bool_dump(struct node *node, struct parameter* params, int depth __attribute__((__unused__)), FILE *stream) |
1059 | { | |
1060 | if (PINDEX(node) >= 0) | |
39679ea0 | 1061 | fprintf(stream, ": |%s|\n", scols_column_get_name(params[PINDEX(node)].cl)); |
846b4cbe MY |
1062 | else |
1063 | fprintf(stream, ": %s\n", | |
1064 | VAL(node,boolean) | |
1065 | ? token_classes[TOKEN_TRUE].name | |
1066 | : token_classes[TOKEN_FALSE].name); | |
1067 | } | |
1068 | ||
abaf378c MY |
1069 | static void node_re_dump(struct node *node, struct parameter* params __attribute__((__unused__)), |
1070 | int depth __attribute__((__unused__)), FILE *stream) | |
1071 | { | |
1072 | fprintf(stream, ": #<regexp %p>\n", &VAL(node,re)); | |
1073 | } | |
1074 | ||
846b4cbe MY |
1075 | static void node_op1_dump(struct node *node, struct parameter* params, int depth, FILE *stream) |
1076 | { | |
1077 | fprintf(stream, ": %s\n", ((struct node_op1 *)node)->opclass->name); | |
1078 | node_dump(((struct node_op1 *)node)->arg, params, depth + 4, stream); | |
1079 | } | |
1080 | ||
1081 | static void node_op2_dump(struct node *node, struct parameter* params, int depth, FILE *stream) | |
1082 | { | |
024ca9ba MY |
1083 | int i; |
1084 | ||
846b4cbe | 1085 | fprintf(stream, ": %s\n", ((struct node_op2 *)node)->opclass->name); |
024ca9ba | 1086 | for (i = 0; i < 2; i++) |
846b4cbe MY |
1087 | node_dump(((struct node_op2 *)node)->args[i], params, depth + 4, stream); |
1088 | } | |
1089 | ||
1090 | static void node_str_free(struct node *node) | |
1091 | { | |
1092 | if (PINDEX(node) < 0) | |
1093 | free(VAL(node,str)); | |
1094 | } | |
1095 | ||
abaf378c MY |
1096 | static void node_re_free(struct node *node) |
1097 | { | |
1098 | regfree(&VAL(node,re)); | |
1099 | } | |
1100 | ||
846b4cbe MY |
1101 | static void node_op1_free(struct node *node) |
1102 | { | |
1103 | node_free(((struct node_op1 *)node)->arg); | |
1104 | } | |
1105 | ||
1106 | static void node_op2_free(struct node *node) | |
1107 | { | |
024ca9ba MY |
1108 | int i; |
1109 | ||
1110 | for (i = 0; i < 2; i++) | |
846b4cbe MY |
1111 | node_free(((struct node_op2 *)node)->args[i]); |
1112 | } | |
1113 | ||
1114 | static bool op1_not(struct node *node, struct parameter* params, struct libscols_line * ln) | |
1115 | { | |
1116 | return !node_apply(node, params, ln); | |
1117 | } | |
1118 | ||
9b6ad16f | 1119 | static bool op1_check_type_bool_or_op(struct parser* parser, const struct op1_class *op1_class, |
23a4b2b2 | 1120 | struct node *node) |
846b4cbe | 1121 | { |
23a4b2b2 | 1122 | if (! (node->type == NODE_OP1 || node->type == NODE_OP2 || node->type == NODE_BOOL)) { |
91a484fe | 1123 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
23a4b2b2 MY |
1124 | _("error: unexpected operand type %s for: %s"), |
1125 | NODE_CLASS(node)->name, | |
1126 | op1_class->name); | |
1127 | return false; | |
1128 | } | |
1129 | return true; | |
846b4cbe MY |
1130 | } |
1131 | ||
1132 | #define OP2_GET_STR(NODE,DEST) do { \ | |
1133 | int pindex = PINDEX(NODE); \ | |
1134 | if (pindex < 0) \ | |
1135 | DEST = VAL(NODE,str); \ | |
1136 | else { \ | |
1137 | struct parameter *p = params + pindex; \ | |
1138 | if (!p->has_value) { \ | |
f8fa2c2a | 1139 | p->val.str = scols_line_get_column_data(ln, p->cl); \ |
846b4cbe MY |
1140 | if (p->val.str == NULL) return false; \ |
1141 | p->has_value = true; \ | |
1142 | } \ | |
1143 | DEST = p->val.str; \ | |
1144 | } \ | |
1145 | } while(0) | |
1146 | ||
564d954a MY |
1147 | struct compnum { |
1148 | bool floating_point_num; | |
1149 | union { | |
1150 | unsigned long long v; | |
1151 | long double fv; | |
1152 | }; | |
1153 | }; | |
1154 | ||
846b4cbe MY |
1155 | #define OP2_GET_NUM(NODE,DEST) do { \ |
1156 | int pindex = PINDEX(NODE); \ | |
564d954a | 1157 | if (pindex < 0) { \ |
1fa18564 MY |
1158 | if (NODE->type == NODE_NUM) { \ |
1159 | DEST.v = VAL(NODE,num); \ | |
1160 | DEST.floating_point_num = false; \ | |
1161 | } else { \ | |
1162 | DEST.fv = VAL(NODE,fnum); \ | |
1163 | DEST.floating_point_num = true; \ | |
1164 | } \ | |
564d954a | 1165 | } else { \ |
846b4cbe MY |
1166 | struct parameter *p = params + pindex; \ |
1167 | if (!p->has_value) { \ | |
564d954a MY |
1168 | unsigned long long val; \ |
1169 | char *endptr = NULL; \ | |
f8fa2c2a | 1170 | const char *tmp = scols_line_get_column_data(ln, p->cl); \ |
846b4cbe | 1171 | if (tmp == NULL) return false; \ |
564d954a MY |
1172 | val = strtoull(tmp, &endptr, 10); \ |
1173 | if (endptr && endptr[0] == '.') { \ | |
1174 | long double fval = strtold(endptr, NULL); \ | |
1175 | if (fval != 0.0) { \ | |
1176 | p->val.fnum = val + fval; \ | |
1177 | p->floating_point_num = true; \ | |
1178 | } else { \ | |
1179 | p->val.num = val; \ | |
1180 | p->floating_point_num = false; \ | |
1181 | } \ | |
1182 | } else { \ | |
1183 | p->val.num = val; \ | |
1184 | p->floating_point_num = false; \ | |
1185 | } \ | |
846b4cbe MY |
1186 | p->has_value = true; \ |
1187 | } \ | |
564d954a MY |
1188 | if (p->floating_point_num) { \ |
1189 | DEST.fv = p->val.fnum; \ | |
1190 | DEST.floating_point_num = true; \ | |
1191 | } else { \ | |
1192 | DEST.v = p->val.num; \ | |
1193 | DEST.floating_point_num = false; \ | |
1194 | } \ | |
846b4cbe MY |
1195 | } \ |
1196 | } while(0) | |
1197 | ||
564d954a MY |
1198 | #define OP2_NUM_CMP_BODY(OP) do { \ |
1199 | struct compnum lv, rv; \ | |
1200 | OP2_GET_NUM(left,lv); \ | |
1201 | OP2_GET_NUM(right,rv); \ | |
1202 | if (lv.floating_point_num && rv.floating_point_num) \ | |
1203 | return lv.fv OP rv.fv; \ | |
1204 | else if (! (lv.floating_point_num || rv.floating_point_num)) \ | |
1205 | return lv.v OP rv.v; \ | |
1206 | else if (lv.floating_point_num) \ | |
1207 | return lv.fv OP ((long double)rv.v); \ | |
1208 | else \ | |
1209 | return ((long double)lv.v) OP rv.fv; \ | |
520f03a2 MY |
1210 | } while(0) |
1211 | ||
846b4cbe MY |
1212 | #define OP2_EQ_BODY(OP,ELSEVAL) do { \ |
1213 | if (left->type == NODE_STR) { \ | |
1214 | const char *lv, *rv; \ | |
1215 | OP2_GET_STR(left,lv); \ | |
1216 | OP2_GET_STR(right,rv); \ | |
1217 | return strcmp(lv, rv) OP 0; \ | |
1fa18564 | 1218 | } else if (left->type == NODE_NUM || left->type == NODE_FNUM) { \ |
520f03a2 | 1219 | OP2_NUM_CMP_BODY(OP); \ |
846b4cbe MY |
1220 | } else { \ |
1221 | return node_apply(left, params, ln) OP node_apply(right, params, ln); \ | |
1222 | } \ | |
1223 | } while(0) | |
1224 | ||
846b4cbe MY |
1225 | static bool op2_eq(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) |
1226 | { | |
1227 | OP2_EQ_BODY(==, false); | |
1228 | } | |
1229 | ||
1230 | static bool op2_ne(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1231 | { | |
1232 | OP2_EQ_BODY(!=, true); | |
1233 | } | |
1234 | ||
1235 | static bool op2_and(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1236 | { | |
1237 | return node_apply(left, params, ln) && node_apply(right, params, ln); | |
1238 | } | |
1239 | ||
1240 | static bool op2_or(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1241 | { | |
1242 | return node_apply(left, params, ln) || node_apply(right, params, ln); | |
1243 | } | |
1244 | ||
1245 | static bool op2_lt(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1246 | { | |
520f03a2 | 1247 | OP2_NUM_CMP_BODY(<); |
846b4cbe MY |
1248 | } |
1249 | ||
1250 | static bool op2_le(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1251 | { | |
520f03a2 | 1252 | OP2_NUM_CMP_BODY(<=); |
846b4cbe MY |
1253 | } |
1254 | ||
1255 | static bool op2_gt(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1256 | { | |
520f03a2 | 1257 | OP2_NUM_CMP_BODY(>); |
846b4cbe MY |
1258 | } |
1259 | ||
1260 | static bool op2_ge(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln) | |
1261 | { | |
520f03a2 | 1262 | OP2_NUM_CMP_BODY(>=); |
846b4cbe MY |
1263 | } |
1264 | ||
abaf378c MY |
1265 | static bool op2_re_match(struct node *left, struct node *right, |
1266 | struct parameter *params, struct libscols_line *ln) | |
1267 | { | |
1268 | const char *str; | |
1269 | OP2_GET_STR(left, str); | |
1270 | ||
1271 | return (regexec(&VAL(right,re), str, 0, NULL, 0) == 0); | |
1272 | } | |
1273 | ||
f439236f MY |
1274 | static bool op2_re_unmatch(struct node *left, struct node *right, |
1275 | struct parameter *params, struct libscols_line *ln) | |
1276 | { | |
1277 | return !op2_re_match(left, right, params, ln); | |
1278 | } | |
1279 | ||
9b6ad16f | 1280 | static bool op2_check_type_boolean_or_op(struct parser* parser, const struct op2_class *op2_class, |
23a4b2b2 | 1281 | struct node *left, struct node *right) |
846b4cbe MY |
1282 | { |
1283 | enum node_type lt = left->type, rt = right->type; | |
1284 | ||
23a4b2b2 | 1285 | if (!(lt == NODE_OP1 || lt == NODE_OP2 || lt == NODE_BOOL)) { |
91a484fe | 1286 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
23a4b2b2 MY |
1287 | _("error: unexpected left operand type %s for: %s"), |
1288 | NODE_CLASS(left)->name, | |
1289 | op2_class->name); | |
1290 | return false; | |
1291 | } | |
846b4cbe | 1292 | |
23a4b2b2 | 1293 | if (! (rt == NODE_OP1 || rt == NODE_OP2 || rt == NODE_BOOL)) { |
91a484fe | 1294 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
23a4b2b2 MY |
1295 | _("error: unexpected right operand type %s for: %s"), |
1296 | NODE_CLASS(right)->name, | |
1297 | op2_class->name); | |
1298 | return false; | |
1299 | } | |
1300 | ||
1301 | return true; | |
846b4cbe MY |
1302 | } |
1303 | ||
9b6ad16f | 1304 | static bool op2_check_type_eq_or_bool_or_op(struct parser* parser, const struct op2_class *op2_class, |
23a4b2b2 | 1305 | struct node *left, struct node *right) |
846b4cbe MY |
1306 | { |
1307 | enum node_type lt = left->type, rt = right->type; | |
1308 | ||
1309 | if (lt == rt) | |
1310 | return true; | |
1fa18564 MY |
1311 | else if ((lt == NODE_NUM && rt == NODE_FNUM) |
1312 | || (lt == NODE_FNUM && rt == NODE_NUM)) | |
1313 | return true; | |
846b4cbe | 1314 | |
23a4b2b2 | 1315 | return op2_check_type_boolean_or_op(parser, op2_class, left, right); |
846b4cbe MY |
1316 | } |
1317 | ||
9b6ad16f | 1318 | static bool op2_check_type_num(struct parser* parser, const struct op2_class *op2_class, |
23a4b2b2 | 1319 | struct node *left, struct node *right) |
846b4cbe | 1320 | { |
1fa18564 | 1321 | if (left->type != NODE_NUM && left->type != NODE_FNUM) { |
91a484fe | 1322 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
23a4b2b2 MY |
1323 | _("error: unexpected left operand type %s for: %s"), |
1324 | NODE_CLASS(left)->name, | |
1325 | op2_class->name); | |
1326 | return false; | |
1327 | } | |
1328 | ||
1fa18564 | 1329 | if (right->type != NODE_NUM && right->type != NODE_FNUM) { |
91a484fe | 1330 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
23a4b2b2 | 1331 | _("error: unexpected right operand type %s for: %s"), |
acc47228 | 1332 | NODE_CLASS(right)->name, |
23a4b2b2 MY |
1333 | op2_class->name); |
1334 | return false; | |
1335 | } | |
1336 | ||
1337 | return true; | |
846b4cbe MY |
1338 | } |
1339 | ||
9b6ad16f | 1340 | static bool op2_check_type_re(struct parser* parser, const struct op2_class *op2_class, |
abaf378c MY |
1341 | struct node *left, struct node *right) |
1342 | { | |
1343 | if (left->type != NODE_STR) { | |
91a484fe | 1344 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
abaf378c MY |
1345 | _("error: unexpected left operand type %s for: %s"), |
1346 | NODE_CLASS(left)->name, | |
1347 | op2_class->name); | |
1348 | return false; | |
1349 | } | |
1350 | ||
1351 | if (right->type != NODE_STR) { | |
91a484fe | 1352 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
abaf378c MY |
1353 | _("error: unexpected right operand type %s for: %s"), |
1354 | NODE_CLASS(right)->name, | |
1355 | op2_class->name); | |
1356 | return false; | |
1357 | } | |
1358 | if (PINDEX(right) >= 0) { | |
91a484fe | 1359 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
abaf378c MY |
1360 | _("error: string literal is expected as right operand for: %s"), |
1361 | op2_class->name); | |
1362 | return false; | |
1363 | } | |
1364 | ||
1365 | char *regex = VAL(right, str); | |
1366 | VAL(right, str) = NULL; | |
1367 | ||
1368 | int err = regcomp(&VAL(right, re), regex, REG_NOSUB | REG_EXTENDED); | |
1369 | if (err != 0) { | |
1370 | size_t size = regerror(err, &VAL(right, re), NULL, 0); | |
1371 | char *buf = xmalloc(size + 1); | |
1372 | ||
1373 | regerror(err, &VAL(right, re), buf, size); | |
1374 | ||
91a484fe | 1375 | snprintf(parser->errmsg, sizeof(parser->errmsg), |
abaf378c MY |
1376 | _("error: could not compile regular expression %s: %s"), |
1377 | regex, buf); | |
1378 | free(buf); | |
1379 | return false; | |
1380 | } | |
1381 | right->type = NODE_RE; | |
1382 | free(regex); | |
1383 | return true; | |
1384 | } | |
1385 | ||
846b4cbe MY |
1386 | struct lsfd_filter *lsfd_filter_new(const char *const expr, struct libscols_table *tb, |
1387 | int ncols, | |
1388 | int (*column_name_to_id)(const char *, void *), | |
1389 | struct libscols_column *(*add_column_by_id)(struct libscols_table *, int, void*), | |
1390 | void *data) | |
1391 | { | |
1392 | struct parser parser; | |
024ca9ba MY |
1393 | int i; |
1394 | struct node *node; | |
1395 | struct lsfd_filter *filter; | |
1396 | ||
846b4cbe MY |
1397 | parser_init(&parser, expr, tb, ncols, |
1398 | column_name_to_id, | |
1399 | add_column_by_id, | |
1400 | data); | |
1401 | ||
024ca9ba | 1402 | node = dparser_compile(&parser); |
91a484fe | 1403 | filter = xcalloc(1, sizeof(struct lsfd_filter)); |
846b4cbe | 1404 | |
846b4cbe | 1405 | if (GOT_ERROR(&parser)) { |
91a484fe | 1406 | xstrncpy(filter->errmsg, parser.errmsg, sizeof(filter->errmsg)); |
846b4cbe MY |
1407 | return filter; |
1408 | } | |
28c20d30 | 1409 | assert(node); |
846b4cbe MY |
1410 | if (parser.paren_level > 0) { |
1411 | node_free(node); | |
91a484fe | 1412 | xstrncpy(filter->errmsg, _("error: unbalanced parenthesis: ("), sizeof(filter->errmsg)); |
846b4cbe MY |
1413 | return filter; |
1414 | } | |
1415 | if (*parser.cursor != '\0') { | |
1416 | node_free(node); | |
91a484fe | 1417 | snprintf(filter->errmsg, sizeof(filter->errmsg), |
846b4cbe MY |
1418 | _("error: garbage at the end of expression: %s"), parser.cursor); |
1419 | return filter; | |
1420 | } | |
1421 | if (node->type == NODE_STR || node->type == NODE_NUM) { | |
1fa18564 | 1422 | /* FNUM like 3.14 is not considered as a bool expression. */ |
846b4cbe | 1423 | node_free(node); |
91a484fe | 1424 | snprintf(filter->errmsg, sizeof(filter->errmsg), |
846b4cbe MY |
1425 | _("error: bool expression is expected: %s"), expr); |
1426 | return filter; | |
1427 | } | |
1428 | ||
1429 | filter->table = tb; | |
1430 | scols_ref_table(filter->table); | |
1431 | filter->node = node; | |
1432 | filter->parameters = parser.parameters; | |
1433 | filter->nparams = ncols; | |
024ca9ba | 1434 | for (i = 0; i < filter->nparams; i++) { |
846b4cbe MY |
1435 | if (filter->parameters[i].cl) |
1436 | scols_ref_column(filter->parameters[i].cl); | |
1437 | } | |
1438 | return filter; | |
1439 | } | |
1440 | ||
1441 | const char *lsfd_filter_get_errmsg(struct lsfd_filter *filter) | |
1442 | { | |
1443 | if (GOT_ERROR(filter)) | |
1444 | return filter->errmsg; | |
1445 | ||
1446 | return NULL; | |
1447 | } | |
1448 | ||
1449 | void lsfd_filter_dump(struct lsfd_filter *filter, FILE *stream) | |
1450 | { | |
1451 | if (!filter) { | |
1452 | fputs("EMPTY\n", stream); | |
1453 | return; | |
1454 | } | |
1455 | ||
1456 | if (GOT_ERROR(filter)) { | |
1457 | fprintf(stream, "ERROR: %s\n", filter->errmsg); | |
1458 | return; | |
1459 | } | |
1460 | ||
1461 | node_dump(filter->node, filter->parameters, 0, stream); | |
1462 | } | |
1463 | ||
1464 | void lsfd_filter_free(struct lsfd_filter *filter) | |
1465 | { | |
024ca9ba MY |
1466 | int i; |
1467 | ||
846b4cbe MY |
1468 | if (!filter) |
1469 | return; | |
1470 | ||
1471 | if (!GOT_ERROR(filter)) { | |
024ca9ba | 1472 | for (i = 0; i < filter->nparams; i++) { |
846b4cbe MY |
1473 | if (filter->parameters[i].cl) |
1474 | scols_unref_column(filter->parameters[i].cl); | |
1475 | } | |
1476 | scols_unref_table(filter->table); | |
1477 | node_free(filter->node); | |
1478 | } | |
cdc778cf | 1479 | free(filter->parameters); |
846b4cbe MY |
1480 | free(filter); |
1481 | } | |
1482 | ||
1483 | bool lsfd_filter_apply(struct lsfd_filter *filter, struct libscols_line * ln) | |
1484 | { | |
024ca9ba MY |
1485 | int i; |
1486 | ||
846b4cbe MY |
1487 | if (!filter) |
1488 | return true; | |
1489 | ||
1490 | if (GOT_ERROR(filter)) | |
1491 | return false; | |
1492 | ||
564d954a | 1493 | for (i = 0; i < filter->nparams; i++) { |
846b4cbe | 1494 | filter->parameters[i].has_value = false; |
564d954a MY |
1495 | filter->parameters[i].floating_point_num = false; |
1496 | } | |
846b4cbe MY |
1497 | |
1498 | return node_apply(filter->node, filter->parameters, ln); | |
1499 | } |