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).
6
This file is part of GNU GNATS.
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)
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.
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. */
26
/* One item to search for. */
28
typedef struct query_item
30
ComplexFieldIndex fieldIndex;
31
struct re_pattern_buffer *compRegexp;
34
static QueryItem freeQueryItemEnt = NULL;
37
newQueryItem (ComplexFieldIndex index)
41
if (freeQueryItemEnt != NULL)
43
res = freeQueryItemEnt;
44
freeQueryItemEnt = NULL;
48
res = (QueryItem) xmalloc (sizeof (struct query_item));
50
res->fieldIndex = index;
51
if (isConstantFieldValue (res->fieldIndex))
54
(struct re_pattern_buffer *)
55
xmalloc (sizeof (struct re_pattern_buffer));
56
memset (res->compRegexp, 0, sizeof (struct re_pattern_buffer));
60
res->compRegexp = NULL;
66
freeQueryItem (QueryItem i)
70
freeComplexFieldIndex (i->fieldIndex);
71
if (i->compRegexp != NULL)
73
/* XXX ??? !!! Hack 'cause the translate buffer is a static array */
74
i->compRegexp->translate = NULL;
75
regfree (i->compRegexp);
78
if (freeQueryItemEnt == NULL)
89
typedef struct search_item
91
/* The type of search to perform, of course. */
92
SearchType searchType;
94
/* The items on the left and right-hand-sides. */
99
typedef struct queryTree
101
/* The operation to perform on this node. */
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;
108
/* If this is a QueryMatch expression, the actual query to perform. */
114
/* Database that this query is associated with. */
115
DatabaseInfo database;
116
/* The actual query tree. */
122
const char *operator;
123
SearchType searchType;
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 }
136
/* Adds query format Q to the list of query formats. */
138
addQueryFormat (DatabaseInfo database, QueryFormat *q)
140
QueryFormat *list = getQueryFormatList (database);
143
setQueryFormatList (database, q);
147
parseQueryFormat (const DatabaseInfo database, const char *expr,
151
char *fstring = NULL;
153
FieldList currEnt = NULL;
155
while (expr[0] != '\0' && isspace ((int)(unsigned char) expr[0]))
168
while (expr[0] != '\0' && expr[0] != '\"')
170
if (expr[0] == '\\' && expr[1] != '\0')
177
fstring = xmalloc (expr - sstart + 1);
178
memcpy (fstring, sstart, expr - sstart);
179
fstring[expr - sstart] = '\0';
185
res = (QueryFormat *) xmalloc (sizeof (QueryFormat));
187
res->printf = fstring;
188
res->separator = xstrdup ("\n");
190
while (expr[0] != '\0')
192
const char *nstart = expr;
195
if (isspace ((int)(unsigned char) expr[0]))
200
while ((! isspace ((int)(unsigned char) expr[0])) && expr[0] != '\0')
204
fstring = xmalloc (expr - nstart + 1);
205
memcpy (fstring, nstart, expr - nstart);
206
fstring[expr - nstart] = '\0';
207
newEnt = newFieldListEnt (database, fstring, NULL);
210
res->fields = newEnt;
214
currEnt->next = newEnt;
217
if (parseComplexFieldIndex (currEnt->ent) != 0)
219
setError (err, CODE_INVALID_FIELD_NAME,
220
"Invalid field name or query format %s", fstring);
221
freeQueryFormat (res);
229
/* Find the first query format with name NAME; returns the query format,
230
or NULL if one was not found. */
232
findQueryFormat (const DatabaseInfo database, const char *name, ErrorDesc *err)
234
QueryFormat *q = getQueryFormatList (database);
238
if (strcmp (q->name, name) == 0)
244
/* It might be a format expression. */
245
return parseQueryFormat (database, name, err);
248
/* Return a non-zero value if P is composed entirely of digits. */
250
numeric (const char *p)
254
if (!isdigit ((int) *p))
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. */
266
get_any_date (const char *string)
272
else if (numeric (string))
274
return atoi (string);
278
return get_date ((char *) string, NULL);
282
/* Return the numeric equivalent of this state; 0 if not available. */
284
enum_numeric (const char *text, FieldIndex field)
286
if ((text != NULL) && (text[0] != '\0'))
291
for (values = fieldDefForIndex (field)->enumValues;
293
values = values->next)
295
if (strcasecmp (values->name, text) == 0)
307
sql_time (const char *s)
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);
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);
323
static char case_fold[256];
325
/* Return 0 if VALUE matched the regexp in PAT, 1 otherwise. */
327
gnats_regcmp (const char *pat, const char *value,
328
struct re_pattern_buffer *barg)
330
struct re_pattern_buffer buf;
331
struct re_pattern_buffer *bufPtr;
340
memset ((void *) &buf, 0, sizeof (buf));
347
if (case_fold[1] == 0)
350
for (i = 0; i < 256; i++)
352
case_fold[i] = tolower (i);
355
if (bufPtr->translate == NULL)
357
bufPtr->translate = case_fold;
358
bufPtr->fastmap = xmalloc (256);
360
bufPtr->syntax_parens = (char *)1;
363
r.c = re_compile_pattern (pat, strlen (pat), bufPtr);
366
fprintf (stderr, "%s: re_compile_pattern: %s\n", program_name, r.c);
367
/* XXX ??? !!! Wow. This makes real sense. :-( */
371
r.i = strlen (value);
372
r.i = re_match (bufPtr, value, strlen (value), 0, 0);
375
buf.translate = NULL;
382
"%s: warning: re_match died with pattern %s and string %s\n",
383
program_name, pat, value);
392
/* Return 0 if any portion of VALUE matches the regexp in PAT, 1 otherwise. */
394
regfind (const char *pat, const char *value, struct re_pattern_buffer *barg)
396
struct re_pattern_buffer buf;
397
struct re_pattern_buffer *bufPtr;
406
memset ((void *) &buf, 0, sizeof (buf));
413
if (case_fold[1] == 0)
416
for (i = 0; i < 256; i++)
418
case_fold[i] = tolower (i);
421
if (bufPtr->translate == NULL)
423
bufPtr->translate = case_fold;
424
bufPtr->fastmap = xmalloc (256);
426
bufPtr->syntax_parens = (char *)1;
429
r.c = re_compile_pattern (pat, strlen (pat), bufPtr);
432
fprintf (stderr, "%s: re_compile_pattern: %s\n", program_name, r.c);
433
/* XXX ??? !!! Wow. This makes real sense. :-( */
437
r.i = strlen (value);
438
r.i = re_search (bufPtr, value, r.i, 0, r.i, 0);
441
buf.translate = NULL;
448
"%s: warning: re_search died with pattern %s and string %s\n",
449
program_name, pat, value);
459
intFieldCompare (QueryItem *fields, const char *lhs, const char *rhs)
462
FieldIndex fieldIndex = simpleFieldIndexValue (fields[0]->fieldIndex);
464
if (fieldIndex != InvalidFieldIndex)
466
fieldType = fieldDefForIndex (fieldIndex)->datatype;
477
time_t lv = (lhs[0] == '\0' ? 0 : get_any_date (lhs));
478
time_t rv = (rhs[0] == '\0' ? 0 : get_any_date (rhs));
480
if (lv == -1 || rv == -1)
482
return strcmp (lhs, rhs);
518
int lv = enum_numeric (lhs, fieldIndex);
519
int rv = enum_numeric (rhs, fieldIndex);
521
if (lv == 0 || rv == 0)
523
return strcmp (lhs, rhs);
541
return strcmp (lhs, rhs);
548
fieldCompare (PR *pr, PR *oldPR, QueryItem *fields,
549
SearchType searchType, FormatNamedParameter *params)
551
struct localFieldInfo
561
for (i = 0; i < 2; i++)
563
/* Not sure what to do with this mess. XXX ??? !!! FIXME */
564
if (parseComplexFieldIndex (fields[i]->fieldIndex) != 0)
570
for (i = 0; i < 2; i++)
572
if (complexFieldType (fields[i]->fieldIndex) != InvalidFieldType)
574
int num_fields = get_num_fields (pr->database);
575
FieldType fieldType = complexFieldType (fields[i]->fieldIndex);
577
for (x = 0; x < num_fields; x++)
579
FieldIndex field = getNthField (pr->database, x);
580
if ((fieldType == Text && fieldDefForIndex (field)->textsearch)
581
|| (fieldType != Text
582
&& fieldDefForIndex (field)->datatype == fieldType))
584
ComplexFieldIndex ent = simpleComplexFieldIndex (field);
585
QueryItem newFields[2];
588
newFields[i] = newQueryItem (ent);
589
newFields[1 - i] = fields [1 - i];
591
res = fieldCompare (pr, oldPR, newFields, searchType,
594
freeQueryItem (newFields[i]);
605
for (x = 0; x < 2; x++)
607
vals[x].index = simpleFieldIndexValue (fields[x]->fieldIndex);
610
if (searchType == DefaultSearchType)
612
if (vals[0].index != InvalidFieldIndex)
614
searchType = fieldDefForIndex (vals[0].index)->defaultSearchType;
621
if ((! PR_IS_FULL (pr)) && (! isIndexedField (fields[0]->fieldIndex)))
625
if (fillInPR (pr, &err) != 0)
631
for (x = 0; x < 2; x++)
633
vals[x].value = get_field_value (pr, oldPR, fields[x]->fieldIndex,
634
params, &(vals[x].mustBeFreed));
637
if (vals[0].value == NULL || vals[1].value == NULL)
639
for (x = 0; x < 2; x++)
641
if (vals[x].mustBeFreed && vals[x].value != NULL)
643
free ((char *)vals[x].value);
651
case DefaultSearchType:
652
/* Shouldn't get here */
657
res = (gnats_regcmp (vals[1].value, vals[0].value, fields[1]->compRegexp)
662
res = (regfind (vals[1].value, vals[0].value, fields[1]->compRegexp) == 0);
666
res = (intFieldCompare (fields, vals[0].value, vals[1].value) < 0);
670
res = (intFieldCompare (fields, vals[0].value, vals[1].value) > 0);
674
res = (intFieldCompare (fields, vals[0].value, vals[1].value) == 0);
678
res = (intFieldCompare (fields, vals[0].value, vals[1].value) != 0);
682
res = (strcmp (vals[0].value, vals[1].value) == 0);
686
/* ??? XXX !!! Hmmmm. Return 1? */
690
case InvalidSearchType:
694
for (x = 0; x < 2; x++)
696
if (vals[x].mustBeFreed)
698
free ((char *) vals[x].value);
705
pr_match_field (PR *pr, PR *oldPR, SearchItem *item,
706
FormatNamedParameter *params)
715
fields[0] = item->lhs;
716
fields[1] = item->rhs;
718
return fieldCompare (pr, oldPR, fields, item->searchType, params);
721
/* Returns a non-zero value if the PR referenced by I matches the expression
725
pr_matches_tree (PR *pr, PR *oldPR, QueryTree qexp,
726
FormatNamedParameter *params)
736
return pr_match_field (pr, oldPR, &qexp->ent, params);
739
return ! pr_matches_tree (pr, oldPR, qexp->left, params);
742
return pr_matches_tree (pr, oldPR, qexp->left, params)
743
&& pr_matches_tree (pr, oldPR, qexp->right, params);
746
return pr_matches_tree (pr, oldPR, qexp->left, params)
747
|| pr_matches_tree (pr, oldPR, qexp->right, params);
756
pr_matches_expr (PR *pr, PR *oldPR, QueryExpr qexp,
757
FormatNamedParameter *params)
759
QueryTree tree = NULL;
765
return pr_matches_tree (pr, oldPR, tree, params);
769
append_string (char **res, const char *string)
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);
780
*res = xstrdup (string);
786
append_char (char **res, char chr)
792
append_string (res, buf);
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 (). */
799
do_print (FILE *fp, char **res, const char *format, const char *contents)
801
size_t flen = strlen (format);
802
if (format[flen - 1] == 'd')
804
int val = atoi (contents);
807
fprintf (fp, format, val);
813
asprintf (&new, format, val);
814
append_string (res, new);
822
fprintf (fp, format, contents);
828
asprintf (&new, format, contents);
829
append_string (res, new);
836
writeFullPR (FILE *fp, PR *pr, int rawQuery, const char *eolTerminator)
839
int num_fields = get_num_fields (pr->database);
841
for (i = 0; i < num_fields; i++)
844
const char *contents;
845
FieldIndex field = getNthField (pr->database, i);
849
ComplexFieldIndex fieldIndex = simpleComplexFieldIndex (field);
850
contents = get_field_value (pr, NULL, fieldIndex, NULL,
852
freeComplexFieldIndex (fieldIndex);
856
contents = field_value (pr, field);
859
write_pr_field (fp, field, contents, eolTerminator);
862
free ((char *) contents);
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)
872
int flen = strlen (format);
873
char *fdup = xstrdup (format);
876
ComplexFieldIndex field = (*fieldPtr)->ent;
878
switch (format[flen - 1])
883
const char *contents =
884
get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
886
if (contents == NULL)
891
do_print (fp, res, fdup, contents);
894
free ((char *) contents);
896
*fieldPtr = (*fieldPtr)->next;
903
const char *contents =
904
get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
907
if (contents == NULL)
912
for (p = contents; *p && *p != ' '; p++)
916
fdup[flen - 1] = 's';
919
temp = xmalloc (p - contents + 1);
920
memcpy (temp, contents, p - contents);
921
temp[p - contents] = '\0';
922
fprintf (fp, fdup, temp);
927
fprintf (fp, fdup, contents);
931
free ((char *) contents);
933
*fieldPtr = (*fieldPtr)->next;
941
= get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
943
write_pr_field (fp, simpleFieldIndexValue (field), contents,
947
free ((char *) contents);
949
*fieldPtr = (*fieldPtr)->next;
958
= get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
959
FieldIndex fieldIndex = simpleFieldIndexValue (field);
961
if (contents == NULL)
965
switch (fieldDefForIndex (fieldIndex)->datatype)
968
sprintf (buffer, "%d", enum_numeric (contents, fieldIndex));
969
do_print (fp, res, format, buffer);
972
sprintf (buffer, "%d", (int) get_any_date (contents));
973
do_print (fp, res, format, buffer);
976
sprintf (buffer, "%d", atoi (contents));
977
do_print (fp, res, format, buffer);
980
do_print (fp, res, format, "0");
985
free ((char *) contents);
987
*fieldPtr = (*fieldPtr)->next;
993
FieldIndex fieldIndex = simpleFieldIndexValue (field);
995
if (fieldIndex != InvalidFieldIndex
996
&& fieldDefForIndex (fieldIndex)->datatype == Date)
999
const char *strftimeFormat = "%a %b %d %H:%M:%S %z %Y";
1001
const char *contents
1002
= get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
1006
if (contents == NULL)
1010
time = get_any_date (contents);
1014
fend = strchr (fdup + 2, '}');
1017
strftimeFormat = fdup + 2;
1022
if (time == 0 || time == -1)
1024
/* Silly, but I'm lazy. */
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
1038
t = xrealloc (t, tlen);
1040
resLen = gnats_strftime (t, tlen, strftimeFormat,
1042
} while (resLen == 0 && tlen < 4096);
1044
fdup[flen - 1] = 's';
1049
do_print (fp, res, fend != NULL ? fend : fdup, t);
1053
free ((char *) contents);
1056
*fieldPtr = (*fieldPtr)->next;
1062
FieldIndex fieldIndex = simpleFieldIndexValue (field);
1064
if (fieldIndex != InvalidFieldIndex
1065
&& fieldDefForIndex (fieldIndex)->datatype == Date)
1067
int mustBeFreed = 0;
1068
const char *contents
1069
= get_field_value (pr, oldPR, field, parameters, &mustBeFreed);
1072
if (contents == NULL)
1077
i = get_any_date (contents);
1079
fdup[flen - 1] = 's';
1080
if (i != 0 && i != -1)
1082
char *qd = sql_time (contents);
1083
do_print (fp, res, fdup, qd);
1088
do_print (fp, res, fdup, "");
1092
free ((char *) contents);
1095
*fieldPtr = (*fieldPtr)->next;
1101
writeFullPR (fp, pr, rawQuery, eolTerminator);
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,
1115
static char fcopy[1024];
1117
while (*format != '\0')
1119
if (format[0] == '\\' && format[1] == 't')
1127
append_char (res, '\t');
1131
else if (format[0] == '\\' && format[1] == 'n')
1135
fputs (eolTerminator, fp);
1139
append_string (res, eolTerminator);
1143
else if (format[0] == '%')
1145
char *fptr = fcopy + 1;
1147
fcopy[0] = *(format++);
1150
while (*format != '}' && (fptr - fcopy) < 1022)
1152
*(fptr++) = *format;
1157
*(fptr++) = *format;
1161
while ((isdigit ((int) *format) || *format == '-' || *format == '+'
1163
&& ((fptr - fcopy) < 1022))
1165
*(fptr++) = *format;
1168
*(fptr++) = *format;
1181
format_pr_field (fp, res, pr, oldPR, &fields, fcopy,
1182
eolTerminator, parameters, rawQuery);
1189
fputc (*format, fp);
1193
append_char (res, *format);
1202
process_format (FILE *fp, char **res, PR *pr, PR *oldPR, QueryFormat *fmt,
1203
const char *eolTerminator, FormatNamedParameter *parameters)
1209
if (! PR_IS_FULL (pr))
1211
FieldList p = fmt->fields;
1221
if (! isIndexedField (p->ent))
1235
if (fillInPR (pr, &err))
1242
if (fmt->printf != NULL)
1244
return process_printf_format (fp, res, pr, oldPR, fmt->printf,
1245
fmt->fields, eolTerminator, parameters,
1248
else if (pr != NULL)
1250
if (fmt->fields == NULL)
1252
write_entire_header (fp, pr, eolTerminator);
1253
fprintf (fp, eolTerminator);
1254
writeFullPR (fp, pr, fmt->rawQuery, eolTerminator);
1258
FieldList flist = fmt->fields;
1260
while (flist != NULL)
1262
if (fmt->separator == NULL)
1264
const char *contents;
1265
int mustBeFreed = 0;
1266
FieldIndex fieldIndex = simpleFieldIndexValue (flist->ent);
1270
contents = field_value (pr, fieldIndex);
1274
contents = get_field_value (pr, oldPR, flist->ent,
1275
parameters, &mustBeFreed);
1277
write_pr_field (fp, fieldIndex, contents, eolTerminator);
1280
free ((char *) contents);
1282
flist = flist->next;
1286
format_pr_field (fp, res, pr, oldPR, &flist, "%s",
1287
eolTerminator, parameters, fmt->rawQuery);
1300
print_named_format_pr (FILE *fp, PR *pr, const char *fmtName,
1301
const char *eolTerminator,
1304
QueryFormat *fmt = findQueryFormat (pr->database, fmtName, err);
1312
return process_format (fp, NULL, pr, NULL, fmt, eolTerminator, NULL);
1317
print_pr (FILE *dest, PR *pr, QueryFormat *query_format,
1318
const char *eolTerminator)
1320
return process_format (dest, NULL, pr, NULL, query_format, eolTerminator,
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
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.
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. */
1339
iterate_prs (const DatabaseInfo database, int ac, char **av, QueryExpr exp,
1340
QueryFormat *query_format, QueryFunc func, ErrorDesc *err)
1343
QueryTree tree = NULL;
1347
if (exp->database != database)
1355
if (ac == 0 || av == NULL)
1357
PR *pr = getFirstPR (database, err);
1363
/* We weren't given a list of PRs to check, so we do the
1364
whole shooting match. */
1367
if (pr_matches_tree (pr, NULL, tree, NULL))
1370
func (found, pr, query_format);
1372
free_pr_header (pr);
1373
free_pr_contents (pr);
1374
pr = getNextPR (pr);
1381
for (cpr = 0; cpr < ac; cpr++)
1387
struct re_pattern_buffer buf;
1389
memset (&buf, 0, sizeof (buf));
1391
/* Remove the category */
1392
if ((n = (char *) strrchr (p, '/')) != NULL)
1398
pat = (char *) xmalloc (plen + 3);
1400
strcpy (pat + plen, "\\'");
1403
pr = getFirstPR (database, err);
1411
if (gnats_regcmp (pat, field_value (pr, NUMBER (pr->database)),
1414
if (pr_matches_expr (pr, NULL, exp, NULL))
1417
func (found, pr, query_format);
1419
/* Only one PR will match this PR number, because it's
1420
not really a regexp */
1423
free_pr_header (pr);
1424
free_pr_contents (pr);
1425
pr = getNextPR (pr);
1427
buf.translate = NULL;
1433
func (found, NULL, 0);
1441
QueryTree ent = (QueryTree) xmalloc (sizeof (struct queryTree));
1444
ent->ent.searchType = InvalidSearchType;
1445
ent->ent.lhs = NULL;
1446
ent->ent.rhs = NULL;
1447
ent->op = InvalidQueryOp;
1452
newQueryExpr (const DatabaseInfo database)
1454
QueryExpr ent = (QueryExpr) xmalloc (sizeof (struct queryExpr));
1455
ent->database = database;
1456
ent->tree = newQueryTree ();
1461
queryField (const DatabaseInfo database,
1462
ComplexFieldIndex lhs, SearchType stype, ComplexFieldIndex rhs)
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);
1473
freeQueryTree (QueryTree tree)
1477
freeQueryTree (tree->left);
1478
freeQueryTree (tree->right);
1479
freeQueryItem (tree->ent.lhs);
1480
freeQueryItem (tree->ent.rhs);
1486
freeQueryExpr (QueryExpr query)
1490
freeQueryTree (query->tree);
1495
/* Return the SearchType value for OPERATOR, which is a query
1496
expression operator. */
1498
getSearchTypeForOperator (const char *operator, size_t *len)
1502
for (x = 0; searchTypes[x].name != NULL; x++)
1504
if (searchTypes[x].operator[0] == operator[0])
1506
*len = strlen (searchTypes[x].operator);
1508
|| strncmp (searchTypes[x].operator, operator, *len) == 0)
1514
return searchTypes[x].searchType;
1517
/* Return the search operator for SearchType TYPE. */
1519
getSearchOperatorForType (SearchType type)
1522
for (x = 0; searchTypes[x].name != NULL; x++)
1524
if (searchTypes[x].searchType == type)
1526
return searchTypes[x].operator;
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. */
1539
findMatchingParen (const char *expr, const char *expend)
1548
while (expr <= expend)
1554
else if (*expr == ')')
1567
/* Invalid expression. */
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. */
1581
findMatchingQuote (const char *expr, const char *exprend)
1590
while (expr <= exprend)
1596
else if (*expr == '"')
1603
if (expr <= exprend)
1613
/* Remove any leading and trailing white space in the string between
1614
*EXPR and *EXPEND inclusive. */
1616
stripWhiteSpace (const char **expr, const char **expend)
1618
while (expr <= expend && isspace ((int)(unsigned char) **expr))
1622
while (expr <= expend && isspace ((int)(unsigned char) **expend))
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. */
1633
parseStringArgument (const char *expr, const char *expend)
1637
stripWhiteSpace (&expr, &expend);
1638
if (*expr == '"' && *expend == '"')
1643
res = xmalloc (expend - expr + 2);
1644
memcpy (res, expr, expend - expr + 1);
1645
res[expend - expr + 1] = '\0';
1649
static ComplexFieldIndex
1650
parseQueryArgument (const DatabaseInfo database,
1651
const char *start, const char *end)
1653
stripWhiteSpace (&start, &end);
1655
if (start[0] == '"')
1657
char *string = parseStringArgument (start, end);
1658
ComplexFieldIndex field = newComplexFieldLiteralString (string);
1664
char *name = parseStringArgument (start, end);
1665
ComplexFieldIndex field = newComplexFieldIndex (database, name);
1671
/* Parse a "simple" query expression between EXPR and EXPEND inclusive. */
1673
parseSimpleQueryExpression (const DatabaseInfo database, const char *expr,
1676
const char *exprstart;
1678
stripWhiteSpace (&expr, &expend);
1681
while (expr <= expend)
1687
else if (*expr == '"')
1689
expr = findMatchingQuote (expr, expend);
1695
else if (*expr == '(')
1697
const char *pend = findMatchingParen (expr, expend);
1698
if (pend == NULL || pend != expend)
1704
return parseQueryExpression (database, expr + 1, expend - 1);
1710
SearchType searchType = getSearchTypeForOperator (expr, &len);
1712
if (searchType != InvalidSearchType)
1714
ComplexFieldIndex lhs, rhs;
1716
lhs = parseQueryArgument (database, exprstart, expr - 1);
1717
rhs = parseQueryArgument (database, expr + len, expend);
1719
return queryField (database, lhs, searchType, rhs);
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.
1731
We may not be handling
1733
quite right--this gets parsed as
1741
which may or may not be what is desired.
1743
Depending on order without using parenthesis to make things clear
1744
is just asking for trouble. */
1747
parseQueryExpression (const DatabaseInfo database,
1748
const char *expr, const char *exprend)
1750
const char *exprstart = expr;
1752
if (exprend == NULL)
1754
exprend = expr + strlen (expr) - 1;
1757
while (expr <= exprend)
1763
else if (*expr == '"')
1765
expr = findMatchingQuote (expr, exprend);
1771
else if (*expr == '(')
1773
const char *pend = findMatchingParen (expr, exprend);
1778
if (pend == exprend)
1780
return parseQueryExpression (database, expr + 1, exprend - 1);
1784
else if (*expr == '!' && *(expr + 1) != '=')
1787
exp = parseQueryExpression (database, expr + 1, exprend);
1788
return booleanQuery (QueryNot, exp, NULL);
1790
else if (*expr == '&' || *expr == '|')
1792
QueryExpr left, right;
1793
QueryOp op = (*expr == '&' ? QueryAnd : QueryOr);
1795
left = parseQueryExpression (database, exprstart, expr - 1);
1800
right = parseQueryExpression (database, expr + 1, exprend);
1805
return booleanQuery (op, left, right);
1809
/* Didn't find any ANDs or ORs, so maybe it's a simple expression. */
1810
return parseSimpleQueryExpression (database, exprstart, exprend);
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
1818
appendSearchItem (char *string, SearchItem *item)
1820
int oldlen = (string == NULL) ? 0 : strlen (string);
1821
SearchType searchType = item->searchType;
1823
char *lhs = complexFieldIndexToString (item->lhs->fieldIndex);
1824
char *rhs = complexFieldIndexToString (item->rhs->fieldIndex);
1827
oper = getSearchOperatorForType (searchType);
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);
1842
/* Converts the QueryExpr EXPR to a string, and appends it to STRING;
1843
STRING is reallocated dynamically with xrealloc () as needed.
1845
Returns either the newly-appended string, or NULL on failure. */
1848
queryTreeToString (char *string, QueryTree expr)
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';\
1866
string = queryTreeToString (string, expr->left);
1869
addCh (expr->op == QueryAnd ? '&' : '|');
1870
string = queryTreeToString (string, expr->right);
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] = '!';
1881
string = queryTreeToString (string, expr->left);
1886
string = appendSearchItem (string, &(expr->ent));
1904
queryExprToString (char *string, QueryExpr expr)
1906
QueryTree tree = NULL;
1912
return queryTreeToString (string, tree);
1916
booleanQuery (QueryOp op, QueryExpr left, QueryExpr right)
1920
if (left == NULL && right == NULL)
1930
else if (right == NULL)
1936
res = newQueryExpr (left->database);
1942
res->tree->left = left->tree;
1943
res->tree->right = right->tree;
1946
freeQueryExpr (left);
1947
freeQueryExpr (right);
1950
res->tree->left = left->tree;
1951
res->tree->right = NULL;
1953
freeQueryExpr (left);
1963
freeQueryFormat (QueryFormat *q)
1967
if (q->name != NULL)
1971
if (q->printf != NULL)
1975
if (q->separator != NULL)
1977
free (q->separator);
1979
if (q->fields != NULL)
1981
freeFieldList (q->fields);
1988
freeQueryFormatList (QueryFormat *q)
1992
QueryFormat *next = q->next;
1993
freeQueryFormat (q);