CCL: fix regression introduced in YAZ-781
[yaz-moved-to-github.git] / src / cclfind.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file cclfind.c
7  * \brief Implements parsing of a CCL FIND query.
8  *
9  * This source file implements parsing of a CCL Query (ISO8777).
10  * The parser uses predictive parsing, but it does several tokens
11  * of lookahead in the handling of relational operations.. So
12  * it's not really pure.
13  */
14 #if HAVE_CONFIG_H
15 #include <config.h>
16 #endif
17
18 #include <stdlib.h>
19 #include <string.h>
20 #include <assert.h>
21
22 #include "cclp.h"
23
24 /* returns type of current lookahead */
25 #define KIND (cclp->look_token->kind)
26
27 /* move one token forward */
28 #define ADVANCE cclp->look_token = cclp->look_token->next
29
30 /**
31  * qual_val_type: test for existance of attribute type/value pair.
32  * qa:     Attribute array
33  * type:   Type of attribute to search for
34  * value:  Value of attribute to seach for
35  * return: 1 if found; 0 otherwise.
36  */
37 static int qual_val_type(ccl_qualifier_t *qa, int type, int value,
38                          char **attset)
39 {
40     int i;
41
42     if (!qa)
43         return 0;
44     for (i = 0; qa[i]; i++)
45     {
46         struct ccl_rpn_attr *q = ccl_qual_get_attr(qa[i]);
47         while (q)
48         {
49             if (q->type == type && q->kind == CCL_RPN_ATTR_NUMERIC &&
50                 q->value.numeric == value)
51             {
52                 if (attset)
53                     *attset = q->set;
54                 return 1;
55             }
56             q = q->next;
57         }
58     }
59     return 0;
60 }
61
62 /**
63  * strxcat: concatenate strings.
64  * n:      Null-terminated Destination string
65  * src:    Source string to be appended (not null-terminated)
66  * len:    Length of source string.
67  */
68 static void strxcat(char *n, const char *src, int len)
69 {
70     while (*n)
71         n++;
72     while (--len >= 0)
73         *n++ = *src++;
74     *n = '\0';
75 }
76
77 /**
78  * copy_token_name: Return copy of CCL token name
79  * tp:      Pointer to token info.
80  * return:  malloc(3) allocated copy of token name.
81  */
82 static char *copy_token_name(struct ccl_token *tp)
83 {
84     char *str = (char *)xmalloc(tp->len + 1);
85     ccl_assert(str);
86     memcpy(str, tp->name, tp->len);
87     str[tp->len] = '\0';
88     return str;
89 }
90
91 /**
92  * mk_node: Create RPN node.
93  * kind:   Type of node.
94  * return: pointer to allocated node.
95  */
96 struct ccl_rpn_node *ccl_rpn_node_create(enum ccl_rpn_kind kind)
97 {
98     struct ccl_rpn_node *p;
99     p = (struct ccl_rpn_node *)xmalloc(sizeof(*p));
100     ccl_assert(p);
101     p->kind = kind;
102
103     switch (kind)
104     {
105     case CCL_RPN_TERM:
106         p->u.t.attr_list = 0;
107         p->u.t.term = 0;
108         p->u.t.qual = 0;
109         break;
110     default:
111         break;
112     }
113     return p;
114 }
115
116 static struct ccl_rpn_node *ccl_rpn_dup(struct ccl_rpn_node *rpn)
117 {
118     struct ccl_rpn_node *n;
119     struct ccl_rpn_attr *attr, **attrp;
120     if (!rpn)
121         return 0;
122     n = ccl_rpn_node_create(rpn->kind);
123     switch (rpn->kind)
124     {
125     case CCL_RPN_AND:
126     case CCL_RPN_OR:
127     case CCL_RPN_NOT:
128         n->u.p[0] = ccl_rpn_dup(rpn->u.p[0]);
129         n->u.p[1] = ccl_rpn_dup(rpn->u.p[1]);
130         break;
131     case CCL_RPN_TERM:
132         n->u.t.term = xstrdup(rpn->u.t.term);
133         n->u.t.qual = rpn->u.t.qual ? xstrdup(rpn->u.t.qual) : 0;
134         attrp = &n->u.t.attr_list;
135         for (attr = rpn->u.t.attr_list; attr; attr = attr->next)
136         {
137             *attrp = (struct ccl_rpn_attr *) xmalloc(sizeof(**attrp));
138             (*attrp)->kind = attr->kind;
139             (*attrp)->type = attr->type;
140             if (attr->kind == CCL_RPN_ATTR_STRING)
141                 (*attrp)->value.str = xstrdup(attr->value.str);
142             else
143                 (*attrp)->value.numeric = attr->value.numeric;
144             if (attr->set)
145                 (*attrp)->set = xstrdup(attr->set);
146             else
147                 (*attrp)->set = 0;
148             attrp = &(*attrp)->next;
149         }
150         *attrp = 0;
151         break;
152     case CCL_RPN_SET:
153         n->u.setname = xstrdup(rpn->u.setname);
154         break;
155     case CCL_RPN_PROX:
156         n->u.p[0] = ccl_rpn_dup(rpn->u.p[0]);
157         n->u.p[1] = ccl_rpn_dup(rpn->u.p[1]);
158         n->u.p[2] = ccl_rpn_dup(rpn->u.p[2]);
159         break;
160     }
161     return n;
162 }
163
164 /**
165  * ccl_rpn_delete: Delete RPN tree.
166  * rpn:   Pointer to tree.
167  */
168 void ccl_rpn_delete(struct ccl_rpn_node *rpn)
169 {
170     struct ccl_rpn_attr *attr, *attr1;
171     if (!rpn)
172         return;
173     switch (rpn->kind)
174     {
175     case CCL_RPN_AND:
176     case CCL_RPN_OR:
177     case CCL_RPN_NOT:
178         ccl_rpn_delete(rpn->u.p[0]);
179         ccl_rpn_delete(rpn->u.p[1]);
180         break;
181     case CCL_RPN_TERM:
182         xfree(rpn->u.t.term);
183         xfree(rpn->u.t.qual);
184         for (attr = rpn->u.t.attr_list; attr; attr = attr1)
185         {
186             attr1 = attr->next;
187             if (attr->kind == CCL_RPN_ATTR_STRING)
188                 xfree(attr->value.str);
189             if (attr->set)
190                 xfree(attr->set);
191             xfree(attr);
192         }
193         break;
194     case CCL_RPN_SET:
195         xfree(rpn->u.setname);
196         break;
197     case CCL_RPN_PROX:
198         ccl_rpn_delete(rpn->u.p[0]);
199         ccl_rpn_delete(rpn->u.p[1]);
200         ccl_rpn_delete(rpn->u.p[2]);
201         break;
202     }
203     xfree(rpn);
204 }
205
206 static struct ccl_rpn_node *find_spec(CCL_parser cclp, ccl_qualifier_t *qa);
207
208 static int is_term_ok(int look, int *list)
209 {
210     for (; *list >= 0; list++)
211         if (look == *list)
212             return 1;
213     return 0;
214 }
215
216 static struct ccl_rpn_node *search_terms(CCL_parser cclp, ccl_qualifier_t *qa);
217
218 static struct ccl_rpn_attr *add_attr_node(struct ccl_rpn_node *p,
219                                            const char *set, int type)
220 {
221     struct ccl_rpn_attr *n = (struct ccl_rpn_attr *) xmalloc(sizeof(*n));
222     ccl_assert(n);
223     if (set)
224         n->set = xstrdup(set);
225     else
226         n->set = 0;
227     n->type = type;
228     n->next = p->u.t.attr_list;
229     p->u.t.attr_list = n;
230     return n;
231 }
232
233 /**
234  * add_attr_numeric: Add attribute (type/value) to RPN term node.
235  * p:     RPN node of type term.
236  * type:  Type of attribute
237  * value: Value of attribute
238  * set: Attribute set name
239  */
240 void ccl_add_attr_numeric(struct ccl_rpn_node *p, const char *set,
241                           int type, int value)
242 {
243     struct ccl_rpn_attr *n = add_attr_node(p, set, type);
244     n->kind = CCL_RPN_ATTR_NUMERIC;
245     n->value.numeric = value;
246 }
247
248 void ccl_add_attr_string(struct ccl_rpn_node *p, const char *set,
249                          int type, char *value)
250 {
251     struct ccl_rpn_attr *n = add_attr_node(p, set, type);
252     n->kind = CCL_RPN_ATTR_STRING;
253     n->value.str = xstrdup(value);
254 }
255
256 static size_t cmp_operator(const char **aliases, const char *input)
257 {
258     for (; *aliases; aliases++)
259     {
260         const char *cp = *aliases;
261         size_t i;
262         for (i = 0; *cp && *cp == input[i]; i++, cp++)
263             ;
264         if (*cp == '\0')
265             return i;
266     }
267     return 0;
268 }
269
270
271 #define REGEX_CHARS "^[]{}()|.*+?!$"
272 #define CCL_CHARS "#?\\"
273
274 static int has_ccl_masking(const char *src_str,
275                            size_t src_len,
276                            const char **truncation_aliases,
277                            const char **mask_aliases)
278 {
279     size_t j;
280     int quote_mode = 0;
281
282     for (j = 0; j < src_len; j++)
283     {
284         size_t op_size;
285         if (j > 0 && src_str[j-1] == '\\')
286             ;
287         else if (src_str[j] == '"')
288             quote_mode = !quote_mode;
289         else if (!quote_mode &&
290                  (op_size = cmp_operator(truncation_aliases,
291                                          src_str + j)))
292             return 1;
293         else if (!quote_mode &&
294                  (op_size = cmp_operator(mask_aliases,
295                                           src_str + j)))
296             return 1;
297     }
298     return 0;
299 }
300
301 static int append_term(CCL_parser cclp, const char *src_str, size_t src_len,
302                        char *dst_term, int regex_trunc, int z3958_trunc,
303                        const char **truncation_aliases,
304                        const char **mask_aliases,
305                        int is_first, int is_last,
306                        int *left_trunc, int *right_trunc)
307 {
308     size_t j;
309     int quote_mode = 0;
310
311     for (j = 0; j < src_len; j++)
312     {
313         size_t op_size;
314         if (j > 0 && src_str[j-1] == '\\')
315         {
316             if (regex_trunc && strchr(REGEX_CHARS "\\", src_str[j]))
317                 strcat(dst_term, "\\");
318             else if (z3958_trunc && strchr(CCL_CHARS "\\", src_str[j]))
319                 strcat(dst_term, "\\");
320             strxcat(dst_term, src_str + j, 1);
321         }
322         else if (src_str[j] == '"')
323             quote_mode = !quote_mode;
324         else if (!quote_mode &&
325                  (op_size = cmp_operator(truncation_aliases,
326                                          src_str + j))
327             )
328         {
329             j += (op_size - 1);  /* j++ in for loop */
330             if (regex_trunc)
331                 strcat(dst_term, ".*");
332             else if (z3958_trunc)
333                 strcat(dst_term, "?");
334             else if (is_first && j == 0)
335                 *left_trunc = 1;
336             else if (is_last && j == src_len - 1)
337                 *right_trunc = 1;
338             else
339             {
340                 cclp->error_code = CCL_ERR_TRUNC_NOT_EMBED;
341                 return -1;
342             }
343         }
344         else if (!quote_mode &&
345                  (op_size = cmp_operator(mask_aliases, src_str + j)))
346         {
347             j += (op_size - 1);  /* j++ in for loop */
348             if (regex_trunc)
349                 strcat(dst_term, ".");
350             else if (z3958_trunc)
351                 strcat(dst_term, "#");
352             else
353             {
354                 cclp->error_code = CCL_ERR_TRUNC_NOT_SINGLE;
355                 return -1;
356             }
357         }
358         else if (src_str[j] != '\\')
359         {
360             if (regex_trunc && strchr(REGEX_CHARS, src_str[j]))
361                 strcat(dst_term, "\\");
362             else if (z3958_trunc && strchr(CCL_CHARS, src_str[j]))
363                 strcat(dst_term, "\\");
364             strxcat(dst_term, src_str + j, 1);
365         }
366     }
367     return 0;
368 }
369
370
371 static struct ccl_rpn_node *ccl_term_one_use(CCL_parser cclp,
372                                              struct ccl_token *lookahead0,
373                                              struct ccl_rpn_attr *attr_use,
374                                              ccl_qualifier_t *qa,
375                                              size_t no,
376                                              int is_phrase,
377                                              int auto_group)
378 {
379     struct ccl_rpn_node *p;
380     size_t i;
381     int relation_value = -1;
382     int position_value = -1;
383     int structure_value = -1;
384     int truncation_value = -1;
385     int completeness_value = -1;
386
387     int left_trunc = 0;
388     int right_trunc = 0;
389     int regex_trunc = 0;
390     int z3958_trunc = 0;
391     int is_ccl_masked = 0;
392     char *attset;
393     struct ccl_token *lookahead = lookahead0;
394     const char **truncation_aliases;
395     const char *t_default[2];
396     const char **mask_aliases;
397     const char *m_default[2];
398     int term_len = 0;
399
400     truncation_aliases =
401         ccl_qual_search_special(cclp->bibset, "truncation");
402     if (!truncation_aliases)
403     {
404         truncation_aliases = t_default;
405         t_default[0] = "?";
406         t_default[1] = 0;
407     }
408     mask_aliases =
409         ccl_qual_search_special(cclp->bibset, "mask");
410     if (!mask_aliases)
411     {
412         mask_aliases = m_default;
413         m_default[0] = "#";
414         m_default[1] = 0;
415     }
416     for (i = 0; i < no; i++)
417     {
418         if (has_ccl_masking(lookahead->name, lookahead->len,
419                             truncation_aliases,
420                             mask_aliases))
421             is_ccl_masked = 1;
422
423         term_len += 1 + lookahead->len + lookahead->ws_prefix_len;
424         lookahead = lookahead->next;
425     }
426     lookahead = lookahead0;
427
428     p = ccl_rpn_node_create(CCL_RPN_TERM);
429     p->u.t.attr_list = NULL;
430     p->u.t.term = NULL;
431     if (qa && qa[0])
432     {
433         const char *n = ccl_qual_get_name(qa[0]);
434         if (n)
435             p->u.t.qual = xstrdup(n);
436     }
437     /* go through all attributes and add them to the attribute list */
438     for (i = 0; qa && qa[i]; i++)
439     {
440         struct ccl_rpn_attr *attr;
441         for (attr = ccl_qual_get_attr(qa[i]); attr; attr = attr->next)
442             if (attr->type != 1 || !attr_use || attr == attr_use)
443             {
444                 switch (attr->kind)
445                 {
446                 case CCL_RPN_ATTR_STRING:
447                     ccl_add_attr_string(p, attr->set, attr->type,
448                                         attr->value.str);
449                     break;
450                 case CCL_RPN_ATTR_NUMERIC:
451                     if (attr->value.numeric > 0)
452                     {   /* deal only with REAL attributes (positive) */
453                         switch (attr->type)
454                         {
455                         case CCL_BIB1_REL:
456                             if (relation_value != -1)
457                                 continue;
458                             relation_value = attr->value.numeric;
459                             break;
460                         case CCL_BIB1_POS:
461                             if (position_value != -1)
462                                 continue;
463                             position_value = attr->value.numeric;
464                             break;
465                         case CCL_BIB1_STR:
466                             if (structure_value != -1)
467                                 continue;
468                             structure_value = attr->value.numeric;
469                             break;
470                         case CCL_BIB1_TRU:
471                             if (truncation_value != -1)
472                                 continue;
473                             truncation_value = attr->value.numeric;
474                             break;
475                         case CCL_BIB1_COM:
476                             if (completeness_value != -1)
477                                 continue;
478                             completeness_value = attr->value.numeric;
479                             break;
480                         }
481                         ccl_add_attr_numeric(p, attr->set, attr->type,
482                                              attr->value.numeric);
483                     }
484                 }
485             }
486     }
487     attset = 0;
488     if (structure_value == -1 && (
489             auto_group ||
490             qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_WP, &attset))
491         )
492     {
493         if (!is_phrase)
494             ccl_add_attr_numeric(p, attset, CCL_BIB1_STR, 2);
495         else
496             ccl_add_attr_numeric(p, attset, CCL_BIB1_STR, 1);
497     }
498     if (qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_REGEX,
499                       &attset))
500     {
501         if (is_ccl_masked)
502             regex_trunc = 1; /* regex trunc (102) allowed */
503     }
504     else if (qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_Z3958,
505                            &attset))
506     {
507         if (is_ccl_masked)
508             z3958_trunc = 1; /* Z39.58 trunc (CCL) trunc allowed */
509     }
510     /* make the RPN token */
511     p->u.t.term = (char *)xmalloc(term_len * 2 + 2);
512     ccl_assert(p->u.t.term);
513     p->u.t.term[0] = '\0';
514
515     for (i = 0; i < no; i++)
516     {
517         const char *src_str = lookahead->name;
518         size_t src_len = lookahead->len;
519
520         if (p->u.t.term[0] && lookahead->ws_prefix_len)
521         {
522             strxcat(p->u.t.term, lookahead->ws_prefix_buf,
523                     lookahead->ws_prefix_len);
524         }
525         if (append_term(cclp, src_str, src_len, p->u.t.term, regex_trunc,
526                         z3958_trunc, truncation_aliases, mask_aliases,
527                         i == 0, i == no - 1,
528                         &left_trunc, &right_trunc))
529         {
530             ccl_rpn_delete(p);
531             return NULL;
532         }
533         lookahead = lookahead->next;
534     }
535     if (left_trunc && right_trunc)
536     {
537         if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_BOTH,
538                            &attset))
539         {
540             cclp->error_code = CCL_ERR_TRUNC_NOT_BOTH;
541             ccl_rpn_delete(p);
542             return NULL;
543         }
544         ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 3);
545     }
546     else if (right_trunc)
547     {
548         if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_RIGHT,
549                            &attset))
550         {
551             cclp->error_code = CCL_ERR_TRUNC_NOT_RIGHT;
552             ccl_rpn_delete(p);
553             return NULL;
554         }
555         ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 1);
556     }
557     else if (left_trunc)
558     {
559         if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_LEFT,
560                            &attset))
561         {
562             cclp->error_code = CCL_ERR_TRUNC_NOT_LEFT;
563             ccl_rpn_delete(p);
564             return NULL;
565         }
566         ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 2);
567     }
568     else if (regex_trunc)
569     {
570         ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 102);
571     }
572     else if (z3958_trunc)
573     {
574         ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 104);
575     }
576     else
577     {
578         if (qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_NONE,
579                           &attset))
580             ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 100);
581     }
582     return p;
583 }
584
585 static struct ccl_rpn_node *split_recur(CCL_parser cclp, ccl_qualifier_t *qa,
586                                         struct ccl_rpn_node *parent,
587                                         struct ccl_token **ar, size_t sz)
588 {
589     size_t l;
590     struct ccl_rpn_node *p_top = 0;
591     assert(sz > 0);
592     for (l = 1; l <= sz; l++)
593     {
594         struct ccl_rpn_node *p1;
595         struct ccl_rpn_node *p2 = ccl_term_one_use(cclp, ar[0],
596                                                    /* attr_use */0,
597                                                    qa, l,
598                                                    l > 1,
599                                                    /* auto_group */0);
600         if (!p2)
601             return 0;
602         if (parent)
603         {
604             struct ccl_rpn_node *tmp = ccl_rpn_node_create(CCL_RPN_AND);
605             tmp->u.p[0] = l > 1 ? ccl_rpn_dup(parent) : parent;
606             tmp->u.p[1] = p2;
607             p2 = tmp;
608         }
609         if (sz > l)
610             p1 = split_recur(cclp, qa, p2, ar + l, sz - l);
611         else
612             p1 = p2;
613         if (p_top)
614         {
615             struct ccl_rpn_node *tmp = ccl_rpn_node_create(CCL_RPN_OR);
616             tmp->u.p[0] = p_top;
617             tmp->u.p[1] = p1;
618             p_top = tmp;
619         }
620         else
621             p_top = p1;
622     }
623     assert(p_top);
624     return p_top;
625 }
626
627 static struct ccl_rpn_node *search_term_split_list(CCL_parser cclp,
628                                                    ccl_qualifier_t *qa,
629                                                    int *term_list, int multi)
630 {
631     struct ccl_rpn_node *p;
632     struct ccl_token **ar;
633     struct ccl_token *lookahead = cclp->look_token;
634     size_t i, sz;
635     for (sz = 0; is_term_ok(lookahead->kind, term_list); sz++)
636         lookahead = lookahead->next;
637     if (sz == 0)
638     {
639         cclp->error_code = CCL_ERR_TERM_EXPECTED;
640         return 0;
641     }
642     ar = (struct ccl_token **) xmalloc(sizeof(*lookahead) * sz);
643     lookahead = cclp->look_token;
644     for (i = 0; is_term_ok(lookahead->kind, term_list); i++)
645     {
646         ar[i] = lookahead;
647         lookahead = lookahead->next;
648     }
649     p = split_recur(cclp, qa, 0, ar, sz);
650     xfree(ar);
651     for (i = 0; i < sz; i++)
652         ADVANCE;
653     return p;
654 }
655
656 /**
657  * search_term: Parse CCL search term.
658  * cclp:   CCL Parser
659  * qa:     Qualifier attributes already applied.
660  * term_list: tokens we accept as terms in context
661  * multi:  whether we accept "multiple" tokens
662  * return: pointer to node(s); NULL on error.
663  */
664 static struct ccl_rpn_node *search_term_x(CCL_parser cclp,
665                                           ccl_qualifier_t *qa,
666                                           int *term_list, int multi)
667 {
668     struct ccl_rpn_node *p_top = 0;
669     struct ccl_token *lookahead = cclp->look_token;
670     int and_list = 0;
671     int auto_group = 0;
672     int or_list = 0;
673
674     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_AND_LIST, 0))
675         and_list = 1;
676     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_AUTO_GROUP, 0))
677         auto_group = 1;
678     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_OR_LIST, 0))
679         or_list = 1;
680     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_SPLIT_LIST, 0))
681     {
682         return search_term_split_list(cclp, qa, term_list, multi);
683     }
684     while (1)
685     {
686         struct ccl_rpn_node *p = 0;
687         size_t no, i;
688         int is_phrase = 0;
689         size_t max = 200;
690         if (and_list || or_list || !multi)
691             max = 1;
692
693         /* ignore commas when dealing with and-lists .. */
694         if (and_list && lookahead && lookahead->kind == CCL_TOK_COMMA)
695         {
696             lookahead = lookahead->next;
697             ADVANCE;
698             continue;
699         }
700         for (no = 0; no < max && is_term_ok(lookahead->kind, term_list); no++)
701         {
702             int this_is_phrase = 0;
703             for (i = 0; i<lookahead->len; i++)
704                 if (lookahead->name[i] == ' ')
705                     this_is_phrase = 1;
706             if (auto_group)
707             {
708                 if (no > 0 && (is_phrase || is_phrase != this_is_phrase))
709                     break;
710                 is_phrase = this_is_phrase;
711             }
712             else if (this_is_phrase || no > 0)
713                 is_phrase = 1;
714             lookahead = lookahead->next;
715         }
716
717         if (no == 0)
718             break;      /* no more terms . stop . */
719
720         /* go through all attributes and add them to the attribute list */
721         for (i = 0; qa && qa[i]; i++)
722         {
723             struct ccl_rpn_attr *attr;
724
725             for (attr = ccl_qual_get_attr(qa[i]); attr; attr = attr->next)
726                 if (attr->type == 1 && i == 0)
727                 {
728                     struct ccl_rpn_node *tmp2;
729                     tmp2 = ccl_term_one_use(cclp, cclp->look_token,
730                                             attr, qa, no,
731                                             is_phrase, auto_group);
732                     if (!tmp2)
733                     {
734                         ccl_rpn_delete(p);
735                         return 0;
736                     }
737                     if (!p)
738                         p = tmp2;
739                     else
740                     {
741                         struct ccl_rpn_node *tmp1;
742                         tmp1 = ccl_rpn_node_create(CCL_RPN_OR);
743                         tmp1->u.p[0] = p;
744                         tmp1->u.p[1] = tmp2;
745                         p = tmp1;
746                     }
747                 }
748         }
749         if (!p)
750             p = ccl_term_one_use(cclp, cclp->look_token,
751                                  0 /* attr: no use */, qa, no,
752                                  is_phrase, auto_group);
753         for (i = 0; i < no; i++)
754             ADVANCE;
755         if (!p)
756             return 0;
757         /* make the top node point to us.. */
758         if (p_top)
759         {
760             struct ccl_rpn_node *tmp;
761
762             if (or_list)
763                 tmp = ccl_rpn_node_create(CCL_RPN_OR);
764             else if (and_list)
765                 tmp = ccl_rpn_node_create(CCL_RPN_AND);
766             else
767                 tmp = ccl_rpn_node_create(CCL_RPN_AND);
768             tmp->u.p[0] = p_top;
769             tmp->u.p[1] = p;
770
771             p_top = tmp;
772         }
773         else
774             p_top = p;
775
776         if (!multi)
777             break;
778     }
779     if (!p_top)
780         cclp->error_code = CCL_ERR_TERM_EXPECTED;
781     return p_top;
782 }
783
784 static struct ccl_rpn_node *search_term(CCL_parser cclp, ccl_qualifier_t *qa)
785 {
786     static int list[] = {CCL_TOK_TERM, CCL_TOK_COMMA, -1};
787     return search_term_x(cclp, qa, list, 0);
788 }
789
790
791 static struct ccl_rpn_node *search_terms2(CCL_parser cclp,
792                                           ccl_qualifier_t *qa)
793 {
794     if (KIND == CCL_TOK_LP)
795     {
796         struct ccl_rpn_node *p;
797         ADVANCE;
798         if (!(p = find_spec(cclp, qa)))
799             return NULL;
800         if (KIND != CCL_TOK_RP)
801         {
802             cclp->error_code = CCL_ERR_RP_EXPECTED;
803             ccl_rpn_delete(p);
804             return NULL;
805         }
806         ADVANCE;
807         return p;
808     }
809     else
810     {
811         static int list[] = {
812             CCL_TOK_TERM, CCL_TOK_COMMA,CCL_TOK_EQ,
813             CCL_TOK_REL, CCL_TOK_SET, -1};
814
815         return search_term_x(cclp, qa, list, 1);
816     }
817 }
818
819
820 static
821 struct ccl_rpn_node *qualifiers_order(CCL_parser cclp,
822                                       ccl_qualifier_t *ap, char *attset)
823 {
824     int rel = 0;
825     struct ccl_rpn_node *p;
826
827     if (cclp->look_token->len == 1)
828     {
829         if (cclp->look_token->name[0] == '<')
830             rel = 1;
831         else if (cclp->look_token->name[0] == '=')
832             rel = 3;
833         else if (cclp->look_token->name[0] == '>')
834             rel = 5;
835     }
836     else if (cclp->look_token->len == 2)
837     {
838         if (!memcmp(cclp->look_token->name, "<=", 2))
839             rel = 2;
840         else if (!memcmp(cclp->look_token->name, ">=", 2))
841             rel = 4;
842         else if (!memcmp(cclp->look_token->name, "<>", 2))
843             rel = 6;
844     }
845     if (!rel)
846     {
847         cclp->error_code = CCL_ERR_BAD_RELATION;
848         return NULL;
849     }
850     ADVANCE;  /* skip relation */
851     if (rel == 3 &&
852         qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, 0))
853     {
854         /* allow - inside term and treat it as range _always_ */
855         /* relation is =. Extract "embedded" - to separate terms */
856         if (KIND == CCL_TOK_TERM)
857         {
858             size_t i;
859             int quote_mode = 0;
860             for (i = 0; i<cclp->look_token->len; i++)
861             {
862                 if (i > 0 && cclp->look_token->name[i] == '\\')
863                     ;
864                 else if (cclp->look_token->name[i] == '"')
865                     quote_mode = !quote_mode;
866                 else if (cclp->look_token->name[i] == '-' && !quote_mode)
867                     break;
868             }
869
870             if (cclp->look_token->len > 1 && i == 0)
871             {   /*  -xx*/
872                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
873
874                 ntoken->kind = CCL_TOK_TERM;
875                 ntoken->name = cclp->look_token->name + 1;
876                 ntoken->len = cclp->look_token->len - 1;
877
878                 cclp->look_token->len = 1;
879                 cclp->look_token->name = "-";
880             }
881             else if (cclp->look_token->len > 1 && i == cclp->look_token->len-1)
882             {   /* xx- */
883                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
884
885                 ntoken->kind = CCL_TOK_TERM;
886                 ntoken->name = "-";
887                 ntoken->len = 1;
888
889                 (cclp->look_token->len)--;
890             }
891             else if (cclp->look_token->len > 2 && i < cclp->look_token->len)
892             {   /* xx-yy */
893                 struct ccl_token *ntoken1 = ccl_token_add(cclp->look_token);
894                 struct ccl_token *ntoken2 = ccl_token_add(ntoken1);
895
896                 ntoken1->kind = CCL_TOK_TERM;  /* generate - */
897                 ntoken1->name = "-";
898                 ntoken1->len = 1;
899
900                 ntoken2->kind = CCL_TOK_TERM;  /* generate yy */
901                 ntoken2->name = cclp->look_token->name + (i+1);
902                 ntoken2->len = cclp->look_token->len - (i+1);
903
904                 cclp->look_token->len = i;     /* adjust xx */
905             }
906             else if (i == cclp->look_token->len &&
907                      cclp->look_token->next &&
908                      cclp->look_token->next->kind == CCL_TOK_TERM &&
909                      cclp->look_token->next->len > 1 &&
910                      cclp->look_token->next->name[0] == '-')
911
912             {   /* xx -yy */
913                 /* we _know_ that xx does not have - in it */
914                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
915
916                 ntoken->kind = CCL_TOK_TERM;    /* generate - */
917                 ntoken->name = "-";
918                 ntoken->len = 1;
919
920                 (ntoken->next->name)++;        /* adjust yy */
921                 (ntoken->next->len)--;
922             }
923         }
924     }
925
926     if (rel == 3 &&
927         KIND == CCL_TOK_TERM &&
928         cclp->look_token->next && cclp->look_token->next->len == 1 &&
929         cclp->look_token->next->name[0] == '-')
930     {
931         struct ccl_rpn_node *p1;
932         if (!(p1 = search_term(cclp, ap)))
933             return NULL;
934         ADVANCE;                   /* skip '-' */
935         if (KIND == CCL_TOK_TERM)  /* = term - term  ? */
936         {
937             struct ccl_rpn_node *p2;
938
939             if (!(p2 = search_term(cclp, ap)))
940             {
941                 ccl_rpn_delete(p1);
942                 return NULL;
943             }
944             p = ccl_rpn_node_create(CCL_RPN_AND);
945             p->u.p[0] = p1;
946             ccl_add_attr_numeric(p1, attset, CCL_BIB1_REL, 4);
947             p->u.p[1] = p2;
948             ccl_add_attr_numeric(p2, attset, CCL_BIB1_REL, 2);
949             return p;
950         }
951         else                       /* = term -    */
952         {
953             ccl_add_attr_numeric(p1, attset, CCL_BIB1_REL, 4);
954             return p1;
955         }
956     }
957     else if (rel == 3 &&
958              cclp->look_token->len == 1 &&
959              cclp->look_token->name[0] == '-')   /* = - term  ? */
960     {
961         ADVANCE;
962         if (!(p = search_term(cclp, ap)))
963             return NULL;
964         ccl_add_attr_numeric(p, attset, CCL_BIB1_REL, 2);
965         return p;
966     }
967     else
968     {
969         if (!(p = search_terms(cclp, ap)))
970             return NULL;
971         if (rel != 3 ||
972             !qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_OMIT_EQUALS, 0))
973             ccl_add_attr_numeric(p, attset, CCL_BIB1_REL, rel);
974         return p;
975     }
976     return NULL;
977 }
978
979 static
980 struct ccl_rpn_node *qualifier_relation(CCL_parser cclp, ccl_qualifier_t *ap)
981 {
982     char *attset;
983
984     if (qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_ORDER, &attset)
985         || qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, &attset))
986         return qualifiers_order(cclp, ap, attset);
987
988     /* unordered relation */
989     if (KIND != CCL_TOK_EQ)
990     {
991         cclp->error_code = CCL_ERR_EQ_EXPECTED;
992         return NULL;
993     }
994     ADVANCE;
995     return search_terms(cclp, ap);
996 }
997
998 /**
999  * qualifier_list: Parse CCL qualifiers and search terms.
1000  * cclp:   CCL Parser
1001  * la:     Token pointer to RELATION token.
1002  * qa:     Qualifier attributes already applied.
1003  * return: pointer to node(s); NULL on error.
1004  */
1005 static struct ccl_rpn_node *qualifier_list(CCL_parser cclp,
1006                                            struct ccl_token *la,
1007                                            ccl_qualifier_t *qa)
1008 {
1009     struct ccl_token *lookahead = cclp->look_token;
1010     struct ccl_token *look_start = cclp->look_token;
1011     ccl_qualifier_t *ap;
1012     struct ccl_rpn_node *node = 0;
1013     const char **field_str;
1014     int no = 0;
1015     int seq = 0;
1016     int i;
1017     int mode_merge = 1;
1018 #if 0
1019     if (qa)
1020     {
1021         cclp->error_code = CCL_ERR_DOUBLE_QUAL;
1022         return NULL;
1023     }
1024 #endif
1025     for (lookahead = cclp->look_token; lookahead != la;
1026          lookahead=lookahead->next)
1027         no++;
1028     if (qa)
1029         for (i=0; qa[i]; i++)
1030             no++;
1031     ap = (ccl_qualifier_t *)xmalloc((no ? (no+1) : 2) * sizeof(*ap));
1032     ccl_assert(ap);
1033
1034     field_str = ccl_qual_search_special(cclp->bibset, "field");
1035     if (field_str)
1036     {
1037         if (!strcmp(field_str[0], "or"))
1038             mode_merge = 0;
1039         else if (!strcmp(field_str[0], "merge"))
1040             mode_merge = 1;
1041     }
1042     if (!mode_merge)
1043     {
1044         /* consider each field separately and OR */
1045         lookahead = look_start;
1046         while (lookahead != la)
1047         {
1048             ap[1] = 0;
1049             seq = 0;
1050             while ((ap[0] = ccl_qual_search(cclp, lookahead->name,
1051                                             lookahead->len, seq)) != 0)
1052             {
1053                 struct ccl_rpn_node *node_sub;
1054                 cclp->look_token = la;
1055
1056                 node_sub = qualifier_relation(cclp, ap);
1057                 if (!node_sub)
1058                 {
1059                     ccl_rpn_delete(node);
1060                     xfree(ap);
1061                     return 0;
1062                 }
1063                 if (node)
1064                 {
1065                     struct ccl_rpn_node *node_this =
1066                         ccl_rpn_node_create(CCL_RPN_OR);
1067                     node_this->u.p[0] = node;
1068                     node_this->u.p[1] = node_sub;
1069                     node = node_this;
1070                 }
1071                 else
1072                     node = node_sub;
1073                 seq++;
1074             }
1075             if (seq == 0)
1076             {
1077                 cclp->look_token = lookahead;
1078                 cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
1079                 xfree(ap);
1080                 return NULL;
1081             }
1082             lookahead = lookahead->next;
1083             if (lookahead->kind == CCL_TOK_COMMA)
1084                 lookahead = lookahead->next;
1085         }
1086     }
1087     else
1088     {
1089         /* merge attributes from ALL fields - including inherited ones */
1090         while (1)
1091         {
1092             struct ccl_rpn_node *node_sub;
1093             int found = 0;
1094             lookahead = look_start;
1095             for (i = 0; lookahead != la; i++)
1096             {
1097                 ap[i] = ccl_qual_search(cclp, lookahead->name,
1098                                          lookahead->len, seq);
1099                 if (ap[i])
1100                     found++;
1101                 if (!ap[i] && seq > 0)
1102                     ap[i] = ccl_qual_search(cclp, lookahead->name,
1103                                              lookahead->len, 0);
1104                 if (!ap[i])
1105                 {
1106                     cclp->look_token = lookahead;
1107                     cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
1108                     xfree(ap);
1109                     return NULL;
1110                 }
1111                 lookahead = lookahead->next;
1112                 if (lookahead->kind == CCL_TOK_COMMA)
1113                     lookahead = lookahead->next;
1114             }
1115             if (qa)
1116             {
1117                 ccl_qualifier_t *qa0 = qa;
1118
1119                 while (*qa0)
1120                     ap[i++] = *qa0++;
1121             }
1122             ap[i] = NULL;
1123
1124             if (!found)
1125                 break;
1126
1127             cclp->look_token = lookahead;
1128
1129             node_sub = qualifier_relation(cclp, ap);
1130             if (!node_sub)
1131             {
1132                 ccl_rpn_delete(node);
1133                 break;
1134             }
1135             if (node)
1136             {
1137                 struct ccl_rpn_node *node_this =
1138                     ccl_rpn_node_create(CCL_RPN_OR);
1139                 node_this->u.p[0] = node;
1140                 node_this->u.p[1] = node_sub;
1141                 node = node_this;
1142             }
1143             else
1144                 node = node_sub;
1145             seq++;
1146         }
1147     }
1148     xfree(ap);
1149     return node;
1150 }
1151
1152
1153 /**
1154  * search_terms: Parse CCL search terms - including proximity.
1155  * cclp:   CCL Parser
1156  * qa:     Qualifier attributes already applied.
1157  * return: pointer to node(s); NULL on error.
1158  */
1159 static struct ccl_rpn_node *search_terms(CCL_parser cclp, ccl_qualifier_t *qa)
1160 {
1161     static int list[] = {
1162         CCL_TOK_TERM, CCL_TOK_COMMA,CCL_TOK_EQ,
1163         CCL_TOK_REL, CCL_TOK_SET, -1};
1164     struct ccl_rpn_node *p1, *p2, *pn;
1165     p1 = search_terms2(cclp, qa);
1166     if (!p1)
1167         return NULL;
1168     while (1)
1169     {
1170         if (KIND == CCL_TOK_PROX)
1171         {
1172             struct ccl_rpn_node *p_prox = 0;
1173             /* ! word order specified */
1174             /* % word order not specified */
1175             p_prox = ccl_rpn_node_create(CCL_RPN_TERM);
1176             p_prox->u.t.term = (char *) xmalloc(1 + cclp->look_token->len);
1177             memcpy(p_prox->u.t.term, cclp->look_token->name,
1178                    cclp->look_token->len);
1179             p_prox->u.t.term[cclp->look_token->len] = 0;
1180             p_prox->u.t.attr_list = 0;
1181
1182             ADVANCE;
1183             p2 = search_terms2(cclp, qa);
1184             if (!p2)
1185             {
1186                 ccl_rpn_delete(p1);
1187                 return NULL;
1188             }
1189             pn = ccl_rpn_node_create(CCL_RPN_PROX);
1190             pn->u.p[0] = p1;
1191             pn->u.p[1] = p2;
1192             pn->u.p[2] = p_prox;
1193             p1 = pn;
1194         }
1195         else if (is_term_ok(KIND, list))
1196         {
1197             p2 = search_terms2(cclp, qa);
1198             if (!p2)
1199             {
1200                 ccl_rpn_delete(p1);
1201                 return NULL;
1202             }
1203             pn = ccl_rpn_node_create(CCL_RPN_PROX);
1204             pn->u.p[0] = p1;
1205             pn->u.p[1] = p2;
1206             pn->u.p[2] = 0;
1207             p1 = pn;
1208         }
1209         else
1210             break;
1211     }
1212     return p1;
1213 }
1214
1215 /**
1216  * search_elements: Parse CCL search elements
1217  * cclp:   CCL Parser
1218  * qa:     Qualifier attributes already applied.
1219  * return: pointer to node(s); NULL on error.
1220  */
1221 static struct ccl_rpn_node *search_elements(CCL_parser cclp,
1222                                             ccl_qualifier_t *qa)
1223 {
1224     struct ccl_rpn_node *p1;
1225     struct ccl_token *lookahead;
1226     if (KIND == CCL_TOK_SET)
1227     {
1228         ADVANCE;
1229         if (KIND == CCL_TOK_EQ)
1230             ADVANCE;
1231         if (KIND != CCL_TOK_TERM)
1232         {
1233             cclp->error_code = CCL_ERR_SETNAME_EXPECTED;
1234             return NULL;
1235         }
1236         p1 = ccl_rpn_node_create(CCL_RPN_SET);
1237         p1->u.setname = copy_token_name(cclp->look_token);
1238         ADVANCE;
1239         return p1;
1240     }
1241     lookahead = cclp->look_token;
1242
1243     while (lookahead->kind==CCL_TOK_TERM)
1244     {
1245         lookahead = lookahead->next;
1246         if (lookahead->kind == CCL_TOK_REL || lookahead->kind == CCL_TOK_EQ)
1247             return qualifier_list(cclp, lookahead, qa);
1248         if (lookahead->kind != CCL_TOK_COMMA)
1249             break;
1250         lookahead = lookahead->next;
1251     }
1252     if (qa || lookahead->kind == CCL_TOK_LP)
1253         return search_terms(cclp, qa);
1254     else
1255     {
1256         ccl_qualifier_t qa[2];
1257         struct ccl_rpn_node *node = 0;
1258         int seq;
1259         lookahead = cclp->look_token;
1260
1261         qa[1] = 0;
1262         for(seq = 0; ;seq++)
1263         {
1264             struct ccl_rpn_node *node_sub;
1265             qa[0] = ccl_qual_search(cclp, "term", 4, seq);
1266             if (!qa[0])
1267                 break;
1268
1269             cclp->look_token = lookahead;
1270
1271             node_sub = search_terms(cclp, qa);
1272             if (!node_sub)
1273             {
1274                 ccl_rpn_delete(node);
1275                 return 0;
1276             }
1277             if (node)
1278             {
1279                 struct ccl_rpn_node *node_this =
1280                     ccl_rpn_node_create(CCL_RPN_OR);
1281                 node_this->u.p[0] = node;
1282                 node_this->u.p[1] = node_sub;
1283                 node_this->u.p[2] = 0;
1284                 node = node_this;
1285             }
1286             else
1287                 node = node_sub;
1288         }
1289         if (!node)
1290             node = search_terms(cclp, 0);
1291         return node;
1292     }
1293 }
1294
1295 /**
1296  * find_spec: Parse CCL find specification
1297  * cclp:   CCL Parser
1298  * qa:     Qualifier attributes already applied.
1299  * return: pointer to node(s); NULL on error.
1300  */
1301 static struct ccl_rpn_node *find_spec(CCL_parser cclp, ccl_qualifier_t *qa)
1302 {
1303     struct ccl_rpn_node *p1, *p2, *pn;
1304     if (!(p1 = search_elements(cclp, qa)))
1305         return NULL;
1306     while (1)
1307     {
1308         switch (KIND)
1309         {
1310         case CCL_TOK_AND:
1311             ADVANCE;
1312             p2 = search_elements(cclp, qa);
1313             if (!p2)
1314             {
1315                 ccl_rpn_delete(p1);
1316                 return NULL;
1317             }
1318             pn = ccl_rpn_node_create(CCL_RPN_AND);
1319             pn->u.p[0] = p1;
1320             pn->u.p[1] = p2;
1321             pn->u.p[2] = 0;
1322             p1 = pn;
1323             continue;
1324         case CCL_TOK_OR:
1325             ADVANCE;
1326             p2 = search_elements(cclp, qa);
1327             if (!p2)
1328             {
1329                 ccl_rpn_delete(p1);
1330                 return NULL;
1331             }
1332             pn = ccl_rpn_node_create(CCL_RPN_OR);
1333             pn->u.p[0] = p1;
1334             pn->u.p[1] = p2;
1335             pn->u.p[2] = 0;
1336             p1 = pn;
1337             continue;
1338         case CCL_TOK_NOT:
1339             ADVANCE;
1340             p2 = search_elements(cclp, qa);
1341             if (!p2)
1342             {
1343                 ccl_rpn_delete(p1);
1344                 return NULL;
1345             }
1346             pn = ccl_rpn_node_create(CCL_RPN_NOT);
1347             pn->u.p[0] = p1;
1348             pn->u.p[1] = p2;
1349             pn->u.p[2] = 0;
1350             p1 = pn;
1351             continue;
1352         }
1353         break;
1354     }
1355     return p1;
1356 }
1357
1358 struct ccl_rpn_node *ccl_parser_find_str(CCL_parser cclp, const char *str)
1359 {
1360     struct ccl_rpn_node *p;
1361     struct ccl_token *list = ccl_parser_tokenize(cclp, str);
1362     p = ccl_parser_find_token(cclp, list);
1363     ccl_token_del(list);
1364     return p;
1365 }
1366
1367 struct ccl_rpn_node *ccl_parser_find_token(CCL_parser cclp,
1368                                            struct ccl_token *list)
1369 {
1370     struct ccl_rpn_node *p;
1371
1372     cclp->look_token = list;
1373     p = find_spec(cclp, NULL);
1374     if (p && KIND != CCL_TOK_EOL)
1375     {
1376         if (KIND == CCL_TOK_RP)
1377             cclp->error_code = CCL_ERR_BAD_RP;
1378         else
1379             cclp->error_code = CCL_ERR_OP_EXPECTED;
1380         ccl_rpn_delete(p);
1381         p = NULL;
1382     }
1383     cclp->error_pos = cclp->look_token->name;
1384     if (p)
1385         cclp->error_code = CCL_ERR_OK;
1386     else
1387         cclp->error_code = cclp->error_code;
1388     return p;
1389 }
1390
1391 /**
1392  * ccl_find_str: Parse CCL find - string representation
1393  * bibset:  Bibset to be used for the parsing
1394  * str:     String to be parsed
1395  * error:   Pointer to integer. Holds error no. on completion.
1396  * pos:     Pointer to char position. Holds approximate error position.
1397  * return:  RPN tree on successful completion; NULL otherwise.
1398  */
1399 struct ccl_rpn_node *ccl_find_str(CCL_bibset bibset, const char *str,
1400                                   int *error, int *pos)
1401 {
1402     CCL_parser cclp = ccl_parser_create(bibset);
1403     struct ccl_token *list;
1404     struct ccl_rpn_node *p;
1405
1406     list = ccl_parser_tokenize(cclp, str);
1407     p = ccl_parser_find_token(cclp, list);
1408
1409     *error = cclp->error_code;
1410     if (*error)
1411         *pos = cclp->error_pos - str;
1412     ccl_parser_destroy(cclp);
1413     ccl_token_del(list);
1414     return p;
1415 }
1416
1417 /*
1418  * Local variables:
1419  * c-basic-offset: 4
1420  * c-file-style: "Stroustrup"
1421  * indent-tabs-mode: nil
1422  * End:
1423  * vim: shiftwidth=4 tabstop=8 expandtab
1424  */
1425