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