1 /* Query handling code.
2 Copyright (C) 1994, 95, 96, 1997, 1999, 2000 Free Software Foundation, Inc.
3 Contributed by Brendan Kehoe (brendan@cygnus.com).
4 Massively revised by Bob Manson (manson@juniper.net).
5
6 This file is part of GNU GNATS.
7
8 GNU GNATS is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 GNU GNATS is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with GNU GNATS; see the file COPYING. If not, write to the Free
20 Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
21
22 #include "gnats.h"
23 #include "query.h"
24 #include "pcodes.h"
25
26 /* One item to search for. */
27
28 typedef struct query_item
29 {
30 ComplexFieldIndex fieldIndex;
31 struct re_pattern_buffer *compRegexp;
32 } *QueryItem;
33
34 static QueryItem freeQueryItemEnt = NULL;
35
36 static QueryItem
newQueryItem(ComplexFieldIndex index)37 newQueryItem (ComplexFieldIndex index)
38 {
39 QueryItem res;
40
41 if (freeQueryItemEnt != NULL)
42 {
43 res = freeQueryItemEnt;
44 freeQueryItemEnt = NULL;
45 }
46 else
47 {
48 res = (QueryItem) xmalloc (sizeof (struct query_item));
49 }
50 res->fieldIndex = index;
51 if (isConstantFieldValue (res->fieldIndex))
52 {
53 res->compRegexp =
54 (struct re_pattern_buffer *)
55 xmalloc (sizeof (struct re_pattern_buffer));
56 memset (res->compRegexp, 0, sizeof (struct re_pattern_buffer));
57 }
58 else
59 {
60 res->compRegexp = NULL;
61 }
62 return res;
63 }
64
65 static void
freeQueryItem(QueryItem i)66 freeQueryItem (QueryItem i)
67 {
68 if (i != NULL)
69 {
70 freeComplexFieldIndex (i->fieldIndex);
71 if (i->compRegexp != NULL)
72 {
73 /* XXX ??? !!! Hack 'cause the translate buffer is a static array */
74 i->compRegexp->translate = NULL;
75 regfree (i->compRegexp);
76 free (i->compRegexp);
77 }
78 if (freeQueryItemEnt == NULL)
79 {
80 freeQueryItemEnt = i;
81 }
82 else
83 {
84 free (i);
85 }
86 }
87 }
88
89 typedef struct search_item
90 {
91 /* The type of search to perform, of course. */
92 SearchType searchType;
93
94 /* The items on the left and right-hand-sides. */
95 QueryItem lhs;
96 QueryItem rhs;
97 } SearchItem;
98
99 typedef struct queryTree
100 {
101 /* The operation to perform on this node. */
102 QueryOp op;
103
104 /* The left and right sides of the expression; if this is a unary op,
105 the left side contains the expression. */
106 struct queryTree *left, *right;
107
108 /* If this is a QueryMatch expression, the actual query to perform. */
109 SearchItem ent;
110 } *QueryTree;
111
112 struct queryExpr
113 {
114 /* Database that this query is associated with. */
115 DatabaseInfo database;
116 /* The actual query tree. */
117 QueryTree tree;
118 };
119
120 struct SearchTypes {
121 const char *name;
122 const char *operator;
123 SearchType searchType;
124 } searchTypes[] = {
125 /* Order here does matter--we need to have == before =. (Bleah, yeah). */
126 { "LessThan", "<", LessThan },
127 { "GreaterThan", ">", GreaterThan },
128 { "Equals", "==", Equals },
129 { "NotEquals", "!=", NotEquals },
130 { "RegCmp", "=", RegCmp },
131 { "RegFind", "~", RegFind },
132 { "DefaultSearchType", "^", DefaultSearchType },
133 { NULL, NULL, InvalidSearchType }
134 };
135
136 /* Adds query format Q to the list of query formats. */
137 void
addQueryFormat(DatabaseInfo database,QueryFormat * q)138 addQueryFormat (DatabaseInfo database, QueryFormat *q)
139 {
140 QueryFormat *list = getQueryFormatList (database);
141
142 q->next = list;
143 setQueryFormatList (database, q);
144 }
145
146 static QueryFormat *
parseQueryFormat(const DatabaseInfo database,const char * expr,ErrorDesc * err)147 parseQueryFormat (const DatabaseInfo database, const char *expr,
148 ErrorDesc *err)
149 {
150 const char *sstart;
151 char *fstring = NULL;
152 QueryFormat *res;
153 FieldList currEnt = NULL;
154
155 while (expr[0] != '\0' && isspace ((int)(unsigned char) expr[0]))
156 {
157 expr++;
158 }
159 if (expr[0] == '\0')
160 {
161 return NULL;
162 }
163 if (expr[0] == '"')
164 {
165 expr++;
166 sstart = expr;
167
168 while (expr[0] != '\0' && expr[0] != '\"')
169 {
170 if (expr[0] == '\\' && expr[1] != '\0')
171 {
172 expr++;
173 }
174 expr++;
175 }
176
177 fstring = xmalloc (expr - sstart + 1);
178 memcpy (fstring, sstart, expr - sstart);
179 fstring[expr - sstart] = '\0';
180 if (expr[0] == '"')
181 {
182 expr++;
183 }
184 }
185 res = (QueryFormat *) xmalloc (sizeof (QueryFormat));
186 res->name = NULL;
187 res->printf = fstring;
188 res->separator = xstrdup ("\n");
189 res->fields = NULL;
190 while (expr[0] != '\0')
191 {
192 const char *nstart = expr;
193 FieldList newEnt;
194
195 if (isspace ((int)(unsigned char) expr[0]))
196 {
197 expr++;
198 continue;
199 }
200 while ((! isspace ((int)(unsigned char) expr[0])) && expr[0] != '\0')
201 {
202 expr++;
203 }
204 fstring = xmalloc (expr - nstart + 1);
205 memcpy (fstring, nstart, expr - nstart);
206 fstring[expr - nstart] = '\0';
207 newEnt = newFieldListEnt (database, fstring, NULL);
208 if (currEnt == NULL)
209 {
210 res->fields = newEnt;
211 }
212 else
213 {
214 currEnt->next = newEnt;
215 }
216 currEnt = newEnt;
217 if (parseComplexFieldIndex (currEnt->ent) != 0)
218 {
219 setError (err, CODE_INVALID_FIELD_NAME,
220 "Invalid field name or query format %s", fstring);
221 freeQueryFormat (res);
222 res = NULL;
223 break;
224 }
225 }
226 return res;
227 }
228
229 /* Find the first query format with name NAME; returns the query format,
230 or NULL if one was not found. */
231 QueryFormat *
findQueryFormat(const DatabaseInfo database,const char * name,ErrorDesc * err)232 findQueryFormat (const DatabaseInfo database, const char *name, ErrorDesc *err)
233 {
234 QueryFormat *q = getQueryFormatList (database);
235
236 while (q != NULL)
237 {
238 if (strcmp (q->name, name) == 0)
239 {
240 return q;
241 }
242 q = q->next;
243 }
244 /* It might be a format expression. */
245 return parseQueryFormat (database, name, err);
246 }
247
248 /* Return a non-zero value if P is composed entirely of digits. */
249 static int
numeric(const char * p)250 numeric (const char *p)
251 {
252 while (*p != '\0')
253 {
254 if (!isdigit ((int) *p))
255 return 0;
256 p++;
257 }
258 return 1;
259 }
260
261
262 /* Convert STRING into a time_t. It may either be a Unix time value
263 (seconds since Jan 1, 1970) or one of the other standard date
264 formats accepted by GNATS. */
265 time_t
get_any_date(const char * string)266 get_any_date (const char *string)
267 {
268 if (string == NULL)
269 {
270 return -1;
271 }
272 else if (numeric (string))
273 {
274 return atoi (string);
275 }
276 else
277 {
278 return get_date ((char *) string, NULL);
279 }
280 }
281
282 /* Return the numeric equivalent of this state; 0 if not available. */
283 int
enum_numeric(const char * text,FieldIndex field)284 enum_numeric (const char *text, FieldIndex field)
285 {
286 if ((text != NULL) && (text[0] != '\0'))
287 {
288 StringList *values;
289 int count = 1;
290
291 for (values = fieldDefForIndex (field)->enumValues;
292 values != NULL;
293 values = values->next)
294 {
295 if (strcasecmp (values->name, text) == 0)
296 {
297 return count;
298 }
299 count++;
300 }
301 }
302
303 return 0;
304 }
305
306 static char *
sql_time(const char * s)307 sql_time (const char *s)
308 {
309 time_t t;
310 struct tm *ar_time;
311 /* Note: we deliberately use 21 here, since that is what the width of
312 this time string will end up looking like. In the case where we
313 use things relative to timezone and such, it'd grow---but not here. */
314 char *buf = (char *) xmalloc (21);
315
316 t = get_any_date (s);
317 ar_time = (struct tm *) localtime (&t);
318 strftime (buf, 21, "%Y-%m-%d %H:%M:%S", ar_time);
319
320 return buf;
321 }
322
323 static char case_fold[256];
324
325 /* Return 0 if VALUE matched the regexp in PAT, 1 otherwise. */
326 int
gnats_regcmp(const char * pat,const char * value,struct re_pattern_buffer * barg)327 gnats_regcmp (const char *pat, const char *value,
328 struct re_pattern_buffer *barg)
329 {
330 struct re_pattern_buffer buf;
331 struct re_pattern_buffer *bufPtr;
332 union {
333 const char *c;
334 int i;
335 } r;
336
337 if (barg == NULL)
338 {
339 bufPtr = &buf;
340 memset ((void *) &buf, 0, sizeof (buf));
341 }
342 else
343 {
344 bufPtr = barg;
345 }
346
347 if (case_fold[1] == 0)
348 {
349 int i;
350 for (i = 0; i < 256; i++)
351 {
352 case_fold[i] = tolower (i);
353 }
354 }
355 if (bufPtr->translate == NULL)
356 {
357 bufPtr->translate = case_fold;
358 bufPtr->fastmap = xmalloc (256);
359 #ifdef USE_RX
360 bufPtr->syntax_parens = (char *)1;
361 #endif
362
363 r.c = re_compile_pattern (pat, strlen (pat), bufPtr);
364 if (r.c)
365 {
366 fprintf (stderr, "%s: re_compile_pattern: %s\n", program_name, r.c);
367 /* XXX ??? !!! Wow. This makes real sense. :-( */
368 exit (2);
369 }
370 }
371 r.i = strlen (value);
372 r.i = re_match (bufPtr, value, strlen (value), 0, 0);
373 if (barg == NULL)
374 {
375 buf.translate = NULL;
376 regfree (&buf);
377 }
378 switch (r.i)
379 {
380 case -2:
381 fprintf (stderr,
382 "%s: warning: re_match died with pattern %s and string %s\n",
383 program_name, pat, value);
384 /*FALLTHRU*/
385 case -1:
386 return 1;
387 default:
388 return 0;
389 }
390 }
391
392 /* Return 0 if any portion of VALUE matches the regexp in PAT, 1 otherwise. */
393 int
regfind(const char * pat,const char * value,struct re_pattern_buffer * barg)394 regfind (const char *pat, const char *value, struct re_pattern_buffer *barg)
395 {
396 struct re_pattern_buffer buf;
397 struct re_pattern_buffer *bufPtr;
398 union {
399 const char *c;
400 int i;
401 } r;
402
403 if (barg == NULL)
404 {
405 bufPtr = &buf;
406 memset ((void *) &buf, 0, sizeof (buf));
407 }
408 else
409 {
410 bufPtr = barg;
411 }
412
413 if (case_fold[1] == 0)
414 {
415 int i;
416 for (i = 0; i < 256; i++)
417 {
418 case_fold[i] = tolower (i);
419 }
420 }
421 if (bufPtr->translate == NULL)
422 {
423 bufPtr->translate = case_fold;
424 bufPtr->fastmap = xmalloc (256);
425 #ifdef USE_RX
426 bufPtr->syntax_parens = (char *)1;
427 #endif
428
429 r.c = re_compile_pattern (pat, strlen (pat), bufPtr);
430 if (r.c)
431 {
432 fprintf (stderr, "%s: re_compile_pattern: %s\n", program_name, r.c);
433 /* XXX ??? !!! Wow. This makes real sense. :-( */
434 exit (2);
435 }
436 }
437 r.i = strlen (value);
438 r.i = re_search (bufPtr, value, r.i, 0, r.i, 0);
439 if (barg == NULL)
440 {
441 buf.translate = NULL;
442 regfree (&buf);
443 }
444 switch (r.i)
445 {
446 case -2:
447 fprintf (stderr,
448 "%s: warning: re_search died with pattern %s and string %s\n",
449 program_name, pat, value);
450 /*FALLTHRU*/
451 case -1:
452 return 1;
453 default:
454 return 0;
455 }
456 }
457
458 static int
intFieldCompare(QueryItem * fields,const char * lhs,const char * rhs)459 intFieldCompare (QueryItem *fields, const char *lhs, const char *rhs)
460 {
461 FieldType fieldType;
462 FieldIndex fieldIndex = simpleFieldIndexValue (fields[0]->fieldIndex);
463
464 if (fieldIndex != InvalidFieldIndex)
465 {
466 fieldType = fieldDefForIndex (fieldIndex)->datatype;
467 }
468 else
469 {
470 fieldType = Text;
471 }
472
473 switch (fieldType)
474 {
475 case Date:
476 {
477 time_t lv = (lhs[0] == '\0' ? 0 : get_any_date (lhs));
478 time_t rv = (rhs[0] == '\0' ? 0 : get_any_date (rhs));
479
480 if (lv == -1 || rv == -1)
481 {
482 return strcmp (lhs, rhs);
483 }
484 else if (lv < rv)
485 {
486 return -1;
487 }
488 else if (lv == rv)
489 {
490 return 0;
491 }
492 else
493 {
494 return 1;
495 }
496 }
497 break;
498 case Integer:
499 {
500 int lv = atoi (lhs);
501 int rv = atoi (rhs);
502 if (lv < rv)
503 {
504 return -1;
505 }
506 else if (lv == rv)
507 {
508 return 0;
509 }
510 else
511 {
512 return 1;
513 }
514 }
515 break;
516 case Enum:
517 {
518 int lv = enum_numeric (lhs, fieldIndex);
519 int rv = enum_numeric (rhs, fieldIndex);
520
521 if (lv == 0 || rv == 0)
522 {
523 return strcmp (lhs, rhs);
524 }
525 if (lv < rv)
526 {
527 return -1;
528 }
529 else if (lv == rv)
530 {
531 return 0;
532 }
533 else
534 {
535 return 1;
536 }
537 }
538 break;
539 default:
540 {
541 return strcmp (lhs, rhs);
542 }
543 break;
544 }
545 }
546
547 static int
fieldCompare(PR * pr,PR * oldPR,QueryItem * fields,SearchType searchType,FormatNamedParameter * params)548 fieldCompare (PR *pr, PR *oldPR, QueryItem *fields,
549 SearchType searchType, FormatNamedParameter *params)
550 {
551 struct localFieldInfo
552 {
553 const char *value;
554 FieldIndex index;
555 int mustBeFreed;
556 } vals[2];
557 int i;
558 int res = 0;
559 int x;
560
561 for (i = 0; i < 2; i++)
562 {
563 /* Not sure what to do with this mess. XXX ??? !!! FIXME */
564 if (parseComplexFieldIndex (fields[i]->fieldIndex) != 0)
565 {
566 return 0;
567 }
568 }
569
570 for (i = 0; i < 2; i++)
571 {
572 if (complexFieldType (fields[i]->fieldIndex) != InvalidFieldType)
573 {
574 int num_fields = get_num_fields (pr->database);
575 FieldType fieldType = complexFieldType (fields[i]->fieldIndex);
576
577 for (x = 0; x < num_fields; x++)
578 {
579 FieldIndex field = getNthField (pr->database, x);
580 if ((fieldType == Text && fieldDefForIndex (field)->textsearch)
581 || (fieldType != Text
582 && fieldDefForIndex (field)->datatype == fieldType))
583 {
584 ComplexFieldIndex ent = simpleComplexFieldIndex (field);
585 QueryItem newFields[2];
586 int res;
587
588 newFields[i] = newQueryItem (ent);
589 newFields[1 - i] = fields [1 - i];
590
591 res = fieldCompare (pr, oldPR, newFields, searchType,
592 params);
593
594 freeQueryItem (newFields[i]);
595 if (res)
596 {
597 return 1;
598 }
599 }
600 }
601 return 0;
602 }
603 }
604
605 for (x = 0; x < 2; x++)
606 {
607 vals[x].index = simpleFieldIndexValue (fields[x]->fieldIndex);
608 }
609
610 if (searchType == DefaultSearchType)
611 {
612 if (vals[0].index != InvalidFieldIndex)
613 {
614 searchType = fieldDefForIndex (vals[0].index)->defaultSearchType;
615 }
616 else
617 {
618 searchType = RegCmp;
619 }
620 }
621 if ((! PR_IS_FULL (pr)) && (! isIndexedField (fields[0]->fieldIndex)))
622 {
623 ErrorDesc err;
624
625 if (fillInPR (pr, &err) != 0)
626 {
627 return 0;
628 }
629 }
630
631 for (x = 0; x < 2; x++)
632 {
633 vals[x].value = get_field_value (pr, oldPR, fields[x]->fieldIndex,
634 params, &(vals[x].mustBeFreed));
635 }
636
637 if (vals[0].value == NULL || vals[1].value == NULL)
638 {
639 for (x = 0; x < 2; x++)
640 {
641 if (vals[x].mustBeFreed && vals[x].value != NULL)
642 {
643 free ((char *)vals[x].value);
644 }
645 }
646 return 0;
647 }
648
649 switch (searchType)
650 {
651 case DefaultSearchType:
652 /* Shouldn't get here */
653 abort ();
654 break;
655
656 case RegCmp:
657 res = (gnats_regcmp (vals[1].value, vals[0].value, fields[1]->compRegexp)
658 == 0);
659 break;
660
661 case RegFind:
662 res = (regfind (vals[1].value, vals[0].value, fields[1]->compRegexp) == 0);
663 break;
664
665 case LessThan:
666 res = (intFieldCompare (fields, vals[0].value, vals[1].value) < 0);
667 break;
668
669 case GreaterThan:
670 res = (intFieldCompare (fields, vals[0].value, vals[1].value) > 0);
671 break;
672
673 case Equals:
674 res = (intFieldCompare (fields, vals[0].value, vals[1].value) == 0);
675 break;
676
677 case NotEquals:
678 res = (intFieldCompare (fields, vals[0].value, vals[1].value) != 0);
679 break;
680
681 case StringMatch:
682 res = (strcmp (vals[0].value, vals[1].value) == 0);
683 break;
684
685 case NilSearch:
686 /* ??? XXX !!! Hmmmm. Return 1? */
687 res = 0;
688 break;
689
690 case InvalidSearchType:
691 abort ();
692 break;
693 }
694 for (x = 0; x < 2; x++)
695 {
696 if (vals[x].mustBeFreed)
697 {
698 free ((char *) vals[x].value);
699 }
700 }
701 return res;
702 }
703
704 static int
pr_match_field(PR * pr,PR * oldPR,SearchItem * item,FormatNamedParameter * params)705 pr_match_field (PR *pr, PR *oldPR, SearchItem *item,
706 FormatNamedParameter *params)
707 {
708 QueryItem fields[2];
709
710 if (pr == NULL)
711 {
712 return 0;
713 }
714
715 fields[0] = item->lhs;
716 fields[1] = item->rhs;
717
718 return fieldCompare (pr, oldPR, fields, item->searchType, params);
719 }
720
721 /* Returns a non-zero value if the PR referenced by I matches the expression
722 tree in QEXP. */
723
724 static int
pr_matches_tree(PR * pr,PR * oldPR,QueryTree qexp,FormatNamedParameter * params)725 pr_matches_tree (PR *pr, PR *oldPR, QueryTree qexp,
726 FormatNamedParameter *params)
727 {
728 if (qexp == NULL)
729 {
730 return 1;
731 }
732
733 switch (qexp->op)
734 {
735 case QueryMatch:
736 return pr_match_field (pr, oldPR, &qexp->ent, params);
737 break;
738 case QueryNot:
739 return ! pr_matches_tree (pr, oldPR, qexp->left, params);
740 break;
741 case QueryAnd:
742 return pr_matches_tree (pr, oldPR, qexp->left, params)
743 && pr_matches_tree (pr, oldPR, qexp->right, params);
744 break;
745 case QueryOr:
746 return pr_matches_tree (pr, oldPR, qexp->left, params)
747 || pr_matches_tree (pr, oldPR, qexp->right, params);
748 break;
749 default:
750 abort ();
751 }
752 return 0;
753 }
754
755 int
pr_matches_expr(PR * pr,PR * oldPR,QueryExpr qexp,FormatNamedParameter * params)756 pr_matches_expr (PR *pr, PR *oldPR, QueryExpr qexp,
757 FormatNamedParameter *params)
758 {
759 QueryTree tree = NULL;
760
761 if (qexp != NULL)
762 {
763 tree = qexp->tree;
764 }
765 return pr_matches_tree (pr, oldPR, tree, params);
766 }
767
768 void
append_string(char ** res,const char * string)769 append_string (char **res, const char *string)
770 {
771 if (*res != NULL)
772 {
773 size_t oldlen = strlen (*res);
774 size_t newlen = strlen (string);
775 *res = xrealloc (*res, oldlen + newlen + 1);
776 memcpy (*res + oldlen, string, newlen + 1);
777 }
778 else
779 {
780 *res = xstrdup (string);
781 }
782 }
783
784 /* mmmm, cheezy. */
785 static void
append_char(char ** res,char chr)786 append_char (char **res, char chr)
787 {
788 char buf[2];
789
790 buf[0] = chr;
791 buf[1] = '\0';
792 append_string (res, buf);
793 }
794
795 /* Prints the string CONTENTS using the printf format FORMAT to either
796 FP (if it is non-NULL), or appended to RES via append_string (). */
797
798 static void
do_print(FILE * fp,char ** res,const char * format,const char * contents)799 do_print (FILE *fp, char **res, const char *format, const char *contents)
800 {
801 size_t flen = strlen (format);
802 if (format[flen - 1] == 'd')
803 {
804 int val = atoi (contents);
805 if (fp != NULL)
806 {
807 fprintf (fp, format, val);
808 }
809 else
810 {
811 char *new;
812
813 asprintf (&new, format, val);
814 append_string (res, new);
815 free (new);
816 }
817 }
818 else
819 {
820 if (fp != NULL)
821 {
822 fprintf (fp, format, contents);
823 }
824 else
825 {
826 char *new;
827
828 asprintf (&new, format, contents);
829 append_string (res, new);
830 free (new);
831 }
832 }
833 }
834
835 static void
writeFullPR(FILE * fp,PR * pr,int rawQuery,const char * eolTerminator)836 writeFullPR (FILE *fp, PR *pr, int rawQuery, const char *eolTerminator)
837 {
838 int i;
839 int num_fields = get_num_fields (pr->database);
840
841 for (i = 0; i < num_fields; i++)
842 {
843 int mustBeFreed = 0;
844 const char *contents;
845 FieldIndex field = getNthField (pr->database, i);
846
847 if (! rawQuery)
848 {
849 ComplexFieldIndex fieldIndex = simpleComplexFieldIndex (field);
850 contents = get_field_value (pr, NULL, fieldIndex, NULL,
851 &mustBeFreed);
852 freeComplexFieldIndex (fieldIndex);
853 }
854 else
855 {
856 contents = field_value (pr, field);
857 }
858
859 write_pr_field (fp, field, contents, eolTerminator);
860 if (mustBeFreed)
861 {
862 free ((char *) contents);
863 }
864 }
865 }
866
867 static void
format_pr_field(FILE * fp,char ** res,PR * pr,PR * oldPR,FieldList * fieldPtr,const char * format,const char * eolTerminator,FormatNamedParameter * parameters,int rawQuery)868 format_pr_field (FILE *fp, char **res, PR *pr, PR *oldPR, FieldList *fieldPtr,
869 const char *format, const char *eolTerminator,
870 FormatNamedParameter *parameters, int rawQuery)
871 {
872 int flen = strlen (format);
873 char *fdup = xstrdup (format);
874 const char *p;
875
876 ComplexFieldIndex field = (*fieldPtr)->ent;
877
878 switch (format[flen - 1])
879 {
880 case 's':
881 {
882 int mustBeFreed = 0;
883 const char *contents =
884 get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
885
886 if (contents == NULL)
887 {
888 contents = "";
889 }
890
891 do_print (fp, res, fdup, contents);
892 if (mustBeFreed)
893 {
894 free ((char *) contents);
895 }
896 *fieldPtr = (*fieldPtr)->next;
897 }
898 break;
899
900 case 'S':
901 {
902 int mustBeFreed = 0;
903 const char *contents =
904 get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
905 char *temp;
906
907 if (contents == NULL)
908 {
909 contents = "";
910 }
911
912 for (p = contents; *p && *p != ' '; p++)
913 {
914 /* Empty */
915 }
916 fdup[flen - 1] = 's';
917 if (*p == ' ')
918 {
919 temp = xmalloc (p - contents + 1);
920 memcpy (temp, contents, p - contents);
921 temp[p - contents] = '\0';
922 fprintf (fp, fdup, temp);
923 free (temp);
924 }
925 else
926 {
927 fprintf (fp, fdup, contents);
928 }
929 if (mustBeFreed)
930 {
931 free ((char *) contents);
932 }
933 *fieldPtr = (*fieldPtr)->next;
934 }
935 break;
936
937 case 'F':
938 {
939 int mustBeFreed = 0;
940 const char *contents
941 = get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
942
943 write_pr_field (fp, simpleFieldIndexValue (field), contents,
944 eolTerminator);
945 if (mustBeFreed)
946 {
947 free ((char *) contents);
948 }
949 *fieldPtr = (*fieldPtr)->next;
950 break;
951 }
952
953 case 'd':
954 {
955 char buffer[21];
956 int mustBeFreed = 0;
957 const char *contents
958 = get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
959 FieldIndex fieldIndex = simpleFieldIndexValue (field);
960
961 if (contents == NULL)
962 {
963 contents = "";
964 }
965 switch (fieldDefForIndex (fieldIndex)->datatype)
966 {
967 case Enum:
968 sprintf (buffer, "%d", enum_numeric (contents, fieldIndex));
969 do_print (fp, res, format, buffer);
970 break;
971 case Date:
972 sprintf (buffer, "%d", (int) get_any_date (contents));
973 do_print (fp, res, format, buffer);
974 break;
975 case Integer:
976 sprintf (buffer, "%d", atoi (contents));
977 do_print (fp, res, format, buffer);
978 break;
979 default:
980 do_print (fp, res, format, "0");
981 break;
982 }
983 if (mustBeFreed)
984 {
985 free ((char *) contents);
986 }
987 *fieldPtr = (*fieldPtr)->next;
988 }
989 break;
990
991 case 'D':
992 {
993 FieldIndex fieldIndex = simpleFieldIndexValue (field);
994
995 if (fieldIndex != InvalidFieldIndex
996 && fieldDefForIndex (fieldIndex)->datatype == Date)
997 {
998 int mustBeFreed = 0;
999 const char *strftimeFormat = "%a %b %d %H:%M:%S %z %Y";
1000 char *fend = NULL;
1001 const char *contents
1002 = get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
1003 char *t = NULL;
1004 time_t time;
1005
1006 if (contents == NULL)
1007 {
1008 contents = "";
1009 }
1010 time = get_any_date (contents);
1011
1012 if (fdup[1] == '{')
1013 {
1014 fend = strchr (fdup + 2, '}');
1015 if (fend != NULL)
1016 {
1017 strftimeFormat = fdup + 2;
1018 *fend = '\0';
1019 }
1020 }
1021
1022 if (time == 0 || time == -1)
1023 {
1024 /* Silly, but I'm lazy. */
1025 t = xstrdup ("");
1026 }
1027 else
1028 {
1029 size_t resLen;
1030 size_t tlen = 0;
1031
1032 /* It isn't clear what strftime returns if there is a format
1033 error. So we're paranoid and limit the amount of data
1034 it can allocate. */
1035 do
1036 {
1037 tlen += 512;
1038 t = xrealloc (t, tlen);
1039 t[0] = '\0';
1040 resLen = gnats_strftime (t, tlen, strftimeFormat,
1041 localtime (&time));
1042 } while (resLen == 0 && tlen < 4096);
1043 }
1044 fdup[flen - 1] = 's';
1045 if (fend != NULL)
1046 {
1047 *fend = '%';
1048 }
1049 do_print (fp, res, fend != NULL ? fend : fdup, t);
1050 free (t);
1051 if (mustBeFreed)
1052 {
1053 free ((char *) contents);
1054 }
1055 }
1056 *fieldPtr = (*fieldPtr)->next;
1057 }
1058 break;
1059
1060 case 'Q':
1061 {
1062 FieldIndex fieldIndex = simpleFieldIndexValue (field);
1063
1064 if (fieldIndex != InvalidFieldIndex
1065 && fieldDefForIndex (fieldIndex)->datatype == Date)
1066 {
1067 int mustBeFreed = 0;
1068 const char *contents
1069 = get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
1070 long i;
1071
1072 if (contents == NULL)
1073 {
1074 contents = "";
1075 }
1076
1077 i = get_any_date (contents);
1078
1079 fdup[flen - 1] = 's';
1080 if (i != 0 && i != -1)
1081 {
1082 char *qd = sql_time (contents);
1083 do_print (fp, res, fdup, qd);
1084 free (qd);
1085 }
1086 else
1087 {
1088 do_print (fp, res, fdup, "");
1089 }
1090 if (mustBeFreed)
1091 {
1092 free ((char *) contents);
1093 }
1094 }
1095 *fieldPtr = (*fieldPtr)->next;
1096 }
1097 break;
1098
1099 case 'P':
1100 {
1101 writeFullPR (fp, pr, rawQuery, eolTerminator);
1102 break;
1103 }
1104 }
1105 free (fdup);
1106 }
1107
1108 static int
process_printf_format(FILE * fp,char ** res,PR * pr,PR * oldPR,const char * format,FieldList fields,const char * eolTerminator,FormatNamedParameter * parameters,int rawQuery)1109 process_printf_format (FILE *fp, char **res, PR *pr, PR *oldPR,
1110 const char *format, FieldList fields,
1111 const char *eolTerminator,
1112 FormatNamedParameter *parameters,
1113 int rawQuery)
1114 {
1115 static char fcopy[1024];
1116
1117 while (*format != '\0')
1118 {
1119 if (format[0] == '\\' && format[1] == 't')
1120 {
1121 if (fp != NULL)
1122 {
1123 fputc ('\t', fp);
1124 }
1125 else
1126 {
1127 append_char (res, '\t');
1128 }
1129 format++;
1130 }
1131 else if (format[0] == '\\' && format[1] == 'n')
1132 {
1133 if (fp != NULL)
1134 {
1135 fputs (eolTerminator, fp);
1136 }
1137 else
1138 {
1139 append_string (res, eolTerminator);
1140 }
1141 format++;
1142 }
1143 else if (format[0] == '%')
1144 {
1145 char *fptr = fcopy + 1;
1146
1147 fcopy[0] = *(format++);
1148 if (*format == '{')
1149 {
1150 while (*format != '}' && (fptr - fcopy) < 1022)
1151 {
1152 *(fptr++) = *format;
1153 format++;
1154 }
1155 if (*format == '}')
1156 {
1157 *(fptr++) = *format;
1158 format++;
1159 }
1160 }
1161 while ((isdigit ((int) *format) || *format == '-' || *format == '+'
1162 || *format == '.')
1163 && ((fptr - fcopy) < 1022))
1164 {
1165 *(fptr++) = *format;
1166 format++;
1167 }
1168 *(fptr++) = *format;
1169 *fptr = '\0';
1170 if (*format == '%')
1171 {
1172 fputs (format, fp);
1173 }
1174 else
1175 {
1176 if (fields == NULL)
1177 {
1178 return 0;
1179 }
1180
1181 format_pr_field (fp, res, pr, oldPR, &fields, fcopy,
1182 eolTerminator, parameters, rawQuery);
1183 }
1184 }
1185 else
1186 {
1187 if (fp != NULL)
1188 {
1189 fputc (*format, fp);
1190 }
1191 else
1192 {
1193 append_char (res, *format);
1194 }
1195 }
1196 format++;
1197 }
1198 return 1;
1199 }
1200
1201 int
process_format(FILE * fp,char ** res,PR * pr,PR * oldPR,QueryFormat * fmt,const char * eolTerminator,FormatNamedParameter * parameters)1202 process_format (FILE *fp, char **res, PR *pr, PR *oldPR, QueryFormat *fmt,
1203 const char *eolTerminator, FormatNamedParameter *parameters)
1204 {
1205 int do_open_pr = 0;
1206
1207 if (pr != NULL)
1208 {
1209 if (! PR_IS_FULL (pr))
1210 {
1211 FieldList p = fmt->fields;
1212
1213 if (p == NULL)
1214 {
1215 do_open_pr = 1;
1216 }
1217 else
1218 {
1219 while (p != NULL)
1220 {
1221 if (! isIndexedField (p->ent))
1222 {
1223 do_open_pr = 1;
1224 break;
1225 }
1226 p = p->next;
1227 }
1228 }
1229 }
1230
1231 if (do_open_pr)
1232 {
1233 ErrorDesc err;
1234
1235 if (fillInPR (pr, &err))
1236 {
1237 return 0;
1238 }
1239 }
1240 }
1241
1242 if (fmt->printf != NULL)
1243 {
1244 return process_printf_format (fp, res, pr, oldPR, fmt->printf,
1245 fmt->fields, eolTerminator, parameters,
1246 fmt->rawQuery);
1247 }
1248 else if (pr != NULL)
1249 {
1250 if (fmt->fields == NULL)
1251 {
1252 write_entire_header (fp, pr, eolTerminator);
1253 fprintf (fp, eolTerminator);
1254 writeFullPR (fp, pr, fmt->rawQuery, eolTerminator);
1255 }
1256 else
1257 {
1258 FieldList flist = fmt->fields;
1259
1260 while (flist != NULL)
1261 {
1262 if (fmt->separator == NULL)
1263 {
1264 const char *contents;
1265 int mustBeFreed = 0;
1266 FieldIndex fieldIndex = simpleFieldIndexValue (flist->ent);
1267
1268 if (fmt->rawQuery)
1269 {
1270 contents = field_value (pr, fieldIndex);
1271 }
1272 else
1273 {
1274 contents = get_field_value (pr, oldPR, flist->ent,
1275 parameters, &mustBeFreed);
1276 }
1277 write_pr_field (fp, fieldIndex, contents, eolTerminator);
1278 if (mustBeFreed)
1279 {
1280 free ((char *) contents);
1281 }
1282 flist = flist->next;
1283 }
1284 else
1285 {
1286 format_pr_field (fp, res, pr, oldPR, &flist, "%s",
1287 eolTerminator, parameters, fmt->rawQuery);
1288 }
1289 }
1290 }
1291 return 1;
1292 }
1293 else
1294 {
1295 return 0;
1296 }
1297 }
1298
1299 int
print_named_format_pr(FILE * fp,PR * pr,const char * fmtName,const char * eolTerminator,ErrorDesc * err)1300 print_named_format_pr (FILE *fp, PR *pr, const char *fmtName,
1301 const char *eolTerminator,
1302 ErrorDesc *err)
1303 {
1304 QueryFormat *fmt = findQueryFormat (pr->database, fmtName, err);
1305
1306 if (fmt == NULL)
1307 {
1308 return 0;
1309 }
1310 else
1311 {
1312 return process_format (fp, NULL, pr, NULL, fmt, eolTerminator, NULL);
1313 }
1314 }
1315
1316 int
print_pr(FILE * dest,PR * pr,QueryFormat * query_format,const char * eolTerminator)1317 print_pr (FILE *dest, PR *pr, QueryFormat *query_format,
1318 const char *eolTerminator)
1319 {
1320 return process_format (dest, NULL, pr, NULL, query_format, eolTerminator,
1321 NULL);
1322 }
1323
1324
1325 /* Iterate through a set of PRs. For those PRs that match the
1326 expression in EXP, invoke FUNC with the count of PRs that have
1327 matched so far, the PR* entry for the PR, and the supplied
1328 QUERY_FORMAT.
1329
1330 FUNC is invoked once more after all of the queries have been
1331 searched. The PR* pointer is NULL, and the count corresponds to
1332 the total # of PRs that matched.
1333
1334 If AC is 0 or AV is NULL, we iterate through all of the PRs in the
1335 index for the current database. Otherwise, only those PRs that
1336 match the PRIDs in AV[0], AV[1]. AV[2]...AV[AC-1] are tested. */
1337
1338 int
iterate_prs(const DatabaseInfo database,int ac,char ** av,QueryExpr exp,QueryFormat * query_format,QueryFunc func,ErrorDesc * err)1339 iterate_prs (const DatabaseInfo database, int ac, char **av, QueryExpr exp,
1340 QueryFormat *query_format, QueryFunc func, ErrorDesc *err)
1341 {
1342 int found = 0;
1343 QueryTree tree = NULL;
1344
1345 if (exp != NULL)
1346 {
1347 if (exp->database != database)
1348 {
1349 abort ();
1350 }
1351 tree = exp->tree;
1352 }
1353
1354 *err = NULL;
1355 if (ac == 0 || av == NULL)
1356 {
1357 PR *pr = getFirstPR (database, err);
1358
1359 if (*err != NULL)
1360 {
1361 return -1;
1362 }
1363 /* We weren't given a list of PRs to check, so we do the
1364 whole shooting match. */
1365 while (pr != NULL)
1366 {
1367 if (pr_matches_tree (pr, NULL, tree, NULL))
1368 {
1369 found++;
1370 func (found, pr, query_format);
1371 }
1372 free_pr_header (pr);
1373 free_pr_contents (pr);
1374 pr = getNextPR (pr);
1375 }
1376 }
1377 else
1378 {
1379 int cpr;
1380
1381 for (cpr = 0; cpr < ac; cpr++)
1382 {
1383 char *pat, *n;
1384 char *p = av[cpr];
1385 int plen;
1386 PR *pr;
1387 struct re_pattern_buffer buf;
1388
1389 memset (&buf, 0, sizeof (buf));
1390
1391 /* Remove the category */
1392 if ((n = (char *) strrchr (p, '/')) != NULL)
1393 {
1394 p = n + 1;
1395 }
1396
1397 plen = strlen (p);
1398 pat = (char *) xmalloc (plen + 3);
1399 strcpy (pat, p);
1400 strcpy (pat + plen, "\\'");
1401
1402 *err = NULL;
1403 pr = getFirstPR (database, err);
1404 if (*err != NULL)
1405 {
1406 return -1;
1407 }
1408
1409 while (pr != NULL)
1410 {
1411 if (gnats_regcmp (pat, field_value (pr, NUMBER (pr->database)),
1412 &buf) == 0)
1413 {
1414 if (pr_matches_expr (pr, NULL, exp, NULL))
1415 {
1416 found++;
1417 func (found, pr, query_format);
1418 }
1419 /* Only one PR will match this PR number, because it's
1420 not really a regexp */
1421 break;
1422 }
1423 free_pr_header (pr);
1424 free_pr_contents (pr);
1425 pr = getNextPR (pr);
1426 }
1427 buf.translate = NULL;
1428 regfree (&buf);
1429 free (pat);
1430 }
1431 }
1432
1433 func (found, NULL, 0);
1434
1435 return found;
1436 }
1437
1438 static QueryTree
newQueryTree(void)1439 newQueryTree (void)
1440 {
1441 QueryTree ent = (QueryTree) xmalloc (sizeof (struct queryTree));
1442 ent->left = NULL;
1443 ent->right = NULL;
1444 ent->ent.searchType = InvalidSearchType;
1445 ent->ent.lhs = NULL;
1446 ent->ent.rhs = NULL;
1447 ent->op = InvalidQueryOp;
1448 return ent;
1449 }
1450
1451 static QueryExpr
newQueryExpr(const DatabaseInfo database)1452 newQueryExpr (const DatabaseInfo database)
1453 {
1454 QueryExpr ent = (QueryExpr) xmalloc (sizeof (struct queryExpr));
1455 ent->database = database;
1456 ent->tree = newQueryTree ();
1457 return ent;
1458 }
1459
1460 static QueryExpr
queryField(const DatabaseInfo database,ComplexFieldIndex lhs,SearchType stype,ComplexFieldIndex rhs)1461 queryField (const DatabaseInfo database,
1462 ComplexFieldIndex lhs, SearchType stype, ComplexFieldIndex rhs)
1463 {
1464 QueryExpr ent = newQueryExpr (database);
1465 ent->tree->op = QueryMatch;
1466 ent->tree->ent.searchType = stype;
1467 ent->tree->ent.lhs = newQueryItem (lhs);
1468 ent->tree->ent.rhs = newQueryItem (rhs);
1469 return ent;
1470 }
1471
1472 static void
freeQueryTree(QueryTree tree)1473 freeQueryTree (QueryTree tree)
1474 {
1475 if (tree != NULL)
1476 {
1477 freeQueryTree (tree->left);
1478 freeQueryTree (tree->right);
1479 freeQueryItem (tree->ent.lhs);
1480 freeQueryItem (tree->ent.rhs);
1481 free (tree);
1482 }
1483 }
1484
1485 void
freeQueryExpr(QueryExpr query)1486 freeQueryExpr (QueryExpr query)
1487 {
1488 if (query != NULL)
1489 {
1490 freeQueryTree (query->tree);
1491 free (query);
1492 }
1493 }
1494
1495 /* Return the SearchType value for OPERATOR, which is a query
1496 expression operator. */
1497 static SearchType
getSearchTypeForOperator(const char * operator,size_t * len)1498 getSearchTypeForOperator (const char *operator, size_t *len)
1499 {
1500 int x;
1501
1502 for (x = 0; searchTypes[x].name != NULL; x++)
1503 {
1504 if (searchTypes[x].operator[0] == operator[0])
1505 {
1506 *len = strlen (searchTypes[x].operator);
1507 if (*len == 1
1508 || strncmp (searchTypes[x].operator, operator, *len) == 0)
1509 {
1510 break;
1511 }
1512 }
1513 }
1514 return searchTypes[x].searchType;
1515 }
1516
1517 /* Return the search operator for SearchType TYPE. */
1518 const char *
getSearchOperatorForType(SearchType type)1519 getSearchOperatorForType (SearchType type)
1520 {
1521 int x;
1522 for (x = 0; searchTypes[x].name != NULL; x++)
1523 {
1524 if (searchTypes[x].searchType == type)
1525 {
1526 return searchTypes[x].operator;
1527 }
1528 }
1529 return NULL;
1530 }
1531
1532
1533
1534 /* Find the matching parenthesis for the parenthesized expression between
1535 EXPR and EXPEND. Returns either a pointer to the matching parenthesis,
1536 or NULL on failure. */
1537
1538 static const char *
findMatchingParen(const char * expr,const char * expend)1539 findMatchingParen (const char *expr, const char *expend)
1540 {
1541 int pcnt = 0;
1542
1543 if (*expr != '(')
1544 {
1545 abort ();
1546 }
1547
1548 while (expr <= expend)
1549 {
1550 if (*expr == '(')
1551 {
1552 pcnt++;
1553 }
1554 else if (*expr == ')')
1555 {
1556 pcnt--;
1557 }
1558 if (pcnt == 0)
1559 {
1560 break;
1561 }
1562 expr++;
1563 }
1564
1565 if (pcnt > 0)
1566 {
1567 /* Invalid expression. */
1568 return NULL;
1569 }
1570 else
1571 {
1572 return expr;
1573 }
1574 }
1575
1576 /* Find the ending quote for the quoted string between EXPR and
1577 EXPREND inclusive; EXPR must point to the leading quote. Returns a
1578 pointer to the ending quote on success, or NULL on failure. */
1579
1580 static const char *
findMatchingQuote(const char * expr,const char * exprend)1581 findMatchingQuote (const char *expr, const char *exprend)
1582 {
1583 if (*expr != '"')
1584 {
1585 abort ();
1586 }
1587
1588 expr++;
1589
1590 while (expr <= exprend)
1591 {
1592 if (*expr == '\\')
1593 {
1594 expr++;
1595 }
1596 else if (*expr == '"')
1597 {
1598 return expr;
1599 }
1600 expr++;
1601 }
1602
1603 if (expr <= exprend)
1604 {
1605 return expr;
1606 }
1607 else
1608 {
1609 return NULL;
1610 }
1611 }
1612
1613 /* Remove any leading and trailing white space in the string between
1614 *EXPR and *EXPEND inclusive. */
1615 static void
stripWhiteSpace(const char ** expr,const char ** expend)1616 stripWhiteSpace (const char **expr, const char **expend)
1617 {
1618 while (expr <= expend && isspace ((int)(unsigned char) **expr))
1619 {
1620 (*expr)++;
1621 }
1622 while (expr <= expend && isspace ((int)(unsigned char) **expend))
1623 {
1624 (*expend)--;
1625 }
1626 }
1627
1628 /* Parse the possibly-quoted string argument between EXPR and EXPEND
1629 inclusive. Returns a malloc()ed copy of the string, with all
1630 leading and trailing whitespace removed if the string was unquoted. */
1631
1632 static char *
parseStringArgument(const char * expr,const char * expend)1633 parseStringArgument (const char *expr, const char *expend)
1634 {
1635 char *res;
1636
1637 stripWhiteSpace (&expr, &expend);
1638 if (*expr == '"' && *expend == '"')
1639 {
1640 expr++;
1641 expend--;
1642 }
1643 res = xmalloc (expend - expr + 2);
1644 memcpy (res, expr, expend - expr + 1);
1645 res[expend - expr + 1] = '\0';
1646 return res;
1647 }
1648
1649 static ComplexFieldIndex
parseQueryArgument(const DatabaseInfo database,const char * start,const char * end)1650 parseQueryArgument (const DatabaseInfo database,
1651 const char *start, const char *end)
1652 {
1653 stripWhiteSpace (&start, &end);
1654
1655 if (start[0] == '"')
1656 {
1657 char *string = parseStringArgument (start, end);
1658 ComplexFieldIndex field = newComplexFieldLiteralString (string);
1659 free (string);
1660 return field;
1661 }
1662 else
1663 {
1664 char *name = parseStringArgument (start, end);
1665 ComplexFieldIndex field = newComplexFieldIndex (database, name);
1666 free (name);
1667 return field;
1668 }
1669 }
1670
1671 /* Parse a "simple" query expression between EXPR and EXPEND inclusive. */
1672 static QueryExpr
parseSimpleQueryExpression(const DatabaseInfo database,const char * expr,const char * expend)1673 parseSimpleQueryExpression (const DatabaseInfo database, const char *expr,
1674 const char *expend)
1675 {
1676 const char *exprstart;
1677
1678 stripWhiteSpace (&expr, &expend);
1679 exprstart = expr;
1680
1681 while (expr <= expend)
1682 {
1683 if (*expr == '\\')
1684 {
1685 expr++;
1686 }
1687 else if (*expr == '"')
1688 {
1689 expr = findMatchingQuote (expr, expend);
1690 if (expr == NULL)
1691 {
1692 return NULL;
1693 }
1694 }
1695 else if (*expr == '(')
1696 {
1697 const char *pend = findMatchingParen (expr, expend);
1698 if (pend == NULL || pend != expend)
1699 {
1700 return NULL;
1701 }
1702 else
1703 {
1704 return parseQueryExpression (database, expr + 1, expend - 1);
1705 }
1706 }
1707 else
1708 {
1709 size_t len;
1710 SearchType searchType = getSearchTypeForOperator (expr, &len);
1711
1712 if (searchType != InvalidSearchType)
1713 {
1714 ComplexFieldIndex lhs, rhs;
1715
1716 lhs = parseQueryArgument (database, exprstart, expr - 1);
1717 rhs = parseQueryArgument (database, expr + len, expend);
1718
1719 return queryField (database, lhs, searchType, rhs);
1720 }
1721 }
1722 expr++;
1723 }
1724 return NULL;
1725 }
1726
1727 /* Try to parse the string between EXPR and EXPREND inclusive as a
1728 query expression. The equivalent QueryExpr tree is returned on
1729 success, or a NULL value if the expression could not be parsed.
1730
1731 We may not be handling
1732 a & b | c
1733 quite right--this gets parsed as
1734 a & (b | c)
1735 instead of
1736 (a & b) | c
1737 Also,
1738 ! a & b & c
1739 is parsed as
1740 ! (a & b & c)
1741 which may or may not be what is desired.
1742
1743 Depending on order without using parenthesis to make things clear
1744 is just asking for trouble. */
1745
1746 QueryExpr
parseQueryExpression(const DatabaseInfo database,const char * expr,const char * exprend)1747 parseQueryExpression (const DatabaseInfo database,
1748 const char *expr, const char *exprend)
1749 {
1750 const char *exprstart = expr;
1751
1752 if (exprend == NULL)
1753 {
1754 exprend = expr + strlen (expr) - 1;
1755 }
1756
1757 while (expr <= exprend)
1758 {
1759 if (*expr == '\\')
1760 {
1761 expr++;
1762 }
1763 else if (*expr == '"')
1764 {
1765 expr = findMatchingQuote (expr, exprend);
1766 if (expr == NULL)
1767 {
1768 return NULL;
1769 }
1770 }
1771 else if (*expr == '(')
1772 {
1773 const char *pend = findMatchingParen (expr, exprend);
1774 if (pend == NULL)
1775 {
1776 return NULL;
1777 }
1778 if (pend == exprend)
1779 {
1780 return parseQueryExpression (database, expr + 1, exprend - 1);
1781 }
1782 expr = pend;
1783 }
1784 else if (*expr == '!' && *(expr + 1) != '=')
1785 {
1786 QueryExpr exp;
1787 exp = parseQueryExpression (database, expr + 1, exprend);
1788 return booleanQuery (QueryNot, exp, NULL);
1789 }
1790 else if (*expr == '&' || *expr == '|')
1791 {
1792 QueryExpr left, right;
1793 QueryOp op = (*expr == '&' ? QueryAnd : QueryOr);
1794
1795 left = parseQueryExpression (database, exprstart, expr - 1);
1796 if (left == NULL)
1797 {
1798 return NULL;
1799 }
1800 right = parseQueryExpression (database, expr + 1, exprend);
1801 if (right == NULL)
1802 {
1803 return NULL;
1804 }
1805 return booleanQuery (op, left, right);
1806 }
1807 expr++;
1808 }
1809 /* Didn't find any ANDs or ORs, so maybe it's a simple expression. */
1810 return parseSimpleQueryExpression (database, exprstart, exprend);
1811 }
1812
1813 /* Appends an expression searching for SearchItem ITEM to the string
1814 in STRING, and returns either a pointer to the new string, or NULL
1815 on failure. */
1816
1817 static char *
appendSearchItem(char * string,SearchItem * item)1818 appendSearchItem (char *string, SearchItem *item)
1819 {
1820 int oldlen = (string == NULL) ? 0 : strlen (string);
1821 SearchType searchType = item->searchType;
1822 size_t addlen;
1823 char *lhs = complexFieldIndexToString (item->lhs->fieldIndex);
1824 char *rhs = complexFieldIndexToString (item->rhs->fieldIndex);
1825 const char *oper;
1826
1827 oper = getSearchOperatorForType (searchType);
1828 if (oper == NULL)
1829 {
1830 return NULL;
1831 }
1832 addlen = strlen (lhs) + strlen (oper) + strlen (rhs);
1833 string = xrealloc (string, oldlen + addlen + 1);
1834 strcpy (string + oldlen, lhs);
1835 strcat (string + oldlen, oper);
1836 strcat (string + oldlen, rhs);
1837 free (lhs);
1838 free (rhs);
1839 return string;
1840 }
1841
1842 /* Converts the QueryExpr EXPR to a string, and appends it to STRING;
1843 STRING is reallocated dynamically with xrealloc () as needed.
1844
1845 Returns either the newly-appended string, or NULL on failure. */
1846
1847 static char *
queryTreeToString(char * string,QueryTree expr)1848 queryTreeToString (char *string, QueryTree expr)
1849 {
1850 /* A little macro to append CH to the string STRING. Yes, this is
1851 sorta slow. No, I don't care. */
1852 #define addCh(CH) {\
1853 size_t len = (string == NULL) ? 0 : strlen (string);\
1854 string = xrealloc (string, len + 2);\
1855 string[len] = (CH);\
1856 string[len + 1] = '\0';\
1857 }
1858
1859 addCh ('(');
1860
1861 switch (expr->op)
1862 {
1863 case QueryAnd:
1864 case QueryOr:
1865 {
1866 string = queryTreeToString (string, expr->left);
1867 if (string != NULL)
1868 {
1869 addCh (expr->op == QueryAnd ? '&' : '|');
1870 string = queryTreeToString (string, expr->right);
1871 }
1872 break;
1873 }
1874 case QueryNot:
1875 {
1876 /* A little tricky. We replace the '(' we added above with a
1877 '!', then add a '('. */
1878 size_t len = strlen (string);
1879 string[len - 1] = '!';
1880 addCh ('(');
1881 string = queryTreeToString (string, expr->left);
1882 break;
1883 }
1884 case QueryMatch:
1885 {
1886 string = appendSearchItem (string, &(expr->ent));
1887 break;
1888 }
1889 default:
1890 {
1891 abort ();
1892 break;
1893 }
1894 }
1895 if (string != NULL)
1896 {
1897 addCh (')');
1898 }
1899 return string;
1900 #undef addChar
1901 }
1902
1903 char *
queryExprToString(char * string,QueryExpr expr)1904 queryExprToString (char *string, QueryExpr expr)
1905 {
1906 QueryTree tree = NULL;
1907
1908 if (expr != NULL)
1909 {
1910 tree = expr->tree;
1911 }
1912 return queryTreeToString (string, tree);
1913 }
1914
1915 QueryExpr
booleanQuery(QueryOp op,QueryExpr left,QueryExpr right)1916 booleanQuery (QueryOp op, QueryExpr left, QueryExpr right)
1917 {
1918 QueryExpr res;
1919
1920 if (left == NULL && right == NULL)
1921 {
1922 return NULL;
1923 }
1924 if (op != QueryNot)
1925 {
1926 if (left == NULL)
1927 {
1928 return right;
1929 }
1930 else if (right == NULL)
1931 {
1932 return left;
1933 }
1934 }
1935
1936 res = newQueryExpr (left->database);
1937 res->tree->op = op;
1938 switch (op)
1939 {
1940 case QueryOr:
1941 case QueryAnd:
1942 res->tree->left = left->tree;
1943 res->tree->right = right->tree;
1944 left->tree = NULL;
1945 right->tree = NULL;
1946 freeQueryExpr (left);
1947 freeQueryExpr (right);
1948 break;
1949 case QueryNot:
1950 res->tree->left = left->tree;
1951 res->tree->right = NULL;
1952 left->tree = NULL;
1953 freeQueryExpr (left);
1954 break;
1955 default:
1956 abort ();
1957 break;
1958 }
1959 return res;
1960 }
1961
1962 void
freeQueryFormat(QueryFormat * q)1963 freeQueryFormat (QueryFormat *q)
1964 {
1965 if (q != NULL)
1966 {
1967 if (q->name != NULL)
1968 {
1969 free (q->name);
1970 }
1971 if (q->printf != NULL)
1972 {
1973 free (q->printf);
1974 }
1975 if (q->separator != NULL)
1976 {
1977 free (q->separator);
1978 }
1979 if (q->fields != NULL)
1980 {
1981 freeFieldList (q->fields);
1982 }
1983 free (q);
1984 }
1985 }
1986
1987 void
freeQueryFormatList(QueryFormat * q)1988 freeQueryFormatList (QueryFormat *q)
1989 {
1990 while (q != NULL)
1991 {
1992 QueryFormat *next = q->next;
1993 freeQueryFormat (q);
1994 q = next;
1995 }
1996 }
1997