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