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