~ubuntu-branches/ubuntu/trusty/gnats/trusty

« back to all changes in this revision

Viewing changes to .pc/format-security.patch/gnats/query.c

  • Committer: Package Import Robot
  • Author(s): Colin Watson
  • Date: 2014-01-09 13:44:48 UTC
  • Revision ID: package-import@ubuntu.com-20140109134448-svwxwts6b2buvo6b
Tags: 4.1.0-3
* QA upload.
* Pass "-s /bin/sh" to "su gnats" to cope with the change of the gnats
  user's shell in base-passwd 3.5.30.
* Remove config.guess and config.sub from Debian patches, and instead
  build-depend on autotools-dev to ensure that cdbs will update them.
* Use dh-autoreconf to update configure scripts, rather than a gigantic
  manual patch.
* Split what remained of debian/patches/debian-changes into separate
  patches with proper DEP-3 headers.
* Pacify "gcc -Wformat-security" in process_format.
* Debconf translations:
  - French (thanks, Christian Perrier; closes: #718198).
  - Italian (thanks, Beatrice Torracca; closes: #719803).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
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
 
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
 
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 *
 
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 *
 
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
 
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
 
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
 
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 *
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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
 
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 *
 
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 *
 
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 *
 
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
 
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 *
 
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
 
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
 
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
 
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 *
 
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 *
 
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 *
 
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
 
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
 
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
 
1988
freeQueryFormatList (QueryFormat *q)
 
1989
{
 
1990
  while (q != NULL)
 
1991
    {
 
1992
      QueryFormat *next = q->next;
 
1993
      freeQueryFormat (q);
 
1994
      q = next;
 
1995
    }
 
1996
}