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