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