Fixes for numeric ranges (date=1980-1990).
[yaz-moved-to-github.git] / ccl / 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  * $Log: cclfind.c,v $
48  * Revision 1.25  2001-10-03 23:54:41  adam
49  * Fixes for numeric ranges (date=1980-1990).
50  *
51  * Revision 1.24  2001/03/22 21:23:30  adam
52  * Directive s=pw sets structure to phrase if term includes blank(s).
53  *
54  * Revision 1.23  2001/03/20 11:22:58  adam
55  * CCL Truncation character may be defined.
56  *
57  * Revision 1.22  2001/03/07 13:24:40  adam
58  * Member and_not in Z_Operator is kept for backwards compatibility.
59  * Added support for definition of CCL operators in field spec file.
60  *
61  * Revision 1.21  2001/02/21 13:46:53  adam
62  * C++ fixes.
63  *
64  * Revision 1.20  2000/11/16 13:03:12  adam
65  * Function ccl_rpn_query sets attributeSet to Bib-1.
66  *
67  * Revision 1.19  2000/11/16 09:58:02  adam
68  * Implemented local AttributeSet setting for CCL field maps.
69  *
70  * Revision 1.18  2000/10/17 19:50:28  adam
71  * Implemented and-list and or-list for CCL module.
72  *
73  * Revision 1.17  2000/05/01 09:36:50  adam
74  * Range operator only treated in ordered ranges so that minus (-) can be
75  * used for, say, the and-not operator.
76  *
77  * Revision 1.16  2000/03/14 09:06:11  adam
78  * Added POSIX threads support for frontend server.
79  *
80  * Revision 1.15  2000/02/24 23:49:13  adam
81  * Fixed memory allocation problem.
82  *
83  * Revision 1.14  2000/01/31 13:15:21  adam
84  * Removed uses of assert(3). Cleanup of ODR. CCL parser update so
85  * that some characters are not surrounded by spaces in resulting term.
86  * ILL-code updates.
87  *
88  * Revision 1.13  1999/12/22 13:13:32  adam
89  * Search terms may include "operators" without causing error.
90  *
91  * Revision 1.12  1999/11/30 13:47:11  adam
92  * Improved installation. Moved header files to include/yaz.
93  *
94  * Revision 1.11  1999/03/31 11:15:37  adam
95  * Fixed memory leaks in ccl_find_str and ccl_qual_rm.
96  *
97  * Revision 1.10  1998/02/11 11:53:33  adam
98  * Changed code so that it compiles as C++.
99  *
100  * Revision 1.9  1997/09/29 08:56:37  adam
101  * Changed CCL parser to be thread safe. New type, CCL_parser, declared
102  * and a create/destructers ccl_parser_create/ccl_parser/destory has
103  * been added.
104  *
105  * Revision 1.8  1997/09/01 08:48:11  adam
106  * New windows NT/95 port using MSV5.0. Only a few changes made
107  * to avoid warnings.
108  *
109  * Revision 1.7  1997/05/14 06:53:26  adam
110  * C++ support.
111  *
112  * Revision 1.6  1997/04/30 08:52:06  quinn
113  * Null
114  *
115  * Revision 1.5  1996/10/11  15:00:24  adam
116  * CCL parser from Europagate Email gateway 1.0.
117  *
118  * Revision 1.16  1996/01/08  08:41:13  adam
119  * Removed unused function.
120  *
121  * Revision 1.15  1995/07/20  08:14:34  adam
122  * Qualifiers were observed too often. Instead tokens are treated as
123  * qualifiers only when separated by comma.
124  *
125  * Revision 1.14  1995/05/16  09:39:26  adam
126  * LICENSE.
127  *
128  * Revision 1.13  1995/04/17  09:31:42  adam
129  * Improved handling of qualifiers. Aliases or reserved words.
130  *
131  * Revision 1.12  1995/03/20  15:27:43  adam
132  * Minor changes.
133  *
134  * Revision 1.11  1995/02/23  08:31:59  adam
135  * Changed header.
136  *
137  * Revision 1.9  1995/02/16  13:20:06  adam
138  * Spell fix.
139  *
140  * Revision 1.8  1995/02/14  19:59:42  adam
141  * Removed a syntax error.
142  *
143  * Revision 1.7  1995/02/14  19:55:10  adam
144  * Header files ccl.h/cclp.h are gone! They have been merged an
145  * moved to ../include/ccl.h.
146  * Node kind(s) in ccl_rpn_node have changed names.
147  *
148  * Revision 1.6  1995/02/14  16:20:55  adam
149  * Qualifiers are read from a file now.
150  *
151  * Revision 1.5  1995/02/14  14:12:41  adam
152  * Ranges for ordered qualfiers implemented (e.g. pd=1980-1990).
153  *
154  * Revision 1.4  1995/02/14  13:16:29  adam
155  * Left and/or right truncation implemented.
156  *
157  * Revision 1.3  1995/02/14  10:25:56  adam
158  * The constructions 'qualifier rel term ...' implemented.
159  *
160  * Revision 1.2  1995/02/13  15:15:07  adam
161  * Added handling of qualifiers. Not finished yet.
162  *
163  * Revision 1.1  1995/02/13  12:35:20  adam
164  * First version of CCL. Qualifiers aren't handled yet.
165  *
166  */
167
168 #include <stdlib.h>
169 #include <string.h>
170
171 #include <yaz/ccl.h>
172
173 /* returns type of current lookahead */
174 #define KIND (cclp->look_token->kind)
175
176 /* move one token forward */
177 #define ADVANCE cclp->look_token = cclp->look_token->next
178
179 /* 
180  * qual_val_type: test for existance of attribute type/value pair.
181  * qa:     Attribute array
182  * type:   Type of attribute to search for
183  * value:  Value of attribute to seach for
184  * return: 1 if found; 0 otherwise.
185  */
186 static int qual_val_type (struct ccl_rpn_attr **qa, int type, int value,
187                            char **attset)
188 {
189     int i;
190     struct ccl_rpn_attr *q;
191
192     if (!qa)
193         return 0;
194     for (i = 0;  (q=qa[i]); i++)
195         while (q)
196         {
197             if (q->type == type && q->value == value)
198             {
199                 if (attset)
200                     *attset = q->set;
201                 return 1;
202             }
203             q = q->next;
204         }
205     return 0;
206 }
207
208 /*
209  * strxcat: concatenate strings.
210  * n:      Null-terminated Destination string 
211  * src:    Source string to be appended (not null-terminated)
212  * len:    Length of source string.
213  */
214 static void strxcat (char *n, const char *src, int len)
215 {
216     while (*n)
217         n++;
218     while (--len >= 0)
219         *n++ = *src++;
220     *n = '\0';
221 }
222
223 /*
224  * copy_token_name: Return copy of CCL token name
225  * tp:      Pointer to token info.
226  * return:  malloc(3) allocated copy of token name.
227  */
228 static char *copy_token_name (struct ccl_token *tp)
229 {
230     char *str = (char *)malloc (tp->len + 1);
231     ccl_assert (str);
232     memcpy (str, tp->name, tp->len);
233     str[tp->len] = '\0';
234     return str;
235 }
236
237 /*
238  * mk_node: Create RPN node.
239  * kind:   Type of node.
240  * return: pointer to allocated node.
241  */
242 static struct ccl_rpn_node *mk_node (int kind)
243 {
244     struct ccl_rpn_node *p;
245     p = (struct ccl_rpn_node *)malloc (sizeof(*p));
246     ccl_assert (p);
247     p->kind = kind;
248     return p;
249 }
250
251 /*
252  * ccl_rpn_delete: Delete RPN tree.
253  * rpn:   Pointer to tree.
254  */
255 void ccl_rpn_delete (struct ccl_rpn_node *rpn)
256 {
257     struct ccl_rpn_attr *attr, *attr1;
258     if (!rpn)
259         return;
260     switch (rpn->kind)
261     {
262     case CCL_RPN_AND:
263     case CCL_RPN_OR:
264     case CCL_RPN_NOT:
265         ccl_rpn_delete (rpn->u.p[0]);
266         ccl_rpn_delete (rpn->u.p[1]);
267         break;
268     case CCL_RPN_TERM:
269         free (rpn->u.t.term);
270         for (attr = rpn->u.t.attr_list; attr; attr = attr1)
271         {
272             attr1 = attr->next;
273             if (attr->set)
274                 free (attr->set);
275             free (attr);
276         }
277         break;
278     case CCL_RPN_SET:
279         free (rpn->u.setname);
280         break;
281     case CCL_RPN_PROX:
282         ccl_rpn_delete (rpn->u.p[0]);
283         ccl_rpn_delete (rpn->u.p[1]);
284         break;
285     }
286     free (rpn);
287 }
288
289 static struct ccl_rpn_node *find_spec (CCL_parser cclp,
290                                        struct ccl_rpn_attr **qa);
291
292 static int is_term_ok (int look, int *list)
293 {
294     for (;*list >= 0; list++)
295         if (look == *list)
296             return 1;
297     return 0;
298 }
299
300 static struct ccl_rpn_node *search_terms (CCL_parser cclp,
301                                           struct ccl_rpn_attr **qa);
302
303 /*
304  * add_attr: Add attribute (type/value) to RPN term node.
305  * p:     RPN node of type term.
306  * type:  Type of attribute
307  * value: Value of attribute
308  * set: Attribute set name
309  */
310 static void add_attr (struct ccl_rpn_node *p, const char *set,
311                       int type, int value)
312 {
313     struct ccl_rpn_attr *n;
314
315     n = (struct ccl_rpn_attr *)malloc (sizeof(*n));
316     ccl_assert (n);
317     if (set)
318     {
319         n->set = (char*) malloc (strlen(set)+1);
320         strcpy (n->set, set);
321     }
322     else
323         n->set = 0;
324     n->type = type;
325     n->value = value;
326     n->next = p->u.t.attr_list;
327     p->u.t.attr_list = n;
328 }
329
330 /*
331  * search_term: Parse CCL search term. 
332  * cclp:   CCL Parser
333  * qa:     Qualifier attributes already applied.
334  * term_list: tokens we accept as terms in context
335  * multi:  whether we accept "multiple" tokens
336  * return: pointer to node(s); NULL on error.
337  */
338 static struct ccl_rpn_node *search_term_x (CCL_parser cclp,
339                                            struct ccl_rpn_attr **qa,
340                                            int *term_list, int multi)
341 {
342     struct ccl_rpn_attr *qa_tmp[2];
343     struct ccl_rpn_node *p_top = 0;
344     struct ccl_token *lookahead = cclp->look_token;
345     int and_list = 0;
346     int or_list = 0;
347     char *attset;
348     const char *truncation_aliases;
349
350     truncation_aliases =
351         ccl_qual_search_special(cclp->bibset, "truncation");
352     if (!truncation_aliases)
353         truncation_aliases = "?";
354
355     if (!qa)
356     {
357         /* no qualifier(s) applied. Use 'term' if it is defined */
358         
359         qa = qa_tmp;
360         ccl_assert (qa);
361         qa[0] = ccl_qual_search (cclp, "term", 4);
362         qa[1] = NULL;
363     }
364     if (qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_AND_LIST, 0))
365         and_list = 1;
366     if (qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_OR_LIST, 0))
367         or_list = 1;
368     while (1)
369     {
370         struct ccl_rpn_node *p;
371         size_t no, i;
372         int no_spaces = 0;
373         int left_trunc = 0;
374         int right_trunc = 0;
375         int mid_trunc = 0;
376         int relation_value = -1;
377         int position_value = -1;
378         int structure_value = -1;
379         int truncation_value = -1;
380         int completeness_value = -1;
381         int len = 0;
382         size_t max = 200;
383         if (and_list || or_list || !multi)
384             max = 1;
385
386         /* go through each TERM token. If no truncation attribute is yet
387            met, then look for left/right truncation markers (?) and
388            set left_trunc/right_trunc/mid_trunc accordingly */
389         for (no = 0; no < max && is_term_ok(lookahead->kind, term_list); no++)
390         {
391             for (i = 0; i<lookahead->len; i++)
392                 if (lookahead->name[i] == ' ')
393                     no_spaces++;
394                 else if (strchr(truncation_aliases, lookahead->name[i]))
395                 {
396                     if (no == 0 && i == 0 && lookahead->len >= 1)
397                         left_trunc = 1;
398                     else if (!is_term_ok(lookahead->next->kind, term_list) &&
399                              i == lookahead->len-1 && i >= 1)
400                         right_trunc = 1;
401                     else
402                         mid_trunc = 1;
403                 }
404             len += 1+lookahead->len;
405             lookahead = lookahead->next;
406         }
407
408         if (len == 0)
409             break;      /* no more terms . stop . */
410
411         if (p_top)
412         {
413             if (or_list)
414                 p = mk_node (CCL_RPN_OR);
415             else if (and_list)
416                 p = mk_node (CCL_RPN_AND);
417             else
418                 p = mk_node (CCL_RPN_AND);
419             p->u.p[0] = p_top;
420             p_top = p;
421         }
422                 
423         /* create the term node, but wait a moment before adding the term */
424         p = mk_node (CCL_RPN_TERM);
425         p->u.t.attr_list = NULL;
426         p->u.t.term = NULL;
427
428         /* make the top node point to us.. */
429         if (p_top)
430             p_top->u.p[1] = p;
431         else
432             p_top = p;
433
434         
435         /* go through all attributes and add them to the attribute list */
436         for (i=0; qa && qa[i]; i++)
437         {
438             struct ccl_rpn_attr *attr;
439             
440             for (attr = qa[i]; attr; attr = attr->next)
441                 if (attr->value > 0)
442                 {   /* deal only with REAL attributes (positive) */
443                     switch (attr->type)
444                     {
445                     case CCL_BIB1_REL:
446                         if (relation_value != -1)
447                             continue;
448                         relation_value = attr->value;
449                         break;
450                     case CCL_BIB1_POS:
451                         if (position_value != -1)
452                             continue;
453                         position_value = attr->value;
454                         break;
455                     case CCL_BIB1_STR:
456                         if (structure_value != -1)
457                             continue;
458                         structure_value = attr->value;
459                         break;
460                     case CCL_BIB1_TRU:
461                         if (truncation_value != -1)
462                             continue;
463                         truncation_value = attr->value;
464                         left_trunc = right_trunc = mid_trunc = 0;
465                         break;
466                     case CCL_BIB1_COM:
467                         if (completeness_value != -1)
468                             continue;
469                         completeness_value = attr->value;
470                         break;
471                     }
472                     add_attr (p, attr->set, attr->type, attr->value);
473             }
474         }
475         /* len now holds the number of characters in the RPN term */
476         /* no holds the number of CCL tokens (1 or more) */
477         
478         if (structure_value == -1 && 
479             qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_WP, &attset))
480         {   /* no structure attribute met. Apply either structure attribute 
481                WORD or PHRASE depending on number of CCL tokens */
482             if (no == 1 && no_spaces == 0)
483                 add_attr (p, attset, CCL_BIB1_STR, 2);
484             else
485                 add_attr (p, attset, CCL_BIB1_STR, 1);
486         }
487         
488         /* make the RPN token */
489         p->u.t.term = (char *)malloc (len);
490         ccl_assert (p->u.t.term);
491         p->u.t.term[0] = '\0';
492         for (i = 0; i<no; i++)
493         {
494             const char *src_str = cclp->look_token->name;
495             int src_len = cclp->look_token->len;
496             
497             if (i == 0 && left_trunc)
498             {
499                 src_len--;
500                 src_str++;
501             }
502             else if (i == no-1 && right_trunc)
503                 src_len--;
504             if (src_len)
505             {
506                 int len = strlen(p->u.t.term);
507                 if (len &&
508                     !strchr("-+", *src_str) &&
509                     !strchr("-+", p->u.t.term[len-1]))
510                 {
511                     strcat (p->u.t.term, " ");
512                 }
513             }
514             strxcat (p->u.t.term, src_str, src_len);
515             ADVANCE;
516         }
517         if (left_trunc && right_trunc)
518         {
519             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_BOTH,
520                                 &attset))
521             {
522                 cclp->error_code = CCL_ERR_TRUNC_NOT_BOTH;
523                 ccl_rpn_delete (p);
524                 return NULL;
525             }
526             add_attr (p, attset, CCL_BIB1_TRU, 3);
527         }
528         else if (right_trunc)
529         {
530             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_RIGHT,
531                                  &attset))
532             {
533                 cclp->error_code = CCL_ERR_TRUNC_NOT_RIGHT;
534                 ccl_rpn_delete (p);
535                 return NULL;
536             }
537             add_attr (p, attset, CCL_BIB1_TRU, 1);
538         }
539         else if (left_trunc)
540         {
541             if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_LEFT,
542                                 &attset))
543             {
544                 cclp->error_code = CCL_ERR_TRUNC_NOT_LEFT;
545                 ccl_rpn_delete (p);
546                 return NULL;
547             }
548             add_attr (p, attset, CCL_BIB1_TRU, 2);
549         }
550         else
551         {
552             if (qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_NONE,
553                                &attset))
554                 add_attr (p, attset, CCL_BIB1_TRU, 100);
555         }
556         if (!multi)
557             break;
558     }
559     if (!p_top)
560         cclp->error_code = CCL_ERR_TERM_EXPECTED;
561     return p_top;
562 }
563
564 static struct ccl_rpn_node *search_term (CCL_parser cclp,
565                                          struct ccl_rpn_attr **qa)
566 {
567     static int list[] = {CCL_TOK_TERM, CCL_TOK_COMMA, -1};
568     return search_term_x(cclp, qa, list, 0);
569 }
570
571 /*
572  * qualifiers: Parse CCL qualifiers and search terms. 
573  * cclp:   CCL Parser
574  * la:     Token pointer to RELATION token.
575  * qa:     Qualifier attributes already applied.
576  * return: pointer to node(s); NULL on error.
577  */
578 static struct ccl_rpn_node *qualifiers (CCL_parser cclp, struct ccl_token *la,
579                                         struct ccl_rpn_attr **qa)
580 {
581     struct ccl_token *lookahead = cclp->look_token;
582     struct ccl_rpn_attr **ap;
583     int no = 0;
584     int i, rel;
585     char *attset;
586 #if 0
587     if (qa)
588     {
589         cclp->error_code = CCL_ERR_DOUBLE_QUAL;
590         return NULL;
591     }
592 #endif
593     for (lookahead = cclp->look_token; lookahead != la;
594          lookahead=lookahead->next)
595         no++;
596     if (qa)
597         for (i=0; qa[i]; i++)
598             no++;
599     ap = (struct ccl_rpn_attr **)malloc ((no+1) * sizeof(*ap));
600     ccl_assert (ap);
601     for (i = 0; cclp->look_token != la; i++)
602     {
603         ap[i] = ccl_qual_search (cclp, cclp->look_token->name,
604                                  cclp->look_token->len);
605         if (!ap[i])
606         {
607             cclp->error_code = CCL_ERR_UNKNOWN_QUAL;
608             free (ap);
609             return NULL;
610         }
611         ADVANCE;
612         if (KIND == CCL_TOK_COMMA)
613             ADVANCE;
614     }
615     if (qa)
616         while (*qa)
617             ap[i++] = *qa++;
618     ap[i] = NULL;
619     if (!qual_val_type(ap, CCL_BIB1_REL, CCL_BIB1_REL_ORDER, &attset))
620     {                
621         /* unordered relation */
622         struct ccl_rpn_node *p;
623         if (KIND != CCL_TOK_EQ)
624         {
625             cclp->error_code = CCL_ERR_EQ_EXPECTED;
626             free (ap);
627             return NULL;
628         }
629         ADVANCE;
630         if (KIND == CCL_TOK_LP)
631         {
632             ADVANCE;
633             if (!(p = find_spec (cclp, ap)))
634             {
635                 free (ap);
636                 return NULL;
637             }
638             if (KIND != CCL_TOK_RP)
639             {
640                 cclp->error_code = CCL_ERR_RP_EXPECTED;
641                 ccl_rpn_delete (p);
642                 free (ap);
643                 return NULL;
644             }
645             ADVANCE;
646         }
647         else
648             p = search_terms (cclp, ap);
649         free (ap);
650         return p;
651     }
652     /* ordered relation ... */
653     rel = 0;
654     if (cclp->look_token->len == 1)
655     {
656         if (cclp->look_token->name[0] == '<')
657             rel = 1;
658         else if (cclp->look_token->name[0] == '=')
659             rel = 3;
660         else if (cclp->look_token->name[0] == '>')
661             rel = 5;
662     }
663     else if (cclp->look_token->len == 2)
664     {
665         if (!memcmp (cclp->look_token->name, "<=", 2))
666             rel = 2;
667         else if (!memcmp (cclp->look_token->name, ">=", 2))
668             rel = 4;
669         else if (!memcmp (cclp->look_token->name, "<>", 2))
670             rel = 6;
671     }
672     if (!rel)
673         cclp->error_code = CCL_ERR_BAD_RELATION;
674     else
675     {
676         struct ccl_rpn_node *p;
677
678         ADVANCE;                      /* skip relation */
679         if (KIND == CCL_TOK_TERM &&
680             cclp->look_token->next && cclp->look_token->next->len == 1 &&
681             cclp->look_token->next->name[0] == '-')
682         {
683             struct ccl_rpn_node *p1;
684             if (!(p1 = search_term (cclp, ap)))
685             {
686                 free (ap);
687                 return NULL;
688             }
689             ADVANCE;                   /* skip '-' */
690             if (KIND == CCL_TOK_TERM)  /* = term - term  ? */
691             {
692                 struct ccl_rpn_node *p2;
693                 
694                 if (!(p2 = search_term (cclp, ap)))
695                 {
696                     ccl_rpn_delete (p1);
697                     free (ap);
698                     return NULL;
699                 }
700                 p = mk_node (CCL_RPN_AND);
701                 p->u.p[0] = p1;
702                 add_attr (p1, attset, CCL_BIB1_REL, 4);
703                 p->u.p[1] = p2;
704                 add_attr (p2, attset, CCL_BIB1_REL, 2);
705                 free (ap);
706                 return p;
707             }
708             else                       /* = term -    */
709             {
710                 add_attr (p1, attset, CCL_BIB1_REL, 4);
711                 free (ap);
712                 return p1;
713             }
714         }
715         else if (cclp->look_token->len == 1 &&
716                  cclp->look_token->name[0] == '"')   /* = - term  ? */
717         {
718             ADVANCE;
719             if (!(p = search_term (cclp, ap)))
720             {
721                 free (ap);
722                 return NULL;
723             }
724             add_attr (p, attset, CCL_BIB1_REL, 2);
725             free (ap);
726             return p;
727         }
728         else if (KIND == CCL_TOK_LP)
729         {
730             ADVANCE;
731             if (!(p = find_spec (cclp, ap)))
732             {
733                 free (ap);
734                 return NULL;
735             }
736             if (KIND != CCL_TOK_RP)
737             {
738                 cclp->error_code = CCL_ERR_RP_EXPECTED;
739                 ccl_rpn_delete (p);
740                 free (ap);
741                 return NULL;
742             }
743             ADVANCE;
744             free (ap);
745             return p;
746         }
747         else
748         {
749             if (!(p = search_terms (cclp, ap)))
750             {
751                 free (ap);
752                 return NULL;
753             }
754             add_attr (p, attset, CCL_BIB1_REL, rel);
755             free (ap);
756             return p;
757         }
758         cclp->error_code = CCL_ERR_TERM_EXPECTED;
759     }
760     free (ap);
761     return NULL;
762 }
763
764 /*
765  * search_terms: Parse CCL search terms - including proximity.
766  * cclp:   CCL Parser
767  * qa:     Qualifier attributes already applied.
768  * return: pointer to node(s); NULL on error.
769  */
770 static struct ccl_rpn_node *search_terms (CCL_parser cclp,
771                                           struct ccl_rpn_attr **qa)
772 {
773     static int list[] = {
774         CCL_TOK_TERM, CCL_TOK_COMMA,CCL_TOK_EQ, CCL_TOK_REL, -1};
775     struct ccl_rpn_node *p1, *p2, *pn;
776     p1 = search_term_x (cclp, qa, list, 1);
777     if (!p1)
778         return NULL;
779     while (1)
780     {
781         if (KIND == CCL_TOK_PROX)
782         {
783             ADVANCE;
784             p2 = search_term_x (cclp, qa, list, 1);
785             if (!p2)
786             {
787                 ccl_rpn_delete (p1);
788                 return NULL;
789             }
790             pn = mk_node (CCL_RPN_PROX);
791             pn->u.p[0] = p1;
792             pn->u.p[1] = p2;
793             p1 = pn;
794         }
795         else if (is_term_ok(KIND, list))
796         {
797             p2 = search_term_x (cclp, qa, list, 1);
798             if (!p2)
799             {
800                 ccl_rpn_delete (p1);
801                 return NULL;
802             }
803             pn = mk_node (CCL_RPN_PROX);
804             pn->u.p[0] = p1;
805             pn->u.p[1] = p2;
806             p1 = pn;
807         }
808         else
809             break;
810     }
811     return p1;
812 }
813
814 /*
815  * search_elements: Parse CCL search elements
816  * cclp:   CCL Parser
817  * qa:     Qualifier attributes already applied.
818  * return: pointer to node(s); NULL on error.
819  */
820 static struct ccl_rpn_node *search_elements (CCL_parser cclp,
821                                              struct ccl_rpn_attr **qa)
822 {
823     struct ccl_rpn_node *p1;
824     struct ccl_token *lookahead;
825     if (KIND == CCL_TOK_LP)
826     {
827         ADVANCE;
828         p1 = find_spec (cclp, qa);
829         if (!p1)
830             return NULL;
831         if (KIND != CCL_TOK_RP)
832         {
833             cclp->error_code = CCL_ERR_RP_EXPECTED;
834             ccl_rpn_delete (p1);
835             return NULL;
836         }
837         ADVANCE;
838         return p1;
839     }
840     else if (KIND == CCL_TOK_SET)
841     {
842         ADVANCE;
843         if (KIND == CCL_TOK_EQ)
844             ADVANCE;
845         if (KIND != CCL_TOK_TERM)
846         {
847             cclp->error_code = CCL_ERR_SETNAME_EXPECTED;
848             return NULL;
849         }
850         p1 = mk_node (CCL_RPN_SET);
851         p1->u.setname = copy_token_name (cclp->look_token);
852         ADVANCE;
853         return p1;
854     }
855     lookahead = cclp->look_token;
856
857     while (lookahead->kind==CCL_TOK_TERM)
858     {
859         lookahead = lookahead->next;
860         if (lookahead->kind == CCL_TOK_REL || lookahead->kind == CCL_TOK_EQ)
861             return qualifiers (cclp, lookahead, qa);
862         if (lookahead->kind != CCL_TOK_COMMA)
863             break;
864         lookahead = lookahead->next;
865     }
866     return search_terms (cclp, qa);
867 }
868
869 /*
870  * find_spec: Parse CCL find specification
871  * cclp:   CCL Parser
872  * qa:     Qualifier attributes already applied.
873  * return: pointer to node(s); NULL on error.
874  */
875 static struct ccl_rpn_node *find_spec (CCL_parser cclp,
876                                        struct ccl_rpn_attr **qa)
877 {
878     struct ccl_rpn_node *p1, *p2, *pn;
879     if (!(p1 = search_elements (cclp, qa)))
880         return NULL;
881     while (1)
882     {
883         switch (KIND)
884         {
885         case CCL_TOK_AND:
886             ADVANCE;
887             p2 = search_elements (cclp, qa);
888             if (!p2)
889             {
890                 ccl_rpn_delete (p1);
891                 return NULL;
892             }
893             pn = mk_node (CCL_RPN_AND);
894             pn->u.p[0] = p1;
895             pn->u.p[1] = p2;
896             p1 = pn;
897             continue;
898         case CCL_TOK_OR:
899             ADVANCE;
900             p2 = search_elements (cclp, qa);
901             if (!p2)
902             {
903                 ccl_rpn_delete (p1);
904                 return NULL;
905             }
906             pn = mk_node (CCL_RPN_OR);
907             pn->u.p[0] = p1;
908             pn->u.p[1] = p2;
909             p1 = pn;
910             continue;
911         case CCL_TOK_NOT:
912             ADVANCE;
913             p2 = search_elements (cclp, qa);
914             if (!p2)
915             {
916                 ccl_rpn_delete (p1);
917                 return NULL;
918             }
919             pn = mk_node (CCL_RPN_NOT);
920             pn->u.p[0] = p1;
921             pn->u.p[1] = p2;
922             p1 = pn;
923             continue;
924         }
925         break;
926     }
927     return p1;
928 }
929
930 struct ccl_rpn_node *ccl_parser_find (CCL_parser cclp, struct ccl_token *list)
931 {
932     struct ccl_rpn_node *p;
933
934     
935
936     cclp->look_token = list;
937     p = find_spec (cclp, NULL);
938     if (p && KIND != CCL_TOK_EOL)
939     {
940         if (KIND == CCL_TOK_RP)
941             cclp->error_code = CCL_ERR_BAD_RP;
942         else
943             cclp->error_code = CCL_ERR_OP_EXPECTED;
944         ccl_rpn_delete (p);
945         p = NULL;
946     }
947     cclp->error_pos = cclp->look_token->name;
948     if (p)
949         cclp->error_code = CCL_ERR_OK;
950     else
951         cclp->error_code = cclp->error_code;
952     return p;
953 }
954
955 /*
956  * ccl_find: Parse CCL find - token representation
957  * bibset:  Bibset to be used for the parsing
958  * list:    List of tokens
959  * error:   Pointer to integer. Holds error no. on completion.
960  * pos:     Pointer to char position. Holds approximate error position.
961  * return:  RPN tree on successful completion; NULL otherwise.
962  */
963 struct ccl_rpn_node *ccl_find (CCL_bibset bibset, struct ccl_token *list,
964                                int *error, const char **pos)
965 {
966     struct ccl_rpn_node *p;
967     CCL_parser cclp = ccl_parser_create ();
968
969     cclp->bibset = bibset;
970
971     p = ccl_parser_find (cclp, list);
972
973     *error = cclp->error_code;
974     *pos = cclp->error_pos;
975
976     ccl_parser_destroy (cclp);
977
978     return p;
979 }
980
981 /*
982  * ccl_find_str: Parse CCL find - string representation
983  * bibset:  Bibset to be used for the parsing
984  * str:     String to be parsed
985  * error:   Pointer to integer. Holds error no. on completion.
986  * pos:     Pointer to char position. Holds approximate error position.
987  * return:  RPN tree on successful completion; NULL otherwise.
988  */
989 struct ccl_rpn_node *ccl_find_str (CCL_bibset bibset, const char *str,
990                                    int *error, int *pos)
991 {
992     CCL_parser cclp = ccl_parser_create ();
993     struct ccl_token *list;
994     struct ccl_rpn_node *p;
995
996     cclp->bibset = bibset;
997
998     list = ccl_parser_tokenize (cclp, str);
999     p = ccl_parser_find (cclp, list);
1000
1001     *error = cclp->error_code;
1002     if (*error)
1003         *pos = cclp->error_pos - str;
1004     ccl_parser_destroy (cclp);
1005     ccl_token_del (list);
1006     return p;
1007 }