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