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