1 /* recsel.c - Record selection
2 * Copyright (C) 2014, 2016 Werner Koch
4 * This file is part of GnuPG.
6 * This file is free software; you can redistribute it and/or modify
7 * it under the terms of either
9 * - the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 3 of the License, or (at
11 * your option) any later version.
15 * - the GNU General Public License as published by the Free
16 * Software Foundation; either version 2 of the License, or (at
17 * your option) any later version.
19 * or both in parallel, as here.
21 * This file is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, see <https://www.gnu.org/licenses/>.
40 /* Select operators. */
47 SELECT_EQ, /* Numerically equal. */
52 SELECT_STRLE, /* String is less or equal. */
59 /* Definition for a select expression. */
63 select_op_t op; /* Operation code. */
64 unsigned int not:1; /* Negate operators. */
65 unsigned int disjun:1;/* Start of a disjunction. */
66 unsigned int xcase:1; /* String match is case sensitive. */
67 const char *value; /* (Points into NAME.) */
68 long numvalue; /* strtol of VALUE. */
69 char name[1]; /* Name of the property. */
74 static inline gpg_error_t
75 my_error_from_syserror (void)
77 return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
81 static inline gpg_error_t
82 my_error (gpg_err_code_t ec)
84 return gpg_err_make (default_errsource, ec);
88 /* This is a case-sensitive version of our memistr. I wonder why no
89 * standard function memstr exists but I better do not use the name
90 * memstr to avoid future conflicts.
92 * FIXME: Move this to a stringhelp.c
95 my_memstr (const void *buffer, size_t buflen, const char *sub)
97 const unsigned char *buf = buffer;
98 const unsigned char *t = (const unsigned char *)buf;
99 const unsigned char *s = (const unsigned char *)sub;
102 for ( ; n ; t++, n-- )
106 for (buf = t++, buflen = n--, s++; n && *t ==*s; t++, s++, n--)
109 return (const char*)buf;
110 t = (const unsigned char *)buf;
111 s = (const unsigned char *)sub ;
119 /* Return a pointer to the next logical connection operator or NULL if
122 find_next_lc (char *string)
126 p1 = strchr (string, '&');
127 if (p1 && p1[1] != '&')
129 p2 = strchr (string, '|');
130 if (p2 && p2[1] != '|')
136 return p1 < p2 ? p1 : p2;
140 /* Parse an expression. The expression syntax is:
142 * [<lc>] {{<flag>} PROPNAME <op> VALUE [<lc>]}
144 * A [] indicates an optional part, a {} a repetition. PROPNAME and
145 * VALUE may not be the empty string. White space between the
146 * elements is ignored. Numerical values are computed as long int;
147 * standard C notation applies. <lc> is the logical connection
148 * operator; either "&&" for a conjunction or "||" for a disjunction.
149 * A conjunction is assumed at the begin of an expression and
150 * conjunctions have higher precedence than disjunctions. If VALUE
151 * starts with one of the characters used in any <op> a space after
152 * the <op> is required. A VALUE is terminated by an <lc> unless the
153 * "--" <flag> is used in which case the VALUE spans to the end of the
154 * expression. <op> may be any of
156 * =~ Substring must match
157 * !~ Substring must not match
158 * = The full string must match
159 * <> The full string must not match
160 * == The numerical value must match
161 * != The numerical value must not match
162 * <= The numerical value of the field must be LE than the value.
163 * < The numerical value of the field must be LT than the value.
164 * >= The numerical value of the field must be GT than the value.
165 * >= The numerical value of the field must be GE than the value.
166 * -n True if value is not empty (no VALUE parameter allowed).
167 * -z True if value is empty (no VALUE parameter allowed).
168 * -t Alias for "PROPNAME != 0" (no VALUE parameter allowed).
169 * -f Alias for "PROPNAME == 0" (no VALUE parameter allowed).
171 * Values for <flag> must be space separated and any of:
173 * -- VALUE spans to the end of the expression.
174 * -c The string match in this part is done case-sensitive.
176 * For example four calls to recsel_parse_expr() with these values for
184 * or the equivalent expression
186 * "uid =~ Alfa" && uid !~ Test" || uid =~ Alpha" && "uid !~ Test"
188 * are making a selector for records where the "uid" property contains
189 * the strings "Alfa" or "Alpha" but not the String "test".
191 * The caller must pass the address of a selector variable to this
192 * function and initialize the value of the function to NULL before
193 * the first call. recset_release needs to be called to free the
197 recsel_parse_expr (recsel_expr_t *selector, const char *expression)
199 recsel_expr_t se_head = NULL;
200 recsel_expr_t se, se2;
207 char *next_lc = NULL;
209 while (*expression == ' ' || *expression == '\t')
212 expr_buffer = xtrystrdup (expression);
214 return my_error_from_syserror ();
217 if (*expr == '|' && expr[1] == '|')
222 else if (*expr == '&' && expr[1] == '&')
226 while (*expr == ' ' || *expr == '\t')
233 case '-': toend = 1; break;
234 case 'c': xcase = 1; break;
236 log_error ("invalid flag '-%c' in expression\n", *expr);
237 recsel_release (se_head);
239 return my_error (GPG_ERR_INV_FLAG);
242 while (*expr == ' ' || *expr == '\t')
246 next_lc = toend? NULL : find_next_lc (expr);
248 *next_lc = 0; /* Terminate this term. */
250 se = xtrymalloc (sizeof *se + strlen (expr));
252 return my_error_from_syserror ();
253 strcpy (se->name, expr);
263 for (se2 = se_head; se2->next; se2 = se2->next)
269 s = strpbrk (expr, "=<>!~-");
270 if (!s || s == expr )
272 log_error ("no field name given in expression\n");
273 recsel_release (se_head);
275 return my_error (GPG_ERR_NO_NAME);
279 if (!strncmp (s, "=~", 2))
284 else if (!strncmp (s, "!~", 2))
290 else if (!strncmp (s, "<>", 2))
292 se->op = SELECT_SAME;
296 else if (!strncmp (s, "==", 2))
301 else if (!strncmp (s, "!=", 2))
307 else if (!strncmp (s, "<=", 2))
312 else if (!strncmp (s, ">=", 2))
317 else if (!strncmp (s, "<", 1))
322 else if (!strncmp (s, ">", 1))
327 else if (!strncmp (s, "=", 1))
329 se->op = SELECT_SAME;
332 else if (!strncmp (s, "-z", 2))
334 se->op = SELECT_NONEMPTY;
338 else if (!strncmp (s, "-n", 2))
340 se->op = SELECT_NONEMPTY;
343 else if (!strncmp (s, "-f", 2))
345 se->op = SELECT_ISTRUE;
349 else if (!strncmp (s, "-t", 2))
351 se->op = SELECT_ISTRUE;
354 else if (!strncmp (s, "-le", 3))
356 se->op = SELECT_STRLE;
359 else if (!strncmp (s, "-ge", 3))
361 se->op = SELECT_STRGE;
364 else if (!strncmp (s, "-lt", 3))
366 se->op = SELECT_STRLT;
369 else if (!strncmp (s, "-gt", 3))
371 se->op = SELECT_STRGT;
376 log_error ("invalid operator in expression\n");
377 recsel_release (se_head);
379 return my_error (GPG_ERR_INV_OP);
382 /* We require that a space is used if the value starts with any of
383 the operator characters. */
384 if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
386 else if (strchr ("=<>!~", *s))
388 log_error ("invalid operator in expression\n");
389 recsel_release (se_head);
391 return my_error (GPG_ERR_INV_OP);
394 while (*s == ' ' || *s == '\t')
397 if (se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE)
401 log_error ("value given for -n or -z\n");
402 recsel_release (se_head);
404 return my_error (GPG_ERR_SYNTAX);
411 log_error ("no value given in expression\n");
412 recsel_release (se_head);
414 return my_error (GPG_ERR_MISSING_VALUE);
418 se->name[s0 - expr] = 0;
419 trim_spaces (se->name);
422 log_error ("no field name given in expression\n");
423 recsel_release (se_head);
425 return my_error (GPG_ERR_NO_NAME);
428 trim_spaces (se->name + (s - expr));
429 se->value = se->name + (s - expr);
430 if (!se->value[0] && !(se->op == SELECT_NONEMPTY || se->op == SELECT_ISTRUE))
432 log_error ("no value given in expression\n");
433 recsel_release (se_head);
435 return my_error (GPG_ERR_MISSING_VALUE);
438 se->numvalue = strtol (se->value, NULL, 0);
442 disjun = next_lc[1] == '|';
447 /* Read:y Append to passes last selector. */
452 for (se2 = *selector; se2->next; se2 = se2->next)
463 recsel_release (recsel_expr_t a)
467 recsel_expr_t tmp = a->next;
475 recsel_dump (recsel_expr_t selector)
479 log_debug ("--- Begin selectors ---\n");
480 for (se = selector; se; se = se->next)
482 log_debug ("%s %s %s %s '%s'\n",
483 se==selector? " ": (se->disjun? "||":"&&"),
486 se->op == SELECT_SAME? (se->not? "<>":"= "):
487 se->op == SELECT_SUB? (se->not? "!~":"=~"):
488 se->op == SELECT_NONEMPTY?(se->not? "-z":"-n"):
489 se->op == SELECT_ISTRUE? (se->not? "-f":"-t"):
490 se->op == SELECT_EQ? (se->not? "!=":"=="):
491 se->op == SELECT_LT? "< ":
492 se->op == SELECT_LE? "<=":
493 se->op == SELECT_GT? "> ":
494 se->op == SELECT_GE? ">=":
495 se->op == SELECT_STRLT? "-lt":
496 se->op == SELECT_STRLE? "-le":
497 se->op == SELECT_STRGT? "-gt":
498 se->op == SELECT_STRGE? "-ge":
502 log_debug ("--- End selectors ---\n");
506 /* Return true if the record RECORD has been selected. The GETVAL
507 * function is called with COOKIE and the NAME of a property used in
510 recsel_select (recsel_expr_t selector,
511 const char *(*getval)(void *cookie, const char *propname),
516 size_t selen, valuelen;
523 value = getval? getval (cookie, se->name) : NULL;
529 /* Field is empty. */
532 else /* Field has a value. */
534 valuelen = strlen (value);
535 numvalue = strtol (value, NULL, 0);
536 selen = strlen (se->value);
542 result = (valuelen==selen && !memcmp (value,se->value,selen));
544 result = (valuelen==selen && !memicmp (value,se->value,selen));
548 result = !!my_memstr (value, valuelen, se->value);
550 result = !!memistr (value, valuelen, se->value);
552 case SELECT_NONEMPTY:
559 result = (numvalue == se->numvalue);
562 result = (numvalue > se->numvalue);
565 result = (numvalue >= se->numvalue);
568 result = (numvalue < se->numvalue);
571 result = (numvalue <= se->numvalue);
575 result = strcmp (value, se->value) > 0;
577 result = strcasecmp (value, se->value) > 0;
581 result = strcmp (value, se->value) >= 0;
583 result = strcasecmp (value, se->value) >= 0;
587 result = strcmp (value, se->value) < 0;
589 result = strcasecmp (value, se->value) < 0;
593 result = strcmp (value, se->value) <= 0;
595 result = strcasecmp (value, se->value) <= 0;
605 /* This expression evaluated to true. See wether there are
606 remaining expressions in this conjunction. */
607 if (!se->next || se->next->disjun)
608 break; /* All expressions are true. Return True. */
609 se = se->next; /* Test the next. */
613 /* This expression evaluated to false and thus the
614 * conjunction evaluates to false. We skip over the
615 * remaining expressions of this conjunction and continue
616 * with the next disjunction if any. */
619 while (se && !se->disjun);