]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsfd-filter.c
lsblk: rename sortdata to rawdata
[thirdparty/util-linux.git] / misc-utils / lsfd-filter.c
CommitLineData
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
31enum 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
47enum op1_type {
48 OP1_NOT,
49};
50
51enum 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
64struct 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
75struct token_class {
9b6ad16f 76 const char * const name;
846b4cbe
MY
77 void (*free)(struct token *);
78 void (*dump)(struct token *, FILE *);
79};
80
81struct 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
93struct 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
105enum 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
115struct node {
116 enum node_type type;
117};
118
119struct 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
127struct 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)
137struct 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
149struct node_op1 {
150 struct node base;
9b6ad16f 151 const struct op1_class *opclass;
846b4cbe
MY
152 struct node *arg;
153};
154
155struct node_op2 {
156 struct node base;
9b6ad16f 157 const struct op2_class *opclass;
846b4cbe
MY
158 struct node *args[2];
159};
160
161struct 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
167struct 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 */
178static struct node *node_val_new(enum node_type, int pindex);
179static void node_free (struct node *);
180static bool node_apply(struct node *, struct parameter *, struct libscols_line *);
181static void node_dump (struct node *, struct parameter *, int, FILE *);
182
183static struct token *token_new (void);
184static void token_free(struct token *);
ae6ccf51 185#ifdef DEBUG
846b4cbe 186static void token_dump(struct token *, FILE *);
ae6ccf51 187#endif /* DEBUG */
846b4cbe
MY
188
189static void token_free_str(struct token *);
190
191static void token_dump_str(struct token *, FILE *);
192static void token_dump_num(struct token *, FILE *);
1fa18564 193static void token_dump_fnum(struct token *, FILE *);
846b4cbe
MY
194static void token_dump_op1(struct token *, FILE *);
195static void token_dump_op2(struct token *, FILE *);
196
197static bool op1_not(struct node *, struct parameter*, struct libscols_line *);
9b6ad16f 198static bool op1_check_type_bool_or_op(struct parser *, const struct op1_class *, struct node *);
846b4cbe
MY
199
200static bool op2_eq (struct node *, struct node *, struct parameter*, struct libscols_line *);
201static bool op2_ne (struct node *, struct node *, struct parameter*, struct libscols_line *);
202static bool op2_and(struct node *, struct node *, struct parameter*, struct libscols_line *);
203static bool op2_or (struct node *, struct node *, struct parameter*, struct libscols_line *);
204static bool op2_lt (struct node *, struct node *, struct parameter*, struct libscols_line *);
205static bool op2_le (struct node *, struct node *, struct parameter*, struct libscols_line *);
206static bool op2_gt (struct node *, struct node *, struct parameter*, struct libscols_line *);
207static bool op2_ge (struct node *, struct node *, struct parameter*, struct libscols_line *);
abaf378c 208static bool op2_re_match (struct node *, struct node *, struct parameter*, struct libscols_line *);
f439236f 209static bool op2_re_unmatch (struct node *, struct node *, struct parameter*, struct libscols_line *);
abaf378c 210
9b6ad16f
TW
211static bool op2_check_type_eq_or_bool_or_op(struct parser *, const struct op2_class *, struct node *, struct node *);
212static bool op2_check_type_boolean_or_op (struct parser *, const struct op2_class *, struct node *, struct node *);
213static bool op2_check_type_num (struct parser *, const struct op2_class *, struct node *, struct node *);
214static bool op2_check_type_re (struct parser *, const struct op2_class *, struct node *, struct node *);
846b4cbe
MY
215
216static void node_str_free(struct node *);
abaf378c 217static void node_re_free (struct node *);
846b4cbe
MY
218static void node_op1_free(struct node *);
219static void node_op2_free(struct node *);
220
221static void node_str_dump (struct node *, struct parameter*, int, FILE *);
222static void node_num_dump (struct node *, struct parameter*, int, FILE *);
1fa18564 223static void node_fnum_dump (struct node *, struct parameter*, int, FILE *);
846b4cbe 224static void node_bool_dump(struct node *, struct parameter*, int, FILE *);
abaf378c 225static void node_re_dump (struct node *, struct parameter*, int, FILE *);
846b4cbe
MY
226static void node_op1_dump (struct node *, struct parameter*, int, FILE *);
227static void node_op2_dump (struct node *, struct parameter*, int, FILE *);
228
229static struct node *dparser_compile(struct parser *);
230
231/*
232 * Data
233 */
234#define TOKEN_CLASS(TOKEN) (&token_classes[(TOKEN)->type])
9b6ad16f 235static 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 280static 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 289static 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 343static 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 */
381static int strputc(char **a, const char b)
382{
383 return strappend(a, (char [2]){b, '\0'});
384}
385
386static 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
393static 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
410static char parser_getc(struct parser *parser)
411{
412 char c = *parser->cursor;
413 if (c != '\0')
414 parser->cursor++;
415 return c;
416}
417
418static void parser_ungetc(struct parser *parser, char c)
419{
420 assert(parser->cursor > parser->expr);
421 if (c != '\0')
422 parser->cursor--;
423}
424
425static 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
467static 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
482static 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
526static 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
690static 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
697static 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
895static 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
917static struct token *token_new(void)
918{
919 return xcalloc(1, sizeof(struct token));
920}
921
922static 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
930static 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
939static void token_free_str(struct token *token)
940{
941 free(token->val.str);
942}
943
944static void token_dump_str(struct token *token, FILE *stream)
945{
946 fputs(token->val.str, stream);
947}
948
949static void token_dump_num(struct token *token, FILE *stream)
950{
951 fprintf(stream, "%llu", token->val.num);
952}
953
1fa18564
MY
954static void token_dump_fnum(struct token *token, FILE *stream)
955{
956 fprintf(stream, "%Lf", token->val.fnum);
957}
958
846b4cbe
MY
959static void token_dump_op1(struct token *token, FILE *stream)
960{
961 fputs(TOKEN_OP1_CLASS(token)->name, stream);
962}
963
964static void token_dump_op2(struct token *token, FILE *stream)
965{
966 fputs(TOKEN_OP2_CLASS(token)->name, stream);
967}
968
969static 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
977static 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
986static 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
1020static 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
1034static 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
1042static 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
1050static 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
1058static 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
1069static 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
1075static 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
1081static 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
1090static void node_str_free(struct node *node)
1091{
1092 if (PINDEX(node) < 0)
1093 free(VAL(node,str));
1094}
1095
abaf378c
MY
1096static void node_re_free(struct node *node)
1097{
1098 regfree(&VAL(node,re));
1099}
1100
846b4cbe
MY
1101static void node_op1_free(struct node *node)
1102{
1103 node_free(((struct node_op1 *)node)->arg);
1104}
1105
1106static 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
1114static bool op1_not(struct node *node, struct parameter* params, struct libscols_line * ln)
1115{
1116 return !node_apply(node, params, ln);
1117}
1118
9b6ad16f 1119static 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
1147struct 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
1225static bool op2_eq(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln)
1226{
1227 OP2_EQ_BODY(==, false);
1228}
1229
1230static bool op2_ne(struct node *left, struct node *right, struct parameter *params, struct libscols_line *ln)
1231{
1232 OP2_EQ_BODY(!=, true);
1233}
1234
1235static 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
1240static 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
1245static 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
1250static 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
1255static 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
1260static 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
1265static 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
1274static 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 1280static 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 1304static 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 1318static 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 1340static 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
1386struct 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
1441const 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
1449void 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
1464void 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
1483bool 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}