New windows NT/95 port using MSV5.0. Only a few changes made
[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.8  1997-09-01 08:48:11  adam
49  * New windows NT/95 port using MSV5.0. Only a few changes made
50  * to avoid warnings.
51  *
52  * Revision 1.7  1997/05/14 06:53:26  adam
53  * C++ support.
54  *
55  * Revision 1.6  1997/04/30 08:52:06  quinn
56  * Null
57  *
58  * Revision 1.5  1996/10/11  15:00:24  adam
59  * CCL parser from Europagate Email gateway 1.0.
60  *
61  * Revision 1.16  1996/01/08  08:41:13  adam
62  * Removed unused function.
63  *
64  * Revision 1.15  1995/07/20  08:14:34  adam
65  * Qualifiers were observed too often. Instead tokens are treated as
66  * qualifiers only when separated by comma.
67  *
68  * Revision 1.14  1995/05/16  09:39:26  adam
69  * LICENSE.
70  *
71  * Revision 1.13  1995/04/17  09:31:42  adam
72  * Improved handling of qualifiers. Aliases or reserved words.
73  *
74  * Revision 1.12  1995/03/20  15:27:43  adam
75  * Minor changes.
76  *
77  * Revision 1.11  1995/02/23  08:31:59  adam
78  * Changed header.
79  *
80  * Revision 1.9  1995/02/16  13:20:06  adam
81  * Spell fix.
82  *
83  * Revision 1.8  1995/02/14  19:59:42  adam
84  * Removed a syntax error.
85  *
86  * Revision 1.7  1995/02/14  19:55:10  adam
87  * Header files ccl.h/cclp.h are gone! They have been merged an
88  * moved to ../include/ccl.h.
89  * Node kind(s) in ccl_rpn_node have changed names.
90  *
91  * Revision 1.6  1995/02/14  16:20:55  adam
92  * Qualifiers are read from a file now.
93  *
94  * Revision 1.5  1995/02/14  14:12:41  adam
95  * Ranges for ordered qualfiers implemented (e.g. pd=1980-1990).
96  *
97  * Revision 1.4  1995/02/14  13:16:29  adam
98  * Left and/or right truncation implemented.
99  *
100  * Revision 1.3  1995/02/14  10:25:56  adam
101  * The constructions 'qualifier rel term ...' implemented.
102  *
103  * Revision 1.2  1995/02/13  15:15:07  adam
104  * Added handling of qualifiers. Not finished yet.
105  *
106  * Revision 1.1  1995/02/13  12:35:20  adam
107  * First version of CCL. Qualifiers aren't handled yet.
108  *
109  */
110
111 #include <stdio.h>
112 #include <stdlib.h>
113 #include <assert.h>
114 #include <string.h>
115
116 #include <ccl.h>
117
118 /* current lookahead token */
119 static struct ccl_token *look_token;
120
121 /* holds error no if error occur */
122 static int ccl_error;
123
124 /* current bibset */
125 static CCL_bibset bibset;
126
127 /* returns type of current lookahead */
128 #define KIND (look_token->kind)
129
130 /* move one token forward */
131 #define ADVANCE look_token = look_token->next
132
133 /* 
134  * qual_val_type: test for existance of attribute type/value pair.
135  * qa:     Attribute array
136  * type:   Type of attribute to search for
137  * value:  Value of attribute to seach for
138  * return: 1 if found; 0 otherwise.
139  */
140 static int qual_val_type (struct ccl_rpn_attr **qa, int type, int value)
141 {
142     int i;
143     struct ccl_rpn_attr *q;
144
145     if (!qa)
146         return 0;
147     for (i = 0;  (q=qa[i]); i++)
148         while (q)
149         {
150             if (q->type == type && q->value == value)
151                 return 1;
152             q = q->next;
153         }
154     return 0;
155 }
156
157 /*
158  * strxcat: concatenate strings.
159  * n:      Null-terminated Destination string 
160  * src:    Source string to be appended (not null-terminated)
161  * len:    Length of source string.
162  */
163 static void strxcat (char *n, const char *src, int len)
164 {
165     while (*n)
166         n++;
167     while (--len >= 0)
168         *n++ = *src++;
169     *n = '\0';
170 }
171
172 /*
173  * copy_token_name: Return copy of CCL token name
174  * tp:      Pointer to token info.
175  * return:  malloc(3) allocated copy of token name.
176  */
177 static char *copy_token_name (struct ccl_token *tp)
178 {
179     char *str = malloc (tp->len + 1);
180     assert (str);
181     memcpy (str, tp->name, tp->len);
182     str[tp->len] = '\0';
183     return str;
184 }
185
186 /*
187  * mk_node: Create RPN node.
188  * kind:   Type of node.
189  * return: pointer to allocated node.
190  */
191 static struct ccl_rpn_node *mk_node (int kind)
192 {
193     struct ccl_rpn_node *p;
194     p = malloc (sizeof(*p));
195     assert (p);
196     p->kind = kind;
197     return p;
198 }
199
200 /*
201  * ccl_rpn_delete: Delete RPN tree.
202  * rpn:   Pointer to tree.
203  */
204 void ccl_rpn_delete (struct ccl_rpn_node *rpn)
205 {
206     struct ccl_rpn_attr *attr, *attr1;
207     if (!rpn)
208         return;
209     switch (rpn->kind)
210     {
211     case CCL_RPN_AND:
212     case CCL_RPN_OR:
213     case CCL_RPN_NOT:
214         ccl_rpn_delete (rpn->u.p[0]);
215         ccl_rpn_delete (rpn->u.p[1]);
216         break;
217     case CCL_RPN_TERM:
218         free (rpn->u.t.term);
219         for (attr = rpn->u.t.attr_list; attr; attr = attr1)
220         {
221             attr1 = attr->next;
222             free (attr);
223         }
224         break;
225     case CCL_RPN_SET:
226         free (rpn->u.setname);
227         break;
228     case CCL_RPN_PROX:
229         ccl_rpn_delete (rpn->u.p[0]);
230         ccl_rpn_delete (rpn->u.p[1]);
231         break;
232     }
233     free (rpn);
234 }
235
236 static struct ccl_rpn_node *find_spec (struct ccl_rpn_attr **qa);
237 static struct ccl_rpn_node *search_terms (struct ccl_rpn_attr **qa);
238
239 /*
240  * add_attr: Add attribute (type/value) to RPN term node.
241  * p:     RPN node of type term.
242  * type:  Type of attribute
243  * value: Value of attribute
244  */
245 static void add_attr (struct ccl_rpn_node *p, int type, int value)
246 {
247     struct ccl_rpn_attr *n;
248
249     n = malloc (sizeof(*n));
250     assert (n);
251     n->type = type;
252     n->value = value;
253     n->next = p->u.t.attr_list;
254     p->u.t.attr_list = n;
255 }
256
257 /*
258  * search_term: Parse CCL search term. 
259  * qa:     Qualifier attributes already applied.
260  * return: pointer to node(s); NULL on error.
261  */
262 static struct ccl_rpn_node *search_term (struct ccl_rpn_attr **qa)
263 {
264     struct ccl_rpn_node *p;
265     struct ccl_token *lookahead = look_token;
266     int len = 0;
267     size_t no, i;
268     int left_trunc = 0;
269     int right_trunc = 0;
270     int mid_trunc = 0;
271     int relation_value = -1;
272     int position_value = -1;
273     int structure_value = -1;
274     int truncation_value = -1;
275     int completeness_value = -1;
276
277     if (KIND != CCL_TOK_TERM)
278     {
279         ccl_error = CCL_ERR_TERM_EXPECTED;
280         return NULL;
281     }
282     /* create the term node, but wait a moment before adding the term */
283     p = mk_node (CCL_RPN_TERM);
284     p->u.t.attr_list = NULL;
285     p->u.t.term = NULL;
286
287     if (!qa)
288     {
289         /* no qualifier(s) applied. Use 'term' if it is defined */
290
291         qa = malloc (2*sizeof(*qa));
292         assert (qa);
293         qa[0] = ccl_qual_search (bibset, "term", 4);
294         qa[1] = NULL;
295     }
296
297     /* go through all attributes and add them to the attribute list */
298     for (i=0; qa && qa[i]; i++)
299     {
300         struct ccl_rpn_attr *attr;
301
302         for (attr = qa[i]; attr; attr = attr->next)
303             if (attr->value > 0)
304             {   /* deal only with REAL attributes (positive) */
305                 switch (attr->type)
306                 {
307                 case CCL_BIB1_REL:
308                     if (relation_value != -1)
309                         continue;
310                     relation_value = attr->value;
311                     break;
312                 case CCL_BIB1_POS:
313                     if (position_value != -1)
314                         continue;
315                     position_value = attr->value;
316                     break;
317                 case CCL_BIB1_STR:
318                     if (structure_value != -1)
319                         continue;
320                     structure_value = attr->value;
321                     break;
322                 case CCL_BIB1_TRU:
323                     if (truncation_value != -1)
324                         continue;
325                     truncation_value = attr->value;
326                     break;
327                 case CCL_BIB1_COM:
328                     if (completeness_value != -1)
329                         continue;
330                     completeness_value = attr->value;
331                     break;
332                 }
333                 add_attr (p, attr->type, attr->value);
334             }
335     }
336     /* go through each TERM token. If no truncation attribute is yet
337        met, then look for left/right truncation markers (?) and
338        set left_trunc/right_trunc/mid_trunc accordingly */
339     for (no = 0; lookahead->kind == CCL_TOK_TERM; no++)
340     {
341         for (i = 0; i<lookahead->len; i++)
342             if (truncation_value == -1 && lookahead->name[i] == '?')
343             {
344                 if (no == 0 && i == 0 && lookahead->len >= 1)
345                     left_trunc = 1;
346                 else if (lookahead->next->kind != CCL_TOK_TERM &&
347                          i == lookahead->len-1 && i >= 1)
348                     right_trunc = 1;
349                 else
350                     mid_trunc = 1;
351             }
352         len += 1+lookahead->len;
353         lookahead = lookahead->next;
354     }
355     /* len now holds the number of characters in the RPN term */
356     /* no holds the number of CCL tokens (1 or more) */
357
358     if (structure_value == -1 && 
359         qual_val_type (qa, CCL_BIB1_STR, CCL_BIB1_STR_WP))
360     {   /* no structure attribute met. Apply either structure attribute 
361            WORD or PHRASE depending on number of CCL tokens */
362         if (no == 1)
363             add_attr (p, CCL_BIB1_STR, 2);
364         else
365             add_attr (p, CCL_BIB1_STR, 1);
366     }
367
368     /* make the RPN token */
369     p->u.t.term = malloc (len);
370     assert (p->u.t.term);
371     p->u.t.term[0] = '\0';
372     for (i = 0; i<no; i++)
373     {
374         const char *src_str = look_token->name;
375         int src_len = look_token->len;
376         
377         if (i == 0 && left_trunc)
378         {
379             src_len--;
380             src_str++;
381         }
382         else if (i == no-1 && right_trunc)
383             src_len--;
384         if (i)
385             strcat (p->u.t.term, " ");
386         strxcat (p->u.t.term, src_str, src_len);
387         ADVANCE;
388     }
389     if (left_trunc && right_trunc)
390     {
391         if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_BOTH))
392         {
393             ccl_error = CCL_ERR_TRUNC_NOT_BOTH;
394             free (qa);
395             ccl_rpn_delete (p);
396             return NULL;
397         }
398         add_attr (p, CCL_BIB1_TRU, 3);
399     }
400     else if (right_trunc)
401     {
402         if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_RIGHT))
403         {
404             ccl_error = CCL_ERR_TRUNC_NOT_RIGHT;
405             free (qa);
406             ccl_rpn_delete (p);
407             return NULL;
408         }
409         add_attr (p, CCL_BIB1_TRU, 1);
410     }
411     else if (left_trunc)
412     {
413         if (!qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_LEFT))
414         {
415             ccl_error = CCL_ERR_TRUNC_NOT_LEFT;
416             free (qa);
417             ccl_rpn_delete (p);
418             return NULL;
419         }
420         add_attr (p, CCL_BIB1_TRU, 2);
421     }
422     else
423     {
424         if (qual_val_type (qa, CCL_BIB1_TRU, CCL_BIB1_TRU_CAN_NONE))
425             add_attr (p, CCL_BIB1_TRU, 100);
426     }
427     return p;
428 }
429
430 /*
431  * qualifiers: Parse CCL qualifiers and search terms. 
432  * la:     Token pointer to RELATION token.
433  * qa:     Qualifier attributes already applied.
434  * return: pointer to node(s); NULL on error.
435  */
436 static struct ccl_rpn_node *qualifiers (struct ccl_token *la,
437                                         struct ccl_rpn_attr **qa)
438 {
439     struct ccl_token *lookahead = look_token;
440     struct ccl_rpn_attr **ap;
441     int no = 0;
442     int i, rel;
443 #if 0
444     if (qa)
445     {
446         ccl_error = CCL_ERR_DOUBLE_QUAL;
447         return NULL;
448     }
449 #endif
450     for (lookahead = look_token; lookahead != la; lookahead=lookahead->next)
451         no++;
452     if (qa)
453         for (i=0; qa[i]; i++)
454             no++;
455     ap = malloc ((no+1) * sizeof(*ap));
456     assert (ap);
457     for (i = 0; look_token != la; i++)
458     {
459         ap[i] = ccl_qual_search (bibset, look_token->name, look_token->len);
460         if (!ap[i])
461         {
462             ccl_error = CCL_ERR_UNKNOWN_QUAL;
463             free (ap);
464             return NULL;
465         }
466         ADVANCE;
467         if (KIND == CCL_TOK_COMMA)
468             ADVANCE;
469     }
470     if (qa)
471         while (*qa)
472             ap[i++] = *qa++;
473     ap[i] = NULL;
474     if (!qual_val_type (ap, CCL_BIB1_REL, CCL_BIB1_REL_ORDER))
475     {                
476         /* unordered relation */
477         struct ccl_rpn_node *p;
478         if (KIND != CCL_TOK_EQ)
479         {
480             ccl_error = CCL_ERR_EQ_EXPECTED;
481             free (ap);
482             return NULL;
483         }
484         ADVANCE;
485         if (KIND == CCL_TOK_LP)
486         {
487             ADVANCE;
488             if (!(p = find_spec (ap)))
489             {
490                 free (ap);
491                 return NULL;
492             }
493             if (KIND != CCL_TOK_RP)
494             {
495                 ccl_error = CCL_ERR_RP_EXPECTED;
496                 ccl_rpn_delete (p);
497                 free (ap);
498                 return NULL;
499             }
500             ADVANCE;
501         }
502         else
503             p = search_terms (ap);
504         free (ap);
505         return p;
506     }
507     rel = 0;
508     if (look_token->len == 1)
509     {
510         if (look_token->name[0] == '<')
511             rel = 1;
512         else if (look_token->name[0] == '=')
513             rel = 3;
514         else if (look_token->name[0] == '>')
515             rel = 5;
516     }
517     else if (look_token->len == 2)
518     {
519         if (!memcmp (look_token->name, "<=", 2))
520             rel = 2;
521         else if (!memcmp (look_token->name, ">=", 2))
522             rel = 4;
523         else if (!memcmp (look_token->name, "<>", 2))
524             rel = 6;
525     }
526     if (!rel)
527         ccl_error = CCL_ERR_BAD_RELATION;
528     else
529     {
530         struct ccl_rpn_node *p;
531
532         ADVANCE;                      /* skip relation */
533         if (KIND == CCL_TOK_TERM && look_token->next->kind == CCL_TOK_MINUS)
534         {
535             struct ccl_rpn_node *p1;
536             if (!(p1 = search_term (ap)))
537             {
538                 free (ap);
539                 return NULL;
540             }
541             ADVANCE;                   /* skip '-' */
542             if (KIND == CCL_TOK_TERM)  /* = term - term  ? */
543             {
544                 struct ccl_rpn_node *p2;
545                 
546                 if (!(p2 = search_term (ap)))
547                 {
548                     ccl_rpn_delete (p1);
549                     free (ap);
550                     return NULL;
551                 }
552                 p = mk_node (CCL_RPN_AND);
553                 p->u.p[0] = p1;
554                 add_attr (p1, CCL_BIB1_REL, 4);
555                 p->u.p[1] = p2;
556                 add_attr (p2, CCL_BIB1_REL, 2);
557                 free (ap);
558                 return p;
559             }
560             else                       /* = term -    */
561             {
562                 add_attr (p1, CCL_BIB1_REL, 4);
563                 free (ap);
564                 return p1;
565             }
566         }
567         else if (KIND == CCL_TOK_MINUS)   /* = - term  ? */
568         {
569             ADVANCE;
570             if (!(p = search_term (ap)))
571             {
572                 free (ap);
573                 return NULL;
574             }
575             add_attr (p, CCL_BIB1_REL, 2);
576             free (ap);
577             return p;
578         }
579         else if (KIND == CCL_TOK_LP)
580         {
581             ADVANCE;
582             if (!(p = find_spec (ap)))
583             {
584                 free (ap);
585                 return NULL;
586             }
587             if (KIND != CCL_TOK_RP)
588             {
589                 ccl_error = CCL_ERR_RP_EXPECTED;
590                 ccl_rpn_delete (p);
591                 free (ap);
592                 return NULL;
593             }
594             ADVANCE;
595             free (ap);
596             return p;
597         }
598         else
599         {
600             if (!(p = search_terms (ap)))
601             {
602                 free (ap);
603                 return NULL;
604             }
605             add_attr (p, CCL_BIB1_REL, rel);
606             free (ap);
607             return p;
608         }
609         ccl_error = CCL_ERR_TERM_EXPECTED;
610     }
611     free (ap);
612     return NULL;
613 }
614
615 /*
616  * search_terms: Parse CCL search terms - including proximity.
617  * qa:     Qualifier attributes already applied.
618  * return: pointer to node(s); NULL on error.
619  */
620 static struct ccl_rpn_node *search_terms (struct ccl_rpn_attr **qa)
621 {
622     struct ccl_rpn_node *p1, *p2, *pn;
623     p1 = search_term (qa);
624     if (!p1)
625         return NULL;
626     while (1)
627     {
628         if (KIND == CCL_TOK_PROX)
629         {
630             ADVANCE;
631             p2 = search_term (qa);
632             if (!p2)
633             {
634                 ccl_rpn_delete (p1);
635                 return NULL;
636             }
637             pn = mk_node (CCL_RPN_PROX);
638             pn->u.p[0] = p1;
639             pn->u.p[1] = p2;
640             p1 = pn;
641         }
642         else if (KIND == CCL_TOK_TERM)
643         {
644             p2 = search_term (qa);
645             if (!p2)
646             {
647                 ccl_rpn_delete (p1);
648                 return NULL;
649             }
650             pn = mk_node (CCL_RPN_PROX);
651             pn->u.p[0] = p1;
652             pn->u.p[1] = p2;
653             p1 = pn;
654         }
655         else
656             break;
657     }
658     return p1;
659 }
660
661 /*
662  * search_elements: Parse CCL search elements
663  * qa:     Qualifier attributes already applied.
664  * return: pointer to node(s); NULL on error.
665  */
666 static struct ccl_rpn_node *search_elements (struct ccl_rpn_attr **qa)
667 {
668     struct ccl_rpn_node *p1;
669     struct ccl_token *lookahead;
670     if (KIND == CCL_TOK_LP)
671     {
672         ADVANCE;
673         p1 = find_spec (qa);
674         if (!p1)
675             return NULL;
676         if (KIND != CCL_TOK_RP)
677         {
678             ccl_error = CCL_ERR_RP_EXPECTED;
679             ccl_rpn_delete (p1);
680             return NULL;
681         }
682         ADVANCE;
683         return p1;
684     }
685     else if (KIND == CCL_TOK_SET)
686     {
687         ADVANCE;
688         if (KIND == CCL_TOK_EQ)
689             ADVANCE;
690         if (KIND != CCL_TOK_TERM)
691         {
692             ccl_error = CCL_ERR_SETNAME_EXPECTED;
693             return NULL;
694         }
695         p1 = mk_node (CCL_RPN_SET);
696         p1->u.setname = copy_token_name (look_token);
697         ADVANCE;
698         return p1;
699     }
700     lookahead = look_token;
701
702     while (lookahead->kind==CCL_TOK_TERM)
703     {
704         lookahead = lookahead->next;
705         if (lookahead->kind == CCL_TOK_REL || lookahead->kind == CCL_TOK_EQ)
706             return qualifiers (lookahead, qa);
707         if (lookahead->kind != CCL_TOK_COMMA)
708             break;
709         lookahead = lookahead->next;
710     }
711     return search_terms (qa);
712 }
713
714 /*
715  * find_spec: Parse CCL find specification
716  * qa:     Qualifier attributes already applied.
717  * return: pointer to node(s); NULL on error.
718  */
719 static struct ccl_rpn_node *find_spec (struct ccl_rpn_attr **qa)
720 {
721     struct ccl_rpn_node *p1, *p2, *pn;
722     if (!(p1 = search_elements (qa)))
723         return NULL;
724     while (1)
725     {
726         switch (KIND)
727         {
728         case CCL_TOK_AND:
729             ADVANCE;
730             p2 = search_elements (qa);
731             if (!p2)
732             {
733                 ccl_rpn_delete (p1);
734                 return NULL;
735             }
736             pn = mk_node (CCL_RPN_AND);
737             pn->u.p[0] = p1;
738             pn->u.p[1] = p2;
739             p1 = pn;
740             continue;
741         case CCL_TOK_OR:
742             ADVANCE;
743             p2 = search_elements (qa);
744             if (!p2)
745             {
746                 ccl_rpn_delete (p1);
747                 return NULL;
748             }
749             pn = mk_node (CCL_RPN_OR);
750             pn->u.p[0] = p1;
751             pn->u.p[1] = p2;
752             p1 = pn;
753             continue;
754         case CCL_TOK_NOT:
755             ADVANCE;
756             p2 = search_elements (qa);
757             if (!p2)
758             {
759                 ccl_rpn_delete (p1);
760                 return NULL;
761             }
762             pn = mk_node (CCL_RPN_NOT);
763             pn->u.p[0] = p1;
764             pn->u.p[1] = p2;
765             p1 = pn;
766             continue;
767         }
768         break;
769     }
770     return p1;
771 }
772
773 /*
774  * ccl_find: Parse CCL find - token representation
775  * abibset: Bibset to be used for the parsing
776  * list:    List of tokens
777  * error:   Pointer to integer. Holds error no. on completion.
778  * pos:     Pointer to char position. Holds approximate error position.
779  * return:  RPN tree on successful completion; NULL otherwise.
780  */
781 struct ccl_rpn_node *ccl_find (CCL_bibset abibset, struct ccl_token *list,
782                                int *error, const char **pos)
783 {
784     struct ccl_rpn_node *p;
785
786     look_token = list;
787     bibset = abibset;
788     p = find_spec (NULL);
789     if (p && KIND != CCL_TOK_EOL)
790     {
791         if (KIND == CCL_TOK_RP)
792             ccl_error = CCL_ERR_BAD_RP;
793         else
794             ccl_error = CCL_ERR_OP_EXPECTED;
795         ccl_rpn_delete (p);
796         p = NULL;
797     }
798     *pos = look_token->name;
799     if (p)
800         *error = CCL_ERR_OK;
801     else
802         *error = ccl_error;
803     return p;
804 }
805
806 /*
807  * ccl_find_str: Parse CCL find - string representation
808  * bibset:  Bibset to be used for the parsing
809  * str:     String to be parsed
810  * error:   Pointer to integer. Holds error no. on completion.
811  * pos:     Pointer to char position. Holds approximate error position.
812  * return:  RPN tree on successful completion; NULL otherwise.
813  */
814 struct ccl_rpn_node *ccl_find_str (CCL_bibset bibset, const char *str,
815                                    int *error, int *pos)
816 {
817     struct ccl_token *list;
818     struct ccl_rpn_node *rpn;
819     const char *char_pos;
820
821     list = ccl_tokenize (str);
822     rpn = ccl_find (bibset, list, error, &char_pos);
823     if (*error)
824         *pos = char_pos - str;
825     return rpn;
826 }