CCL: retain ? for CCL terms
[yaz-moved-to-github.git] / src / cclfind.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 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
21 #include "cclp.h"
22
23 /* returns type of current lookahead */
24 #define KIND (cclp->look_token->kind)
25
26 /* move one token forward */
27 #define ADVANCE cclp->look_token = cclp->look_token->next
28
29 /**
30  * qual_val_type: test for existance of attribute type/value pair.
31  * qa:     Attribute array
32  * type:   Type of attribute to search for
33  * value:  Value of attribute to seach for
34  * return: 1 if found; 0 otherwise.
35  */
36 static int qual_val_type(ccl_qualifier_t *qa, int type, int value,
37                          char **attset)
38 {
39     int i;
40
41     if (!qa)
42         return 0;
43     for (i = 0; qa[i]; i++)
44     {
45         struct ccl_rpn_attr *q = ccl_qual_get_attr(qa[i]);
46         while (q)
47         {
48             if (q->type == type && q->kind == CCL_RPN_ATTR_NUMERIC &&
49                 q->value.numeric == value)
50             {
51                 if (attset)
52                     *attset = q->set;
53                 return 1;
54             }
55             q = q->next;
56         }
57     }
58     return 0;
59 }
60
61 /**
62  * strxcat: concatenate strings.
63  * n:      Null-terminated Destination string 
64  * src:    Source string to be appended (not null-terminated)
65  * len:    Length of source string.
66  */
67 static void strxcat(char *n, const char *src, int len)
68 {
69     while (*n)
70         n++;
71     while (--len >= 0)
72         *n++ = *src++;
73     *n = '\0';
74 }
75
76 /**
77  * copy_token_name: Return copy of CCL token name
78  * tp:      Pointer to token info.
79  * return:  malloc(3) allocated copy of token name.
80  */
81 static char *copy_token_name(struct ccl_token *tp)
82 {
83     char *str = (char *)xmalloc(tp->len + 1);
84     ccl_assert(str);
85     memcpy(str, tp->name, tp->len);
86     str[tp->len] = '\0';
87     return str;
88 }
89
90 /**
91  * mk_node: Create RPN node.
92  * kind:   Type of node.
93  * return: pointer to allocated node.
94  */
95 struct ccl_rpn_node *ccl_rpn_node_create(enum ccl_rpn_kind kind)
96 {
97     struct ccl_rpn_node *p;
98     p = (struct ccl_rpn_node *)xmalloc(sizeof(*p));
99     ccl_assert(p);
100     p->kind = kind;
101
102     switch(kind)
103     {
104     case CCL_RPN_TERM:
105         p->u.t.attr_list = 0;
106         p->u.t.term = 0;
107         p->u.t.qual = 0;
108         break;
109     default:
110         break;
111     }
112     return p;
113 }
114
115 /**
116  * ccl_rpn_delete: Delete RPN tree.
117  * rpn:   Pointer to tree.
118  */
119 void ccl_rpn_delete(struct ccl_rpn_node *rpn)
120 {
121     struct ccl_rpn_attr *attr, *attr1;
122     if (!rpn)
123         return;
124     switch (rpn->kind)
125     {
126     case CCL_RPN_AND:
127     case CCL_RPN_OR:
128     case CCL_RPN_NOT:
129         ccl_rpn_delete(rpn->u.p[0]);
130         ccl_rpn_delete(rpn->u.p[1]);
131         break;
132     case CCL_RPN_TERM:
133         xfree(rpn->u.t.term);
134         xfree(rpn->u.t.qual);
135         for (attr = rpn->u.t.attr_list; attr; attr = attr1)
136         {
137             attr1 = attr->next;
138             if (attr->kind == CCL_RPN_ATTR_STRING)
139                 xfree(attr->value.str);
140             if (attr->set)
141                 xfree(attr->set);
142             xfree(attr);
143         }
144         break;
145     case CCL_RPN_SET:
146         xfree(rpn->u.setname);
147         break;
148     case CCL_RPN_PROX:
149         ccl_rpn_delete(rpn->u.p[0]);
150         ccl_rpn_delete(rpn->u.p[1]);
151         ccl_rpn_delete(rpn->u.p[2]);
152         break;
153     }
154     xfree(rpn);
155 }
156
157 static struct ccl_rpn_node *find_spec(CCL_parser cclp, ccl_qualifier_t *qa);
158
159 static int is_term_ok(int look, int *list)
160 {
161     for (;*list >= 0; list++)
162         if (look == *list)
163             return 1;
164     return 0;
165 }
166
167 static struct ccl_rpn_node *search_terms(CCL_parser cclp, ccl_qualifier_t *qa);
168
169 static struct ccl_rpn_attr *add_attr_node(struct ccl_rpn_node *p,
170                                            const char *set, int type)
171 {
172     struct ccl_rpn_attr *n;
173     
174     n = (struct ccl_rpn_attr *)xmalloc(sizeof(*n));
175     ccl_assert(n);
176     if (set)
177         n->set = xstrdup(set);
178     else
179         n->set = 0;
180     n->type = type;
181     n->next = p->u.t.attr_list;
182     p->u.t.attr_list = n;
183     
184     return n;
185 }
186
187 /**
188  * add_attr_numeric: Add attribute (type/value) to RPN term node.
189  * p:     RPN node of type term.
190  * type:  Type of attribute
191  * value: Value of attribute
192  * set: Attribute set name
193  */
194 void ccl_add_attr_numeric(struct ccl_rpn_node *p, const char *set,
195                           int type, int value)
196 {
197     struct ccl_rpn_attr *n;
198
199     n = add_attr_node(p, set, type);
200     n->kind = CCL_RPN_ATTR_NUMERIC;
201     n->value.numeric = value;
202 }
203
204 void ccl_add_attr_string(struct ccl_rpn_node *p, const char *set,
205                          int type, char *value)
206 {
207     struct ccl_rpn_attr *n;
208
209     n = add_attr_node(p, set, type);
210     n->kind = CCL_RPN_ATTR_STRING;
211     n->value.str = xstrdup(value);
212 }
213
214
215 /**
216  * search_term: Parse CCL search term. 
217  * cclp:   CCL Parser
218  * qa:     Qualifier attributes already applied.
219  * term_list: tokens we accept as terms in context
220  * multi:  whether we accept "multiple" tokens
221  * return: pointer to node(s); NULL on error.
222  */
223 static struct ccl_rpn_node *search_term_x(CCL_parser cclp,
224                                           ccl_qualifier_t *qa,
225                                           int *term_list, int multi)
226 {
227     struct ccl_rpn_node *p_top = 0;
228     struct ccl_token *lookahead = cclp->look_token;
229     int and_list = 0;
230     int or_list = 0;
231     char *attset;
232     const char **truncation_aliases;
233     const char *t_default[2];
234
235     truncation_aliases =
236         ccl_qual_search_special(cclp->bibset, "truncation");
237     if (!truncation_aliases)
238     {
239         truncation_aliases = t_default;
240         t_default[0] = "?";
241         t_default[1] = 0;
242     }
243
244     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_AND_LIST, 0))
245         and_list = 1;
246     if (qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_OR_LIST, 0))
247         or_list = 1;
248     while (1)
249     {
250         struct ccl_rpn_node *p;
251         size_t no, i;
252         int no_spaces = 0;
253         int relation_value = -1;
254         int position_value = -1;
255         int structure_value = -1;
256         int truncation_value = -1;
257         int completeness_value = -1;
258         int len = 0;
259         int left_trunc = 0;
260         int right_trunc = 0;
261         size_t max = 200;
262         if (and_list || or_list || !multi)
263             max = 1;
264         
265         /* ignore commas when dealing with and-lists .. */
266         if (and_list && lookahead && lookahead->kind == CCL_TOK_COMMA)
267         {
268             lookahead = lookahead->next;
269             ADVANCE;
270             continue;
271         }
272         /* go through each TERM token. If no truncation attribute is yet
273            met, then look for left/right truncation markers (?) and
274            set left_trunc/right_trunc/mid_trunc accordingly */
275         for (no = 0; no < max && is_term_ok(lookahead->kind, term_list); no++)
276         {
277             for (i = 0; i<lookahead->len; i++)
278                 if (lookahead->name[i] == ' ')
279                     no_spaces++;
280             len += 1+lookahead->len+lookahead->ws_prefix_len;
281             lookahead = lookahead->next;
282         }
283
284         if (len == 0)
285             break;      /* no more terms . stop . */
286                 
287         /* create the term node, but wait a moment before adding the term */
288         p = ccl_rpn_node_create(CCL_RPN_TERM);
289         p->u.t.attr_list = NULL;
290         p->u.t.term = NULL;
291         if (qa && qa[0])
292         {
293             const char *n = ccl_qual_get_name(qa[0]);
294             if (n)
295                 p->u.t.qual = xstrdup(n);
296         }
297
298         /* go through all attributes and add them to the attribute list */
299         for (i=0; qa && qa[i]; i++)
300         {
301             struct ccl_rpn_attr *attr;
302             
303             for (attr = ccl_qual_get_attr(qa[i]); attr; attr = attr->next)
304                 switch(attr->kind)
305                 {
306                 case CCL_RPN_ATTR_STRING:
307                     ccl_add_attr_string(p, attr->set, attr->type,
308                                         attr->value.str);
309                     break;
310                 case CCL_RPN_ATTR_NUMERIC:
311                     if (attr->value.numeric > 0)
312                     {   /* deal only with REAL attributes (positive) */
313                         switch (attr->type)
314                         {
315                         case CCL_BIB1_REL:
316                             if (relation_value != -1)
317                                 continue;
318                             relation_value = attr->value.numeric;
319                             break;
320                         case CCL_BIB1_POS:
321                             if (position_value != -1)
322                                 continue;
323                             position_value = attr->value.numeric;
324                             break;
325                         case CCL_BIB1_STR:
326                             if (structure_value != -1)
327                                 continue;
328                             structure_value = attr->value.numeric;
329                             break;
330                         case CCL_BIB1_TRU:
331                             if (truncation_value != -1)
332                                 continue;
333                             truncation_value = attr->value.numeric;
334                             break;
335                         case CCL_BIB1_COM:
336                             if (completeness_value != -1)
337                                 continue;
338                             completeness_value = attr->value.numeric;
339                             break;
340                         }
341                         ccl_add_attr_numeric(p, attr->set, attr->type,
342                                              attr->value.numeric);
343                     }
344                 }
345         }
346         /* len now holds the number of characters in the RPN term */
347         /* no holds the number of CCL tokens (1 or more) */
348         
349         if (structure_value == -1 && 
350             qual_val_type(qa, CCL_BIB1_STR, CCL_BIB1_STR_WP, &attset))
351         {   /* no structure attribute met. Apply either structure attribute 
352                WORD or PHRASE depending on number of CCL tokens */
353             if (no == 1 && no_spaces == 0)
354                 ccl_add_attr_numeric(p, attset, CCL_BIB1_STR, 2);
355             else
356                 ccl_add_attr_numeric(p, attset, CCL_BIB1_STR, 1);
357         }
358
359         /* make the RPN token */
360         p->u.t.term = (char *)xmalloc(len);
361         ccl_assert(p->u.t.term);
362         p->u.t.term[0] = '\0';
363         for (i = 0; i<no; i++)
364         {
365             const char *src_str = cclp->look_token->name;
366             size_t src_len = cclp->look_token->len;
367
368             if (i == 0 && src_len > 0 && *src_str == '?')
369             {
370                 src_len--;
371                 src_str++;
372                 left_trunc = 1;
373             }
374             if (i == no - 1 && src_len > 0 && src_str[src_len-1] == '?')
375             {
376                 src_len--;
377                 right_trunc = 1;
378             }
379             if (p->u.t.term[0] && cclp->look_token->ws_prefix_len)
380             {
381                 size_t len = strlen(p->u.t.term);
382                 memcpy(p->u.t.term + len, cclp->look_token->ws_prefix_buf,
383                        cclp->look_token->ws_prefix_len);
384                 p->u.t.term[len + cclp->look_token->ws_prefix_len] = '\0';
385             }
386             strxcat(p->u.t.term, src_str, src_len);
387             ADVANCE;
388         }
389
390         /* make the top node point to us.. */
391         if (p_top)
392         {
393             struct ccl_rpn_node *tmp;
394
395             if (or_list)
396                 tmp = ccl_rpn_node_create(CCL_RPN_OR);
397             else if (and_list)
398                 tmp = ccl_rpn_node_create(CCL_RPN_AND);
399             else
400                 tmp = ccl_rpn_node_create(CCL_RPN_AND);
401             tmp->u.p[0] = p_top;
402             tmp->u.p[1] = p;
403
404             p_top = tmp;
405         }
406         else
407             p_top = p;
408
409
410         if (left_trunc && right_trunc)
411         {
412             if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_BOTH,
413                                 &attset))
414             {
415                 cclp->error_code = CCL_ERR_TRUNC_NOT_BOTH;
416                 ccl_rpn_delete(p);
417                 return NULL;
418             }
419             ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 3);
420         }
421         else if (right_trunc)
422         {
423             if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_RIGHT,
424                                  &attset))
425             {
426                 cclp->error_code = CCL_ERR_TRUNC_NOT_RIGHT;
427                 ccl_rpn_delete(p);
428                 return NULL;
429             }
430             ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 1);
431         }
432         else if (left_trunc)
433         {
434             if (!qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_LEFT,
435                                 &attset))
436             {
437                 cclp->error_code = CCL_ERR_TRUNC_NOT_LEFT;
438                 ccl_rpn_delete(p);
439                 return NULL;
440             }
441             ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 2);
442         }
443         else
444         {
445             if (qual_val_type(qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_NONE,
446                                &attset))
447                 ccl_add_attr_numeric(p, attset, CCL_BIB1_TRU, 100);
448         }
449         if (!multi)
450             break;
451     }
452     if (!p_top)
453         cclp->error_code = CCL_ERR_TERM_EXPECTED;
454     return p_top;
455 }
456
457 static struct ccl_rpn_node *search_term(CCL_parser cclp, ccl_qualifier_t *qa)
458 {
459     static int list[] = {CCL_TOK_TERM, CCL_TOK_COMMA, -1};
460     return search_term_x(cclp, qa, list, 0);
461 }
462
463 static
464 struct ccl_rpn_node *qualifiers_order(CCL_parser cclp,
465                                       ccl_qualifier_t *ap, char *attset)
466 {
467     int rel = 0;
468     struct ccl_rpn_node *p;
469
470     if (cclp->look_token->len == 1)
471     {
472         if (cclp->look_token->name[0] == '<')
473             rel = 1;
474         else if (cclp->look_token->name[0] == '=')
475             rel = 3;
476         else if (cclp->look_token->name[0] == '>')
477             rel = 5;
478     }
479     else if (cclp->look_token->len == 2)
480     {
481         if (!memcmp(cclp->look_token->name, "<=", 2))
482             rel = 2;
483         else if (!memcmp(cclp->look_token->name, ">=", 2))
484             rel = 4;
485         else if (!memcmp(cclp->look_token->name, "<>", 2))
486             rel = 6;
487     }
488     if (!rel)
489     {
490         cclp->error_code = CCL_ERR_BAD_RELATION;
491         return NULL;
492     }
493     ADVANCE;  /* skip relation */
494     if (rel == 3 &&
495         qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, 0))
496     {
497         /* allow - inside term and treat it as range _always_ */
498         /* relation is =. Extract "embedded" - to separate terms */
499         if (KIND == CCL_TOK_TERM)
500         {
501             size_t i;
502             for (i = 0; i<cclp->look_token->len; i++)
503             {
504                 if (cclp->look_token->name[i] == '-')
505                     break;
506             }
507             
508             if (cclp->look_token->len > 1 && i == 0)
509             {   /*  -xx*/
510                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
511
512                 ntoken->kind = CCL_TOK_TERM;
513                 ntoken->name = cclp->look_token->name + 1;
514                 ntoken->len = cclp->look_token->len - 1;
515
516                 cclp->look_token->len = 1;
517                 cclp->look_token->name = "-";
518             }
519             else if (cclp->look_token->len > 1 && i == cclp->look_token->len-1)
520             {   /* xx- */
521                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
522
523                 ntoken->kind = CCL_TOK_TERM;
524                 ntoken->name = "-";
525                 ntoken->len = 1;
526
527                 (cclp->look_token->len)--;
528             }
529             else if (cclp->look_token->len > 2 && i < cclp->look_token->len)
530             {   /* xx-yy */
531                 struct ccl_token *ntoken1 = ccl_token_add(cclp->look_token);
532                 struct ccl_token *ntoken2 = ccl_token_add(ntoken1);
533
534                 ntoken1->kind = CCL_TOK_TERM;  /* generate - */
535                 ntoken1->name = "-";
536                 ntoken1->len = 1;
537
538                 ntoken2->kind = CCL_TOK_TERM;  /* generate yy */
539                 ntoken2->name = cclp->look_token->name + (i+1);
540                 ntoken2->len = cclp->look_token->len - (i+1);
541
542                 cclp->look_token->len = i;     /* adjust xx */
543             }
544             else if (i == cclp->look_token->len &&
545                      cclp->look_token->next &&
546                      cclp->look_token->next->kind == CCL_TOK_TERM &&
547                      cclp->look_token->next->len > 1 &&
548                      cclp->look_token->next->name[0] == '-')
549                      
550             {   /* xx -yy */
551                 /* we _know_ that xx does not have - in it */
552                 struct ccl_token *ntoken = ccl_token_add(cclp->look_token);
553
554                 ntoken->kind = CCL_TOK_TERM;    /* generate - */
555                 ntoken->name = "-";
556                 ntoken->len = 1;
557
558                 (ntoken->next->name)++;        /* adjust yy */
559                 (ntoken->next->len)--; 
560             }
561         }
562     }
563         
564     if (rel == 3 &&
565         KIND == CCL_TOK_TERM &&
566         cclp->look_token->next && cclp->look_token->next->len == 1 &&
567         cclp->look_token->next->name[0] == '-')
568     {
569         struct ccl_rpn_node *p1;
570         if (!(p1 = search_term(cclp, ap)))
571             return NULL;
572         ADVANCE;                   /* skip '-' */
573         if (KIND == CCL_TOK_TERM)  /* = term - term  ? */
574         {
575             struct ccl_rpn_node *p2;
576             
577             if (!(p2 = search_term(cclp, ap)))
578             {
579                 ccl_rpn_delete(p1);
580                 return NULL;
581             }
582             p = ccl_rpn_node_create(CCL_RPN_AND);
583             p->u.p[0] = p1;
584             ccl_add_attr_numeric(p1, attset, CCL_BIB1_REL, 4);
585             p->u.p[1] = p2;
586             ccl_add_attr_numeric(p2, attset, CCL_BIB1_REL, 2);
587             return p;
588         }
589         else                       /* = term -    */
590         {
591             ccl_add_attr_numeric(p1, attset, CCL_BIB1_REL, 4);
592             return p1;
593         }
594     }
595     else if (rel == 3 &&
596              cclp->look_token->len == 1 &&
597              cclp->look_token->name[0] == '-')   /* = - term  ? */
598     {
599         ADVANCE;
600         if (!(p = search_term(cclp, ap)))
601             return NULL;
602         ccl_add_attr_numeric(p, attset, CCL_BIB1_REL, 2);
603         return p;
604     }
605     else if (KIND == CCL_TOK_LP)
606     {
607         ADVANCE;
608         if (!(p = find_spec(cclp, ap)))
609             return NULL;
610         if (KIND != CCL_TOK_RP)
611         {
612             cclp->error_code = CCL_ERR_RP_EXPECTED;
613             ccl_rpn_delete(p);
614             return NULL;
615         }
616         ADVANCE;
617         return p;
618     }
619     else
620     {
621         if (!(p = search_terms(cclp, ap)))
622             return NULL;
623         ccl_add_attr_numeric(p, attset, CCL_BIB1_REL, rel);
624         return p;
625     }
626     cclp->error_code = CCL_ERR_TERM_EXPECTED;
627     return NULL;
628 }
629
630 static
631 struct ccl_rpn_node *qualifier_relation(CCL_parser cclp, ccl_qualifier_t *ap)
632 {
633     char *attset;
634     struct ccl_rpn_node *p;
635     
636     if (qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_ORDER, &attset)
637         || qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, &attset))
638         return qualifiers_order(cclp, ap, attset);
639
640     /* unordered relation */
641     if (KIND != CCL_TOK_EQ)
642     {
643         cclp->error_code = CCL_ERR_EQ_EXPECTED;
644         return NULL;
645     }
646     ADVANCE;
647     if (KIND == CCL_TOK_LP)
648     {
649         ADVANCE;
650         if (!(p = find_spec(cclp, ap)))
651         {
652             return NULL;
653         }
654         if (KIND != CCL_TOK_RP)
655         {
656             cclp->error_code = CCL_ERR_RP_EXPECTED;
657             ccl_rpn_delete(p);
658             return NULL;
659         }
660         ADVANCE;
661     }
662     else
663         p = search_terms(cclp, ap);
664     return p;
665 }
666
667 /**
668  * qualifier_list: Parse CCL qualifiers and search terms. 
669  * cclp:   CCL Parser
670  * la:     Token pointer to RELATION token.
671  * qa:     Qualifier attributes already applied.
672  * return: pointer to node(s); NULL on error.
673  */
674 static struct ccl_rpn_node *qualifier_list(CCL_parser cclp, 
675                                            struct ccl_token *la,
676                                            ccl_qualifier_t *qa)
677 {
678     struct ccl_token *lookahead = cclp->look_token;
679     struct ccl_token *look_start = cclp->look_token;
680     ccl_qualifier_t *ap;
681     struct ccl_rpn_node *node = 0;
682     const char **field_str;
683     int no = 0;
684     int seq = 0;
685     int i;
686     int mode_merge = 1;
687 #if 0
688     if (qa)
689     {
690         cclp->error_code = CCL_ERR_DOUBLE_QUAL;
691         return NULL;
692     }
693 #endif
694     for (lookahead = cclp->look_token; lookahead != la;
695          lookahead=lookahead->next)
696         no++;
697     if (qa)
698         for (i=0; qa[i]; i++)
699             no++;
700     ap = (ccl_qualifier_t *)xmalloc((no ? (no+1) : 2) * sizeof(*ap));
701     ccl_assert(ap);
702
703     field_str = ccl_qual_search_special(cclp->bibset, "field");
704     if (field_str)
705     {
706         if (!strcmp(field_str[0], "or"))
707             mode_merge = 0;
708         else if (!strcmp(field_str[0], "merge"))
709             mode_merge = 1;
710     }
711     if (!mode_merge)
712     {
713         /* consider each field separately and OR */
714         lookahead = look_start;
715         while (lookahead != la)
716         {
717             ap[1] = 0;
718             seq = 0;
719             while ((ap[0] = ccl_qual_search(cclp, lookahead->name,
720                                             lookahead->len, seq)) != 0)
721             {
722                 struct ccl_rpn_node *node_sub;
723                 cclp->look_token = la;
724                 
725                 node_sub = qualifier_relation(cclp, ap);
726                 if (!node_sub)
727                 {
728                     ccl_rpn_delete(node);
729                     xfree(ap);
730                     return 0;
731                 }
732                 if (node)
733                 {
734                     struct ccl_rpn_node *node_this = 
735                         ccl_rpn_node_create(CCL_RPN_OR);
736                     node_this->u.p[0] = node;
737                     node_this->u.p[1] = node_sub;
738                     node = node_this;
739                 }
740                 else
741                     node = node_sub;
742                 seq++;
743             }
744             if (seq == 0)
745             {
746                 cclp->look_token = lookahead;
747                 cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
748                 xfree(ap);
749                 return NULL;
750             }
751             lookahead = lookahead->next;
752             if (lookahead->kind == CCL_TOK_COMMA)
753                 lookahead = lookahead->next;
754         }
755     }
756     else
757     {
758         /* merge attributes from ALL fields - including inherited ones */
759         while (1)
760         {
761             struct ccl_rpn_node *node_sub;
762             int found = 0;
763             lookahead = look_start;
764             for (i = 0; lookahead != la; i++)
765             {
766                 ap[i] = ccl_qual_search(cclp, lookahead->name,
767                                          lookahead->len, seq);
768                 if (ap[i])
769                     found++;
770                 if (!ap[i] && seq > 0)
771                     ap[i] = ccl_qual_search(cclp, lookahead->name,
772                                              lookahead->len, 0);
773                 if (!ap[i])
774                 {
775                     cclp->look_token = lookahead;
776                     cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
777                     xfree(ap);
778                     return NULL;
779                 }
780                 lookahead = lookahead->next;
781                 if (lookahead->kind == CCL_TOK_COMMA)
782                     lookahead = lookahead->next;
783             }
784             if (qa)
785             {
786                 ccl_qualifier_t *qa0 = qa;
787                 
788                 while (*qa0)
789                     ap[i++] = *qa0++;
790             }
791             ap[i] = NULL;
792             
793             if (!found)
794                 break;
795             
796             cclp->look_token = lookahead;
797             
798             node_sub = qualifier_relation(cclp, ap);
799             if (!node_sub)
800             {
801                 ccl_rpn_delete(node);
802                 break;
803             }
804             if (node)
805             {
806                 struct ccl_rpn_node *node_this = 
807                     ccl_rpn_node_create(CCL_RPN_OR);
808                 node_this->u.p[0] = node;
809                 node_this->u.p[1] = node_sub;
810                 node = node_this;
811             }
812             else
813                 node = node_sub;
814             seq++;
815         }
816     }
817     xfree(ap);
818     return node;
819 }
820
821
822 /**
823  * search_terms: Parse CCL search terms - including proximity.
824  * cclp:   CCL Parser
825  * qa:     Qualifier attributes already applied.
826  * return: pointer to node(s); NULL on error.
827  */
828 static struct ccl_rpn_node *search_terms(CCL_parser cclp, ccl_qualifier_t *qa)
829 {
830     static int list[] = {
831         CCL_TOK_TERM, CCL_TOK_COMMA,CCL_TOK_EQ, CCL_TOK_REL, CCL_TOK_SET, -1};
832     struct ccl_rpn_node *p1, *p2, *pn;
833     p1 = search_term_x(cclp, qa, list, 1);
834     if (!p1)
835         return NULL;
836     while (1)
837     {
838         if (KIND == CCL_TOK_PROX)
839         {
840             struct ccl_rpn_node *p_prox = 0;
841             /* ! word order specified */
842             /* % word order not specified */
843             p_prox = ccl_rpn_node_create(CCL_RPN_TERM);
844             p_prox->u.t.term = (char *) xmalloc(1 + cclp->look_token->len);
845             memcpy(p_prox->u.t.term, cclp->look_token->name,
846                    cclp->look_token->len);
847             p_prox->u.t.term[cclp->look_token->len] = 0;
848             p_prox->u.t.attr_list = 0;
849
850             ADVANCE;
851             p2 = search_term_x(cclp, qa, list, 1);
852             if (!p2)
853             {
854                 ccl_rpn_delete(p1);
855                 return NULL;
856             }
857             pn = ccl_rpn_node_create(CCL_RPN_PROX);
858             pn->u.p[0] = p1;
859             pn->u.p[1] = p2;
860             pn->u.p[2] = p_prox;
861             p1 = pn;
862         }
863         else if (is_term_ok(KIND, list))
864         {
865             p2 = search_term_x(cclp, qa, list, 1);
866             if (!p2)
867             {
868                 ccl_rpn_delete(p1);
869                 return NULL;
870             }
871             pn = ccl_rpn_node_create(CCL_RPN_PROX);
872             pn->u.p[0] = p1;
873             pn->u.p[1] = p2;
874             pn->u.p[2] = 0;
875             p1 = pn;
876         }
877         else
878             break;
879     }
880     return p1;
881 }
882
883 /**
884  * search_elements: Parse CCL search elements
885  * cclp:   CCL Parser
886  * qa:     Qualifier attributes already applied.
887  * return: pointer to node(s); NULL on error.
888  */
889 static struct ccl_rpn_node *search_elements(CCL_parser cclp,
890                                             ccl_qualifier_t *qa)
891 {
892     struct ccl_rpn_node *p1;
893     struct ccl_token *lookahead;
894     if (KIND == CCL_TOK_LP)
895     {
896         ADVANCE;
897         p1 = find_spec(cclp, qa);
898         if (!p1)
899             return NULL;
900         if (KIND != CCL_TOK_RP)
901         {
902             cclp->error_code = CCL_ERR_RP_EXPECTED;
903             ccl_rpn_delete(p1);
904             return NULL;
905         }
906         ADVANCE;
907         return p1;
908     }
909     else if (KIND == CCL_TOK_SET)
910     {
911         ADVANCE;
912         if (KIND == CCL_TOK_EQ)
913             ADVANCE;
914         if (KIND != CCL_TOK_TERM)
915         {
916             cclp->error_code = CCL_ERR_SETNAME_EXPECTED;
917             return NULL;
918         }
919         p1 = ccl_rpn_node_create(CCL_RPN_SET);
920         p1->u.setname = copy_token_name(cclp->look_token);
921         ADVANCE;
922         return p1;
923     }
924     lookahead = cclp->look_token;
925
926     while (lookahead->kind==CCL_TOK_TERM)
927     {
928         lookahead = lookahead->next;
929         if (lookahead->kind == CCL_TOK_REL || lookahead->kind == CCL_TOK_EQ)
930             return qualifier_list(cclp, lookahead, qa);
931         if (lookahead->kind != CCL_TOK_COMMA)
932             break;
933         lookahead = lookahead->next;
934     }
935     if (qa)
936         return search_terms(cclp, qa);
937     else
938     {
939         ccl_qualifier_t qa[2];
940         struct ccl_rpn_node *node = 0;
941         int seq;
942         lookahead = cclp->look_token;
943
944         qa[1] = 0;
945         for(seq = 0; ;seq++)
946         {
947             struct ccl_rpn_node *node_sub;
948             qa[0] = ccl_qual_search(cclp, "term", 4, seq);
949             if (!qa[0])
950                 break;
951
952             cclp->look_token = lookahead;
953
954             node_sub = search_terms(cclp, qa);
955             if (!node_sub)
956             {
957                 ccl_rpn_delete(node);
958                 return 0;
959             }
960             if (node)
961             {
962                 struct ccl_rpn_node *node_this = 
963                     ccl_rpn_node_create(CCL_RPN_OR);
964                 node_this->u.p[0] = node;
965                 node_this->u.p[1] = node_sub;
966                 node_this->u.p[2] = 0;
967                 node = node_this;
968             }
969             else
970                 node = node_sub;
971         }
972         if (!node)
973             node = search_terms(cclp, 0);
974         return node;
975     }
976 }
977
978 /**
979  * find_spec: Parse CCL find specification
980  * cclp:   CCL Parser
981  * qa:     Qualifier attributes already applied.
982  * return: pointer to node(s); NULL on error.
983  */
984 static struct ccl_rpn_node *find_spec(CCL_parser cclp, ccl_qualifier_t *qa)
985 {
986     struct ccl_rpn_node *p1, *p2, *pn;
987     if (!(p1 = search_elements(cclp, qa)))
988         return NULL;
989     while (1)
990     {
991         switch (KIND)
992         {
993         case CCL_TOK_AND:
994             ADVANCE;
995             p2 = search_elements(cclp, qa);
996             if (!p2)
997             {
998                 ccl_rpn_delete(p1);
999                 return NULL;
1000             }
1001             pn = ccl_rpn_node_create(CCL_RPN_AND);
1002             pn->u.p[0] = p1;
1003             pn->u.p[1] = p2;
1004             pn->u.p[2] = 0;
1005             p1 = pn;
1006             continue;
1007         case CCL_TOK_OR:
1008             ADVANCE;
1009             p2 = search_elements(cclp, qa);
1010             if (!p2)
1011             {
1012                 ccl_rpn_delete(p1);
1013                 return NULL;
1014             }
1015             pn = ccl_rpn_node_create(CCL_RPN_OR);
1016             pn->u.p[0] = p1;
1017             pn->u.p[1] = p2;
1018             pn->u.p[2] = 0;
1019             p1 = pn;
1020             continue;
1021         case CCL_TOK_NOT:
1022             ADVANCE;
1023             p2 = search_elements(cclp, qa);
1024             if (!p2)
1025             {
1026                 ccl_rpn_delete(p1);
1027                 return NULL;
1028             }
1029             pn = ccl_rpn_node_create(CCL_RPN_NOT);
1030             pn->u.p[0] = p1;
1031             pn->u.p[1] = p2;
1032             pn->u.p[2] = 0;
1033             p1 = pn;
1034             continue;
1035         }
1036         break;
1037     }
1038     return p1;
1039 }
1040
1041 struct ccl_rpn_node *ccl_parser_find_str(CCL_parser cclp, const char *str)
1042 {
1043     struct ccl_rpn_node *p;
1044     struct ccl_token *list = ccl_parser_tokenize(cclp, str);
1045     p = ccl_parser_find_token(cclp, list);
1046     ccl_token_del(list);
1047     return p;
1048 }
1049
1050 struct ccl_rpn_node *ccl_parser_find_token(CCL_parser cclp, 
1051                                            struct ccl_token *list)
1052 {
1053     struct ccl_rpn_node *p;
1054
1055     cclp->look_token = list;
1056     p = find_spec(cclp, NULL);
1057     if (p && KIND != CCL_TOK_EOL)
1058     {
1059         if (KIND == CCL_TOK_RP)
1060             cclp->error_code = CCL_ERR_BAD_RP;
1061         else
1062             cclp->error_code = CCL_ERR_OP_EXPECTED;
1063         ccl_rpn_delete(p);
1064         p = NULL;
1065     }
1066     cclp->error_pos = cclp->look_token->name;
1067     if (p)
1068         cclp->error_code = CCL_ERR_OK;
1069     else
1070         cclp->error_code = cclp->error_code;
1071     return p;
1072 }
1073
1074 /**
1075  * ccl_find_str: Parse CCL find - string representation
1076  * bibset:  Bibset to be used for the parsing
1077  * str:     String to be parsed
1078  * error:   Pointer to integer. Holds error no. on completion.
1079  * pos:     Pointer to char position. Holds approximate error position.
1080  * return:  RPN tree on successful completion; NULL otherwise.
1081  */
1082 struct ccl_rpn_node *ccl_find_str(CCL_bibset bibset, const char *str,
1083                                   int *error, int *pos)
1084 {
1085     CCL_parser cclp = ccl_parser_create(bibset);
1086     struct ccl_token *list;
1087     struct ccl_rpn_node *p;
1088
1089     list = ccl_parser_tokenize(cclp, str);
1090     p = ccl_parser_find_token(cclp, list);
1091
1092     *error = cclp->error_code;
1093     if (*error)
1094         *pos = cclp->error_pos - str;
1095     ccl_parser_destroy(cclp);
1096     ccl_token_del(list);
1097     return p;
1098 }
1099
1100 /*
1101  * Local variables:
1102  * c-basic-offset: 4
1103  * c-file-style: "Stroustrup"
1104  * indent-tabs-mode: nil
1105  * End:
1106  * vim: shiftwidth=4 tabstop=8 expandtab
1107  */
1108