bef1d81e5c30a65cbb7591f75bd45c0472aaa4a9
[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.5 2004-10-10 21:43:21 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         ccl_rpn_delete (rpn->u.p[2]);
221         break;
222     }
223     xfree (rpn);
224 }
225
226 static struct ccl_rpn_node *find_spec (CCL_parser cclp,
227                                        struct ccl_rpn_attr **qa);
228
229 static int is_term_ok (int look, int *list)
230 {
231     for (;*list >= 0; list++)
232         if (look == *list)
233             return 1;
234     return 0;
235 }
236
237 static struct ccl_rpn_node *search_terms (CCL_parser cclp,
238                                           struct ccl_rpn_attr **qa);
239
240 static struct ccl_rpn_attr *add_attr_node (struct ccl_rpn_node *p,
241                                            const char *set, int type)
242 {
243     struct ccl_rpn_attr *n;
244     
245     n = (struct ccl_rpn_attr *)xmalloc (sizeof(*n));
246     ccl_assert (n);
247     if (set)
248     {
249         n->set = (char*) xmalloc (strlen(set)+1);
250         strcpy (n->set, set);
251     }
252     else
253         n->set = 0;
254     n->type = type;
255     n->next = p->u.t.attr_list;
256     p->u.t.attr_list = n;
257     
258     n->kind = CCL_RPN_ATTR_NUMERIC;
259     n->value.numeric = 0;
260     return n;
261 }
262
263 /*
264  * add_attr_numeric: Add attribute (type/value) to RPN term node.
265  * p:     RPN node of type term.
266  * type:  Type of attribute
267  * value: Value of attribute
268  * set: Attribute set name
269  */
270 static void add_attr_numeric (struct ccl_rpn_node *p, const char *set,
271                               int type, int value)
272 {
273     struct ccl_rpn_attr *n;
274
275     n = add_attr_node(p, set, type);
276     n->kind = CCL_RPN_ATTR_NUMERIC;
277     n->value.numeric = value;
278 }
279
280 static void add_attr_string (struct ccl_rpn_node *p, const char *set,
281                              int type, char *value)
282 {
283     struct ccl_rpn_attr *n;
284
285     n = add_attr_node(p, set, type);
286     n->kind = CCL_RPN_ATTR_STRING;
287     n->value.str = xstrdup(value);
288 }
289
290
291 /*
292  * search_term: Parse CCL search term. 
293  * cclp:   CCL Parser
294  * qa:     Qualifier attributes already applied.
295  * term_list: tokens we accept as terms in context
296  * multi:  whether we accept "multiple" tokens
297  * return: pointer to node(s); NULL on error.
298  */
299 static struct ccl_rpn_node *search_term_x (CCL_parser cclp,
300                                            struct ccl_rpn_attr **qa,
301                                            int *term_list, int multi)
302 {
303     struct ccl_rpn_node *p_top = 0;
304     struct ccl_token *lookahead = cclp->look_token;
305     int and_list = 0;
306     int or_list = 0;
307     char *attset;
308     const char *truncation_aliases;
309
310     truncation_aliases =
311         ccl_qual_search_special(cclp->bibset, "truncation");
312     if (!truncation_aliases)
313         truncation_aliases = "?";
314
315     if (qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_AND_LIST, 0))
316         and_list = 1;
317     if (qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_OR_LIST, 0))
318         or_list = 1;
319     while (1)
320     {
321         struct ccl_rpn_node *p;
322         size_t no, i;
323         int no_spaces = 0;
324         int left_trunc = 0;
325         int right_trunc = 0;
326         int mid_trunc = 0;
327         int relation_value = -1;
328         int position_value = -1;
329         int structure_value = -1;
330         int truncation_value = -1;
331         int completeness_value = -1;
332         int len = 0;
333         size_t max = 200;
334         if (and_list || or_list || !multi)
335             max = 1;
336         
337         /* ignore commas when dealing with and-lists .. */
338         if (and_list && lookahead && lookahead->kind == CCL_TOK_COMMA)
339         {
340             lookahead = lookahead->next;
341             ADVANCE;
342             continue;
343         }
344         /* go through each TERM token. If no truncation attribute is yet
345            met, then look for left/right truncation markers (?) and
346            set left_trunc/right_trunc/mid_trunc accordingly */
347         for (no = 0; no < max && is_term_ok(lookahead->kind, term_list); no++)
348         {
349             for (i = 0; i<lookahead->len; i++)
350                 if (lookahead->name[i] == ' ')
351                     no_spaces++;
352                 else if (strchr(truncation_aliases, lookahead->name[i]))
353                 {
354                     if (no == 0 && i == 0 && lookahead->len >= 1)
355                         left_trunc = 1;
356                     else if (!is_term_ok(lookahead->next->kind, term_list) &&
357                              i == lookahead->len-1 && i >= 1)
358                         right_trunc = 1;
359                     else
360                         mid_trunc = 1;
361                 }
362             len += 1+lookahead->len;
363             lookahead = lookahead->next;
364         }
365
366         if (len == 0)
367             break;      /* no more terms . stop . */
368
369
370         if (p_top)
371         {
372             if (or_list)
373                 p = mk_node (CCL_RPN_OR);
374             else if (and_list)
375                 p = mk_node (CCL_RPN_AND);
376             else
377                 p = mk_node (CCL_RPN_AND);
378             p->u.p[0] = p_top;
379             p_top = p;
380         }
381                 
382         /* create the term node, but wait a moment before adding the term */
383         p = mk_node (CCL_RPN_TERM);
384         p->u.t.attr_list = NULL;
385         p->u.t.term = NULL;
386
387         /* make the top node point to us.. */
388         if (p_top)
389             p_top->u.p[1] = p;
390         else
391             p_top = p;
392
393         
394         /* go through all attributes and add them to the attribute list */
395         for (i=0; qa && qa[i]; i++)
396         {
397             struct ccl_rpn_attr *attr;
398             
399             for (attr = qa[i]; attr; attr = attr->next)
400                 switch(attr->kind)
401                 {
402                 case CCL_RPN_ATTR_STRING:
403                     add_attr_string(p, attr->set, attr->type,
404                                     attr->value.str);
405                     break;
406                 case CCL_RPN_ATTR_NUMERIC:
407                     if (attr->value.numeric > 0)
408                     {   /* deal only with REAL attributes (positive) */
409                         switch (attr->type)
410                         {
411                         case CCL_BIB1_REL:
412                             if (relation_value != -1)
413                                 continue;
414                             relation_value = attr->value.numeric;
415                             break;
416                         case CCL_BIB1_POS:
417                             if (position_value != -1)
418                                 continue;
419                             position_value = attr->value.numeric;
420                             break;
421                         case CCL_BIB1_STR:
422                             if (structure_value != -1)
423                                 continue;
424                             structure_value = attr->value.numeric;
425                             break;
426                         case CCL_BIB1_TRU:
427                             if (truncation_value != -1)
428                                 continue;
429                             truncation_value = attr->value.numeric;
430                             left_trunc = right_trunc = mid_trunc = 0;
431                             break;
432                         case CCL_BIB1_COM:
433                             if (completeness_value != -1)
434                                 continue;
435                             completeness_value = attr->value.numeric;
436                             break;
437                         }
438                         add_attr_numeric(p, attr->set, attr->type,
439                                          attr->value.numeric);
440                     }
441                 }
442         }
443         /* len now holds the number of characters in the RPN term */
444         /* no holds the number of CCL tokens (1 or more) */
445         
446         if (structure_value == -1 && 
447             qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_WP, &attset))
448         {   /* no structure attribute met. Apply either structure attribute 
449                WORD or PHRASE depending on number of CCL tokens */
450             if (no == 1 && no_spaces == 0)
451                 add_attr_numeric (p, attset, CCL_BIB1_STR, 2);
452             else
453                 add_attr_numeric (p, attset, CCL_BIB1_STR, 1);
454         }
455
456         /* make the RPN token */
457         p->u.t.term = (char *)xmalloc (len);
458         ccl_assert (p->u.t.term);
459         p->u.t.term[0] = '\0';
460         for (i = 0; i<no; i++)
461         {
462             const char *src_str = cclp->look_token->name;
463             int src_len = cclp->look_token->len;
464             
465             if (i == 0 && left_trunc)
466             {
467                 src_len--;
468                 src_str++;
469             }
470             if (i == no-1 && right_trunc)
471                 src_len--;
472             if (src_len)
473             {
474                 int len = strlen(p->u.t.term);
475                 if (len &&
476                     !strchr("-+", *src_str) &&
477                     !strchr("-+", p->u.t.term[len-1]))
478                 {
479                     strcat (p->u.t.term, " ");
480                 }
481             }
482             strxcat (p->u.t.term, src_str, src_len);
483             ADVANCE;
484         }
485         if (left_trunc && right_trunc)
486         {
487             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_BOTH,
488                                 &attset))
489             {
490                 cclp->error_code = CCL_ERR_TRUNC_NOT_BOTH;
491                 ccl_rpn_delete (p);
492                 return NULL;
493             }
494             add_attr_numeric (p, attset, CCL_BIB1_TRU, 3);
495         }
496         else if (right_trunc)
497         {
498             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_RIGHT,
499                                  &attset))
500             {
501                 cclp->error_code = CCL_ERR_TRUNC_NOT_RIGHT;
502                 ccl_rpn_delete (p);
503                 return NULL;
504             }
505             add_attr_numeric (p, attset, CCL_BIB1_TRU, 1);
506         }
507         else if (left_trunc)
508         {
509             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_LEFT,
510                                 &attset))
511             {
512                 cclp->error_code = CCL_ERR_TRUNC_NOT_LEFT;
513                 ccl_rpn_delete (p);
514                 return NULL;
515             }
516             add_attr_numeric (p, attset, CCL_BIB1_TRU, 2);
517         }
518         else
519         {
520             if (qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_NONE,
521                                &attset))
522                 add_attr_numeric (p, attset, CCL_BIB1_TRU, 100);
523         }
524         if (!multi)
525             break;
526     }
527     if (!p_top)
528         cclp->error_code = CCL_ERR_TERM_EXPECTED;
529     return p_top;
530 }
531
532 static struct ccl_rpn_node *search_term (CCL_parser cclp,
533                                          struct ccl_rpn_attr **qa)
534 {
535     static int list[] = {CCL_TOK_TERM, CCL_TOK_COMMA, -1};
536     return search_term_x(cclp, qa, list, 0);
537 }
538
539 static
540 struct ccl_rpn_node *qualifiers_order (CCL_parser cclp,
541                                        struct ccl_rpn_attr **ap, char *attset)
542 {
543     int rel = 0;
544     struct ccl_rpn_node *p;
545
546     if (cclp->look_token->len == 1)
547     {
548         if (cclp->look_token->name[0] == '<')
549             rel = 1;
550         else if (cclp->look_token->name[0] == '=')
551             rel = 3;
552         else if (cclp->look_token->name[0] == '>')
553             rel = 5;
554     }
555     else if (cclp->look_token->len == 2)
556     {
557         if (!memcmp (cclp->look_token->name, "<=", 2))
558             rel = 2;
559         else if (!memcmp (cclp->look_token->name, ">=", 2))
560             rel = 4;
561         else if (!memcmp (cclp->look_token->name, "<>", 2))
562             rel = 6;
563     }
564     if (!rel)
565     {
566         cclp->error_code = CCL_ERR_BAD_RELATION;
567         return NULL;
568     }
569     ADVANCE;  /* skip relation */
570     if (rel == 3 &&
571         qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, 0))
572     {
573         /* allow - inside term and treat it as range _always_ */
574         /* relation is =. Extract "embedded" - to separate terms */
575         if (KIND == CCL_TOK_TERM)
576         {
577             size_t i;
578             for (i = 0; i<cclp->look_token->len; i++)
579             {
580                 if (cclp->look_token->name[i] == '-')
581                     break;
582             }
583             
584             if (cclp->look_token->len > 1 && i == 0)
585             {   /*  -xx*/
586                 struct ccl_token *ntoken = ccl_token_add (cclp->look_token);
587
588                 ntoken->kind = CCL_TOK_TERM;
589                 ntoken->name = cclp->look_token->name + 1;
590                 ntoken->len = cclp->look_token->len - 1;
591
592                 cclp->look_token->len = 1;
593                 cclp->look_token->name = "-";
594             }
595             else if (cclp->look_token->len > 1 && i == cclp->look_token->len-1)
596             {   /* xx- */
597                 struct ccl_token *ntoken = ccl_token_add (cclp->look_token);
598
599                 ntoken->kind = CCL_TOK_TERM;
600                 ntoken->name = "-";
601                 ntoken->len = 1;
602
603                 (cclp->look_token->len)--;
604             }
605             else if (cclp->look_token->len > 2 && i < cclp->look_token->len)
606             {   /* xx-yy */
607                 struct ccl_token *ntoken1 = ccl_token_add (cclp->look_token);
608                 struct ccl_token *ntoken2 = ccl_token_add (ntoken1);
609
610                 ntoken1->kind = CCL_TOK_TERM;  /* generate - */
611                 ntoken1->name = "-";
612                 ntoken1->len = 1;
613
614                 ntoken2->kind = CCL_TOK_TERM;  /* generate yy */
615                 ntoken2->name = cclp->look_token->name + (i+1);
616                 ntoken2->len = cclp->look_token->len - (i+1);
617
618                 cclp->look_token->len = i;     /* adjust xx */
619             }
620             else if (i == cclp->look_token->len &&
621                      cclp->look_token->next &&
622                      cclp->look_token->next->kind == CCL_TOK_TERM &&
623                      cclp->look_token->next->len > 1 &&
624                      cclp->look_token->next->name[0] == '-')
625                      
626             {   /* xx -yy */
627                 /* we _know_ that xx does not have - in it */
628                 struct ccl_token *ntoken = ccl_token_add (cclp->look_token);
629
630                 ntoken->kind = CCL_TOK_TERM;    /* generate - */
631                 ntoken->name = "-";
632                 ntoken->len = 1;
633
634                 (ntoken->next->name)++;        /* adjust yy */
635                 (ntoken->next->len)--; 
636             }
637         }
638     }
639         
640     if (rel == 3 &&
641         KIND == CCL_TOK_TERM &&
642         cclp->look_token->next && cclp->look_token->next->len == 1 &&
643         cclp->look_token->next->name[0] == '-')
644     {
645         struct ccl_rpn_node *p1;
646         if (!(p1 = search_term (cclp, ap)))
647             return NULL;
648         ADVANCE;                   /* skip '-' */
649         if (KIND == CCL_TOK_TERM)  /* = term - term  ? */
650         {
651             struct ccl_rpn_node *p2;
652             
653             if (!(p2 = search_term (cclp, ap)))
654             {
655                 ccl_rpn_delete (p1);
656                 return NULL;
657             }
658             p = mk_node (CCL_RPN_AND);
659             p->u.p[0] = p1;
660             add_attr_numeric (p1, attset, CCL_BIB1_REL, 4);
661             p->u.p[1] = p2;
662             add_attr_numeric (p2, attset, CCL_BIB1_REL, 2);
663             return p;
664         }
665         else                       /* = term -    */
666         {
667             add_attr_numeric (p1, attset, CCL_BIB1_REL, 4);
668             return p1;
669         }
670     }
671     else if (rel == 3 &&
672              cclp->look_token->len == 1 &&
673              cclp->look_token->name[0] == '-')   /* = - term  ? */
674     {
675         ADVANCE;
676         if (!(p = search_term (cclp, ap)))
677             return NULL;
678         add_attr_numeric (p, attset, CCL_BIB1_REL, 2);
679         return p;
680     }
681     else if (KIND == CCL_TOK_LP)
682     {
683         ADVANCE;
684         if (!(p = find_spec (cclp, ap)))
685             return NULL;
686         if (KIND != CCL_TOK_RP)
687         {
688             cclp->error_code = CCL_ERR_RP_EXPECTED;
689             ccl_rpn_delete (p);
690             return NULL;
691         }
692         ADVANCE;
693         return p;
694     }
695     else
696     {
697         if (!(p = search_terms (cclp, ap)))
698             return NULL;
699         add_attr_numeric (p, attset, CCL_BIB1_REL, rel);
700         return p;
701     }
702     cclp->error_code = CCL_ERR_TERM_EXPECTED;
703     return NULL;
704 }
705
706 static
707 struct ccl_rpn_node *qualifiers2 (CCL_parser cclp, struct ccl_rpn_attr **ap)
708 {
709     char *attset;
710     struct ccl_rpn_node *p;
711     
712     if (qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_ORDER, &attset)
713         || qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_PORDER, &attset))
714         return qualifiers_order(cclp, ap, attset);
715
716     /* unordered relation */
717     if (KIND != CCL_TOK_EQ)
718     {
719         cclp->error_code = CCL_ERR_EQ_EXPECTED;
720         return NULL;
721     }
722     ADVANCE;
723     if (KIND == CCL_TOK_LP)
724     {
725         ADVANCE;
726         if (!(p = find_spec (cclp, ap)))
727         {
728             return NULL;
729         }
730         if (KIND != CCL_TOK_RP)
731         {
732             cclp->error_code = CCL_ERR_RP_EXPECTED;
733             ccl_rpn_delete (p);
734             return NULL;
735         }
736         ADVANCE;
737     }
738     else
739         p = search_terms (cclp, ap);
740     return p;
741 }
742
743 /*
744  * qualifiers1: Parse CCL qualifiers and search terms. 
745  * cclp:   CCL Parser
746  * la:     Token pointer to RELATION token.
747  * qa:     Qualifier attributes already applied.
748  * return: pointer to node(s); NULL on error.
749  */
750 static struct ccl_rpn_node *qualifiers1 (CCL_parser cclp, struct ccl_token *la,
751                                          struct ccl_rpn_attr **qa)
752 {
753     struct ccl_token *lookahead = cclp->look_token;
754     struct ccl_token *look_start = cclp->look_token;
755     struct ccl_rpn_attr **ap;
756     struct ccl_rpn_node *node = 0;
757     const char *field_str;
758     int no = 0;
759     int seq = 0;
760     int i;
761     int mode_merge = 1;
762 #if 0
763     if (qa)
764     {
765         cclp->error_code = CCL_ERR_DOUBLE_QUAL;
766         return NULL;
767     }
768 #endif
769     for (lookahead = cclp->look_token; lookahead != la;
770          lookahead=lookahead->next)
771         no++;
772     if (qa)
773         for (i=0; qa[i]; i++)
774             no++;
775     ap = (struct ccl_rpn_attr **)xmalloc ((no ? (no+1) : 2) * sizeof(*ap));
776     ccl_assert (ap);
777
778     field_str = ccl_qual_search_special(cclp->bibset, "field");
779     if (field_str)
780     {
781         if (!strcmp (field_str, "or"))
782             mode_merge = 0;
783         else if (!strcmp (field_str, "merge"))
784             mode_merge = 1;
785     }
786     if (!mode_merge)
787     {
788         /* consider each field separately and OR */
789         lookahead = look_start;
790         while (lookahead != la)
791         {
792             ap[1] = 0;
793             seq = 0;
794             while ((ap[0] = ccl_qual_search (cclp, lookahead->name,
795                                              lookahead->len, seq)) != 0)
796             {
797                 struct ccl_rpn_node *node_sub;
798                 cclp->look_token = la;
799                 
800                 node_sub = qualifiers2(cclp, ap);
801                 if (!node_sub)
802                 {
803                     ccl_rpn_delete (node);
804                     xfree (ap);
805                     return 0;
806                 }
807                 if (node)
808                 {
809                     struct ccl_rpn_node *node_this = mk_node(CCL_RPN_OR);
810                     node_this->u.p[0] = node;
811                     node_this->u.p[1] = node_sub;
812                     node = node_this;
813                 }
814                 else
815                     node = node_sub;
816                 seq++;
817             }
818             if (seq == 0)
819             {
820                 cclp->look_token = lookahead;
821                 cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
822                 xfree (ap);
823                 return NULL;
824             }
825             lookahead = lookahead->next;
826             if (lookahead->kind == CCL_TOK_COMMA)
827                 lookahead = lookahead->next;
828         }
829     }
830     else
831     {
832         /* merge attributes from ALL fields - including inherited ones */
833         while (1)
834         {
835             struct ccl_rpn_node *node_sub;
836             int found = 0;
837             lookahead = look_start;
838             for (i = 0; lookahead != la; i++)
839             {
840                 ap[i] = ccl_qual_search (cclp, lookahead->name,
841                                          lookahead->len, seq);
842                 if (ap[i])
843                     found++;
844                 if (!ap[i] && seq > 0)
845                     ap[i] = ccl_qual_search (cclp, lookahead->name,
846                                              lookahead->len, 0);
847                 if (!ap[i])
848                 {
849                     cclp->look_token = lookahead;
850                     cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
851                     xfree (ap);
852                     return NULL;
853                 }
854                 lookahead = lookahead->next;
855                 if (lookahead->kind == CCL_TOK_COMMA)
856                     lookahead = lookahead->next;
857             }
858             if (qa)
859             {
860                 struct ccl_rpn_attr **qa0 = qa;
861                 
862                 while (*qa0)
863                     ap[i++] = *qa0++;
864             }
865             ap[i] = NULL;
866             
867             if (!found)
868                 break;
869             
870             cclp->look_token = lookahead;
871             
872             node_sub = qualifiers2(cclp, ap);
873             if (!node_sub)
874             {
875                 ccl_rpn_delete (node);
876                 break;
877             }
878             if (node)
879             {
880                 struct ccl_rpn_node *node_this = mk_node(CCL_RPN_OR);
881                 node_this->u.p[0] = node;
882                 node_this->u.p[1] = node_sub;
883                 node = node_this;
884             }
885             else
886                 node = node_sub;
887             seq++;
888         }
889     }
890     xfree (ap);
891     return node;
892 }
893
894
895 /*
896  * search_terms: Parse CCL search terms - including proximity.
897  * cclp:   CCL Parser
898  * qa:     Qualifier attributes already applied.
899  * return: pointer to node(s); NULL on error.
900  */
901 static struct ccl_rpn_node *search_terms (CCL_parser cclp,
902                                           struct ccl_rpn_attr **qa)
903 {
904     static int list[] = {
905         CCL_TOK_TERM, CCL_TOK_COMMA,CCL_TOK_EQ, CCL_TOK_REL, CCL_TOK_SET, -1};
906     struct ccl_rpn_node *p1, *p2, *pn;
907     p1 = search_term_x (cclp, qa, list, 1);
908     if (!p1)
909         return NULL;
910     while (1)
911     {
912         if (KIND == CCL_TOK_PROX)
913         {
914             struct ccl_rpn_node *p_prox = 0;
915             /* ! word order specified */
916             /* % word order not specified */
917             p_prox = mk_node(CCL_RPN_TERM);
918             p_prox->u.t.term = (char *) xmalloc(1 + cclp->look_token->len);
919             memcpy(p_prox->u.t.term, cclp->look_token->name,
920                    cclp->look_token->len);
921             p_prox->u.t.term[cclp->look_token->len] = 0;
922             p_prox->u.t.attr_list = 0;
923
924             ADVANCE;
925             p2 = search_term_x (cclp, qa, list, 1);
926             if (!p2)
927             {
928                 ccl_rpn_delete (p1);
929                 return NULL;
930             }
931             pn = mk_node (CCL_RPN_PROX);
932             pn->u.p[0] = p1;
933             pn->u.p[1] = p2;
934             pn->u.p[2] = p_prox;
935             p1 = pn;
936         }
937         else if (is_term_ok(KIND, list))
938         {
939             p2 = search_term_x (cclp, qa, list, 1);
940             if (!p2)
941             {
942                 ccl_rpn_delete (p1);
943                 return NULL;
944             }
945             pn = mk_node (CCL_RPN_PROX);
946             pn->u.p[0] = p1;
947             pn->u.p[1] = p2;
948             pn->u.p[2] = 0;
949             p1 = pn;
950         }
951         else
952             break;
953     }
954     return p1;
955 }
956
957 /*
958  * search_elements: Parse CCL search elements
959  * cclp:   CCL Parser
960  * qa:     Qualifier attributes already applied.
961  * return: pointer to node(s); NULL on error.
962  */
963 static struct ccl_rpn_node *search_elements (CCL_parser cclp,
964                                              struct ccl_rpn_attr **qa)
965 {
966     struct ccl_rpn_node *p1;
967     struct ccl_token *lookahead;
968     if (KIND == CCL_TOK_LP)
969     {
970         ADVANCE;
971         p1 = find_spec (cclp, qa);
972         if (!p1)
973             return NULL;
974         if (KIND != CCL_TOK_RP)
975         {
976             cclp->error_code = CCL_ERR_RP_EXPECTED;
977             ccl_rpn_delete (p1);
978             return NULL;
979         }
980         ADVANCE;
981         return p1;
982     }
983     else if (KIND == CCL_TOK_SET)
984     {
985         ADVANCE;
986         if (KIND == CCL_TOK_EQ)
987             ADVANCE;
988         if (KIND != CCL_TOK_TERM)
989         {
990             cclp->error_code = CCL_ERR_SETNAME_EXPECTED;
991             return NULL;
992         }
993         p1 = mk_node (CCL_RPN_SET);
994         p1->u.setname = copy_token_name (cclp->look_token);
995         ADVANCE;
996         return p1;
997     }
998     lookahead = cclp->look_token;
999
1000     while (lookahead->kind==CCL_TOK_TERM)
1001     {
1002         lookahead = lookahead->next;
1003         if (lookahead->kind == CCL_TOK_REL || lookahead->kind == CCL_TOK_EQ)
1004             return qualifiers1 (cclp, lookahead, qa);
1005         if (lookahead->kind != CCL_TOK_COMMA)
1006             break;
1007         lookahead = lookahead->next;
1008     }
1009     if (qa)
1010         return search_terms (cclp, qa);
1011     else
1012     {
1013         struct ccl_rpn_attr *qa[2];
1014         struct ccl_rpn_node *node = 0;
1015         int seq;
1016         lookahead = cclp->look_token;
1017
1018         qa[1] = 0;
1019         for(seq = 0; ;seq++)
1020         {
1021             struct ccl_rpn_node *node_sub;
1022             qa[0] = ccl_qual_search(cclp, "term", 4, seq);
1023             if (!qa[0])
1024                 break;
1025
1026             cclp->look_token = lookahead;
1027
1028             node_sub = search_terms (cclp, qa);
1029             if (!node_sub)
1030             {
1031                 ccl_rpn_delete (node);
1032                 return 0;
1033             }
1034             if (node)
1035             {
1036                 struct ccl_rpn_node *node_this = mk_node(CCL_RPN_OR);
1037                 node_this->u.p[0] = node;
1038                 node_this->u.p[1] = node_sub;
1039                 node_this->u.p[2] = 0;
1040                 node = node_this;
1041             }
1042             else
1043                 node = node_sub;
1044         }
1045         if (!node)
1046             node = search_terms (cclp, 0);
1047         return node;
1048     }
1049 }
1050
1051 /*
1052  * find_spec: Parse CCL find specification
1053  * cclp:   CCL Parser
1054  * qa:     Qualifier attributes already applied.
1055  * return: pointer to node(s); NULL on error.
1056  */
1057 static struct ccl_rpn_node *find_spec (CCL_parser cclp,
1058                                        struct ccl_rpn_attr **qa)
1059 {
1060     struct ccl_rpn_node *p1, *p2, *pn;
1061     if (!(p1 = search_elements (cclp, qa)))
1062         return NULL;
1063     while (1)
1064     {
1065         switch (KIND)
1066         {
1067         case CCL_TOK_AND:
1068             ADVANCE;
1069             p2 = search_elements (cclp, qa);
1070             if (!p2)
1071             {
1072                 ccl_rpn_delete (p1);
1073                 return NULL;
1074             }
1075             pn = mk_node (CCL_RPN_AND);
1076             pn->u.p[0] = p1;
1077             pn->u.p[1] = p2;
1078             pn->u.p[2] = 0;
1079             p1 = pn;
1080             continue;
1081         case CCL_TOK_OR:
1082             ADVANCE;
1083             p2 = search_elements (cclp, qa);
1084             if (!p2)
1085             {
1086                 ccl_rpn_delete (p1);
1087                 return NULL;
1088             }
1089             pn = mk_node (CCL_RPN_OR);
1090             pn->u.p[0] = p1;
1091             pn->u.p[1] = p2;
1092             pn->u.p[2] = 0;
1093             p1 = pn;
1094             continue;
1095         case CCL_TOK_NOT:
1096             ADVANCE;
1097             p2 = search_elements (cclp, qa);
1098             if (!p2)
1099             {
1100                 ccl_rpn_delete (p1);
1101                 return NULL;
1102             }
1103             pn = mk_node (CCL_RPN_NOT);
1104             pn->u.p[0] = p1;
1105             pn->u.p[1] = p2;
1106             pn->u.p[2] = 0;
1107             p1 = pn;
1108             continue;
1109         }
1110         break;
1111     }
1112     return p1;
1113 }
1114
1115 struct ccl_rpn_node *ccl_parser_find (CCL_parser cclp, struct ccl_token *list)
1116 {
1117     struct ccl_rpn_node *p;
1118
1119     cclp->look_token = list;
1120     p = find_spec (cclp, NULL);
1121     if (p && KIND != CCL_TOK_EOL)
1122     {
1123         if (KIND == CCL_TOK_RP)
1124             cclp->error_code = CCL_ERR_BAD_RP;
1125         else
1126             cclp->error_code = CCL_ERR_OP_EXPECTED;
1127         ccl_rpn_delete (p);
1128         p = NULL;
1129     }
1130     cclp->error_pos = cclp->look_token->name;
1131     if (p)
1132         cclp->error_code = CCL_ERR_OK;
1133     else
1134         cclp->error_code = cclp->error_code;
1135     return p;
1136 }
1137
1138 /*
1139  * ccl_find: Parse CCL find - token representation
1140  * bibset:  Bibset to be used for the parsing
1141  * list:    List of tokens
1142  * error:   Pointer to integer. Holds error no. on completion.
1143  * pos:     Pointer to char position. Holds approximate error position.
1144  * return:  RPN tree on successful completion; NULL otherwise.
1145  */
1146 struct ccl_rpn_node *ccl_find (CCL_bibset bibset, struct ccl_token *list,
1147                                int *error, const char **pos)
1148 {
1149     struct ccl_rpn_node *p;
1150     CCL_parser cclp = ccl_parser_create ();
1151
1152     cclp->bibset = bibset;
1153
1154     p = ccl_parser_find (cclp, list);
1155
1156     *error = cclp->error_code;
1157     *pos = cclp->error_pos;
1158
1159     ccl_parser_destroy (cclp);
1160
1161     return p;
1162 }
1163
1164 /*
1165  * ccl_find_str: Parse CCL find - string representation
1166  * bibset:  Bibset to be used for the parsing
1167  * str:     String to be parsed
1168  * error:   Pointer to integer. Holds error no. on completion.
1169  * pos:     Pointer to char position. Holds approximate error position.
1170  * return:  RPN tree on successful completion; NULL otherwise.
1171  */
1172 struct ccl_rpn_node *ccl_find_str (CCL_bibset bibset, const char *str,
1173                                    int *error, int *pos)
1174 {
1175     CCL_parser cclp = ccl_parser_create ();
1176     struct ccl_token *list;
1177     struct ccl_rpn_node *p;
1178
1179     cclp->bibset = bibset;
1180
1181     list = ccl_parser_tokenize (cclp, str);
1182     p = ccl_parser_find (cclp, list);
1183
1184     *error = cclp->error_code;
1185     if (*error)
1186         *pos = cclp->error_pos - str;
1187     ccl_parser_destroy (cclp);
1188     ccl_token_del (list);
1189     return p;
1190 }