Fixed the asn1 for facets
[yaz-moved-to-github.git] / src / cqltransform.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2010 Index Data
3  * See the file LICENSE for details.
4  */
5
6 /**
7  * \file cqltransform.c
8  * \brief Implements CQL transform (CQL to RPN conversion).
9  *
10  * Evaluation order of rules:
11  *
12  * always
13  * relation
14  * structure
15  * position
16  * truncation
17  * index
18  * relationModifier
19  */
20
21 #include <assert.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <yaz/rpn2cql.h>
26 #include <yaz/xmalloc.h>
27 #include <yaz/diagsrw.h>
28 #include <yaz/tokenizer.h>
29 #include <yaz/wrbuf.h>
30 #include <yaz/z-core.h>
31 #include <yaz/matchstr.h>
32 #include <yaz/oid_db.h>
33 #include <yaz/log.h>
34
35 struct cql_prop_entry {
36     char *pattern;
37     char *value;
38     Z_AttributeList attr_list;
39     struct cql_prop_entry *next;
40 };
41
42 struct cql_transform_t_ {
43     struct cql_prop_entry *entry;
44     yaz_tok_cfg_t tok_cfg;
45     int error;
46     char *addinfo;
47     WRBUF w;
48     NMEM nmem;
49 };
50
51
52 cql_transform_t cql_transform_create(void)
53 {
54     cql_transform_t ct = (cql_transform_t) xmalloc(sizeof(*ct));
55     ct->tok_cfg = yaz_tok_cfg_create();
56     ct->w = wrbuf_alloc();
57     ct->error = 0;
58     ct->addinfo = 0;
59     ct->entry = 0;
60     ct->nmem = nmem_create();
61     return ct;
62 }
63
64 static int cql_transform_parse_tok_line(cql_transform_t ct,
65                                         const char *pattern,
66                                         yaz_tok_parse_t tp)
67 {
68     int ae_num = 0;
69     Z_AttributeElement *ae[20];
70     int ret = 0; /* 0=OK, != 0 FAIL */
71     int t;
72     t = yaz_tok_move(tp);
73     
74     while (t == YAZ_TOK_STRING && ae_num < 20)
75     {
76         WRBUF type_str = wrbuf_alloc();
77         WRBUF set_str = 0;
78         Z_AttributeElement *elem = 0;
79         const char *value_str = 0;
80         /* attset type=value  OR  type=value */
81         
82         elem = (Z_AttributeElement *) nmem_malloc(ct->nmem, sizeof(*elem));
83         elem->attributeSet = 0;
84         ae[ae_num] = elem;
85         wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
86         wrbuf_puts(type_str, yaz_tok_parse_string(tp));
87         t = yaz_tok_move(tp);
88         if (t == YAZ_TOK_EOF)
89         {
90             wrbuf_destroy(type_str);
91             if (set_str)
92                 wrbuf_destroy(set_str);                
93             break;
94         }
95         if (t == YAZ_TOK_STRING)  
96         {  
97             wrbuf_puts(ct->w, " ");
98             wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
99             set_str = type_str;
100             
101             elem->attributeSet =
102                 yaz_string_to_oid_nmem(yaz_oid_std(), CLASS_ATTSET,
103                                        wrbuf_cstr(set_str), ct->nmem);
104             
105             type_str = wrbuf_alloc();
106             wrbuf_puts(type_str, yaz_tok_parse_string(tp));
107             t = yaz_tok_move(tp);
108         }
109         elem->attributeType = nmem_intdup(ct->nmem, 0);
110         if (sscanf(wrbuf_cstr(type_str), ODR_INT_PRINTF, elem->attributeType)
111             != 1)
112         {
113             wrbuf_destroy(type_str);
114             if (set_str)
115                 wrbuf_destroy(set_str);                
116             yaz_log(YLOG_WARN, "Expected numeric attribute type");
117             ret = -1;
118             break;
119         }
120
121         wrbuf_destroy(type_str);
122         if (set_str)
123             wrbuf_destroy(set_str);                
124         
125         if (t != '=')
126         {
127             yaz_log(YLOG_WARN, "Expected = after after attribute type");
128             ret = -1;
129             break;
130         }
131         t = yaz_tok_move(tp);
132         if (t != YAZ_TOK_STRING) /* value */
133         {
134             yaz_log(YLOG_WARN, "Missing attribute value");
135             ret = -1;
136             break;
137         }
138         value_str = yaz_tok_parse_string(tp);
139         if (isdigit(*value_str))
140         {
141             elem->which = Z_AttributeValue_numeric;
142             elem->value.numeric =
143                 nmem_intdup(ct->nmem, atoi(value_str));
144         }
145         else
146         {
147             Z_ComplexAttribute *ca = (Z_ComplexAttribute *)
148                 nmem_malloc(ct->nmem, sizeof(*ca));
149             elem->which = Z_AttributeValue_complex;
150             elem->value.complex = ca;
151             ca->num_list = 1;
152             ca->list = (Z_StringOrNumeric **)
153                 nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric *));
154             ca->list[0] = (Z_StringOrNumeric *)
155                 nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric));
156             ca->list[0]->which = Z_StringOrNumeric_string;
157             ca->list[0]->u.string = nmem_strdup(ct->nmem, value_str);
158             ca->num_semanticAction = 0;
159             ca->semanticAction = 0;
160         }
161         wrbuf_puts(ct->w, "=");
162         wrbuf_puts(ct->w, yaz_tok_parse_string(tp));
163         t = yaz_tok_move(tp);
164         wrbuf_puts(ct->w, " ");
165         ae_num++;
166     }
167     if (ret == 0) /* OK? */
168     {
169         struct cql_prop_entry **pp = &ct->entry;
170         while (*pp)
171             pp = &(*pp)->next;
172         *pp = (struct cql_prop_entry *) xmalloc(sizeof(**pp));
173         (*pp)->pattern = xstrdup(pattern);
174         (*pp)->value = xstrdup(wrbuf_cstr(ct->w));
175
176         (*pp)->attr_list.num_attributes = ae_num;
177         if (ae_num == 0)
178             (*pp)->attr_list.attributes = 0;
179         else
180         {
181             (*pp)->attr_list.attributes = (Z_AttributeElement **)
182                 nmem_malloc(ct->nmem,
183                             ae_num * sizeof(Z_AttributeElement *));
184             memcpy((*pp)->attr_list.attributes, ae, 
185                    ae_num * sizeof(Z_AttributeElement *));
186         }
187         (*pp)->next = 0;
188
189         if (0)
190         {
191             ODR pr = odr_createmem(ODR_PRINT);
192             Z_AttributeList *alp = &(*pp)->attr_list;
193             odr_setprint(pr, yaz_log_file());
194             z_AttributeList(pr, &alp, 0, 0);
195             odr_setprint(pr, 0);
196             odr_destroy(pr);
197         }
198     }
199     return ret;
200 }
201
202 int cql_transform_define_pattern(cql_transform_t ct, const char *pattern,
203                                  const char *value)
204 {
205     int r;
206     yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, value);
207     yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
208     r = cql_transform_parse_tok_line(ct, pattern, tp);
209     yaz_tok_parse_destroy(tp);
210     return r;
211 }
212     
213 cql_transform_t cql_transform_open_FILE(FILE *f)
214 {
215     cql_transform_t ct = cql_transform_create();
216     char line[1024];
217
218     yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
219
220     while (fgets(line, sizeof(line)-1, f))
221     {
222         yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, line);
223         int t;
224         wrbuf_rewind(ct->w);
225         t = yaz_tok_move(tp);
226         if (t == YAZ_TOK_STRING)
227         {
228             char * pattern = xstrdup(yaz_tok_parse_string(tp));
229             t = yaz_tok_move(tp);
230             if (t != '=')
231             {
232                 yaz_tok_parse_destroy(tp);
233                 cql_transform_close(ct);
234                 return 0;
235             }
236             if (cql_transform_parse_tok_line(ct, pattern, tp))
237             {
238                 yaz_tok_parse_destroy(tp);
239                 cql_transform_close(ct);
240                 return 0;
241             }
242             xfree(pattern);
243         }
244         else if (t != YAZ_TOK_EOF)
245         {
246             yaz_tok_parse_destroy(tp);
247             cql_transform_close(ct);
248             return 0;
249         }
250         yaz_tok_parse_destroy(tp);
251     }
252     return ct;
253 }
254
255 void cql_transform_close(cql_transform_t ct)
256 {
257     struct cql_prop_entry *pe;
258     if (!ct)
259         return;
260     pe = ct->entry;
261     while (pe)
262     {
263         struct cql_prop_entry *pe_next = pe->next;
264         xfree(pe->pattern);
265         xfree(pe->value);
266         xfree(pe);
267         pe = pe_next;
268     }
269     xfree(ct->addinfo);
270     yaz_tok_cfg_destroy(ct->tok_cfg);
271     wrbuf_destroy(ct->w);
272     nmem_destroy(ct->nmem);
273     xfree(ct);
274 }
275
276 cql_transform_t cql_transform_open_fname(const char *fname)
277 {
278     cql_transform_t ct;
279     FILE *f = fopen(fname, "r");
280     if (!f)
281         return 0;
282     ct = cql_transform_open_FILE(f);
283     fclose(f);
284     return ct;
285 }
286
287 #if 0
288 struct Z_AttributeElement {
289         Z_AttributeSetId *attributeSet; /* OPT */
290         int *attributeType;
291         int which;
292         union {
293                 int *numeric;
294                 Z_ComplexAttribute *complex;
295 #define Z_AttributeValue_numeric 1
296 #define Z_AttributeValue_complex 2
297         } value;
298 };
299 #endif
300
301 static int compare_attr(Z_AttributeElement *a, Z_AttributeElement *b)
302 {
303     ODR odr_a = odr_createmem(ODR_ENCODE);
304     ODR odr_b = odr_createmem(ODR_ENCODE);
305     int len_a, len_b;
306     char *buf_a, *buf_b;
307     int ret;
308
309     z_AttributeElement(odr_a, &a, 0, 0);
310     z_AttributeElement(odr_b, &b, 0, 0);
311     
312     buf_a = odr_getbuf(odr_a, &len_a, 0);
313     buf_b = odr_getbuf(odr_b, &len_b, 0);
314
315     ret = yaz_memcmp(buf_a, buf_b, len_a, len_b);
316
317     odr_destroy(odr_a);
318     odr_destroy(odr_b);
319     return ret;
320 }
321
322 const char *cql_lookup_reverse(cql_transform_t ct, 
323                                const char *category,
324                                Z_AttributeList *attributes)
325 {
326     struct cql_prop_entry *e;
327     size_t clen = strlen(category);
328     for (e = ct->entry; e; e = e->next)
329     {
330         if (!strncmp(e->pattern, category, clen))
331         {
332             /* category matches.. See if attributes in pattern value
333                are all listed in actual attributes */
334             int i;
335             for (i = 0; i < e->attr_list.num_attributes; i++)
336             {
337                 /* entry attribute */
338                 Z_AttributeElement *e_ae = e->attr_list.attributes[i];
339                 int j;
340                 for (j = 0; j < attributes->num_attributes; j++)
341                 {
342                     /* actual attribute */
343                     Z_AttributeElement *a_ae = attributes->attributes[j];
344                     int r = compare_attr(e_ae, a_ae);
345                     if (r == 0)
346                         break;
347                 }
348                 if (j == attributes->num_attributes)
349                     break; /* i was not found at all.. try next pattern */
350                     
351             }
352             if (i == e->attr_list.num_attributes)
353                 return e->pattern + clen;
354         }
355     }
356     return 0;
357 }
358                                       
359 static const char *cql_lookup_property(cql_transform_t ct,
360                                        const char *pat1, const char *pat2,
361                                        const char *pat3)
362 {
363     char pattern[120];
364     struct cql_prop_entry *e;
365
366     if (pat1 && pat2 && pat3)
367         sprintf(pattern, "%.39s.%.39s.%.39s", pat1, pat2, pat3);
368     else if (pat1 && pat2)
369         sprintf(pattern, "%.39s.%.39s", pat1, pat2);
370     else if (pat1 && pat3)
371         sprintf(pattern, "%.39s.%.39s", pat1, pat3);
372     else if (pat1)
373         sprintf(pattern, "%.39s", pat1);
374     else
375         return 0;
376     
377     for (e = ct->entry; e; e = e->next)
378     {
379         if (!cql_strcmp(e->pattern, pattern))
380             return e->value;
381     }
382     return 0;
383 }
384
385 int cql_pr_attr_uri(cql_transform_t ct, const char *category,
386                    const char *uri, const char *val, const char *default_val,
387                    void (*pr)(const char *buf, void *client_data),
388                    void *client_data,
389                    int errcode)
390 {
391     const char *res = 0;
392     const char *eval = val ? val : default_val;
393     const char *prefix = 0;
394     
395     if (uri)
396     {
397         struct cql_prop_entry *e;
398         
399         for (e = ct->entry; e; e = e->next)
400             if (!memcmp(e->pattern, "set.", 4) && e->value &&
401                 !strcmp(e->value, uri))
402             {
403                 prefix = e->pattern+4;
404                 break;
405             }
406         /* must have a prefix now - if not it's an error */
407     }
408
409     if (!uri || prefix)
410     {
411         if (!res)
412             res = cql_lookup_property(ct, category, prefix, eval);
413         /* we have some aliases for some relations unfortunately.. */
414         if (!res && !prefix && !strcmp(category, "relation"))
415         {
416             if (!strcmp(val, "=="))
417                 res = cql_lookup_property(ct, category, prefix, "exact");
418             if (!strcmp(val, "="))
419                 res = cql_lookup_property(ct, category, prefix, "eq");
420             if (!strcmp(val, "<="))
421                 res = cql_lookup_property(ct, category, prefix, "le");
422             if (!strcmp(val, ">="))
423                 res = cql_lookup_property(ct, category, prefix, "ge");
424         }
425         if (!res)
426             res = cql_lookup_property(ct, category, prefix, "*");
427     }
428     if (res)
429     {
430         char buf[64];
431
432         const char *cp0 = res, *cp1;
433         while ((cp1 = strchr(cp0, '=')))
434         {
435             int i;
436             while (*cp1 && *cp1 != ' ')
437                 cp1++;
438             if (cp1 - cp0 >= (ptrdiff_t) sizeof(buf))
439                 break;
440             memcpy(buf, cp0, cp1 - cp0);
441             buf[cp1-cp0] = 0;
442             (*pr)("@attr ", client_data);
443
444             for (i = 0; buf[i]; i++)
445             {
446                 if (buf[i] == '*')
447                     (*pr)(eval, client_data);
448                 else
449                 {
450                     char tmp[2];
451                     tmp[0] = buf[i];
452                     tmp[1] = '\0';
453                     (*pr)(tmp, client_data);
454                 }
455             }
456             (*pr)(" ", client_data);
457             cp0 = cp1;
458             while (*cp0 == ' ')
459                 cp0++;
460         }
461         return 1;
462     }
463     /* error ... */
464     if (errcode && !ct->error)
465     {
466         ct->error = errcode;
467         if (val)
468             ct->addinfo = xstrdup(val);
469         else
470             ct->addinfo = 0;
471     }
472     return 0;
473 }
474
475 int cql_pr_attr(cql_transform_t ct, const char *category,
476                 const char *val, const char *default_val,
477                 void (*pr)(const char *buf, void *client_data),
478                 void *client_data,
479                 int errcode)
480 {
481     return cql_pr_attr_uri(ct, category, 0 /* uri */,
482                            val, default_val, pr, client_data, errcode);
483 }
484
485
486 static void cql_pr_int(int val,
487                        void (*pr)(const char *buf, void *client_data),
488                        void *client_data)
489 {
490     char buf[21];              /* enough characters to 2^64 */
491     sprintf(buf, "%d", val);
492     (*pr)(buf, client_data);
493     (*pr)(" ", client_data);
494 }
495
496
497 static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods,
498                        void (*pr)(const char *buf, void *client_data),
499                        void *client_data)
500 {
501     int exclusion = 0;
502     int distance;               /* to be filled in later depending on unit */
503     int distance_defined = 0;
504     int ordered = 0;
505     int proxrel = 2;            /* less than or equal */
506     int unit = 2;               /* word */
507
508     while (mods)
509     {
510         const char *name = mods->u.st.index;
511         const char *term = mods->u.st.term;
512         const char *relation = mods->u.st.relation;
513
514         if (!strcmp(name, "distance")) {
515             distance = strtol(term, (char**) 0, 0);
516             distance_defined = 1;
517             if (!strcmp(relation, "="))
518                 proxrel = 3;
519             else if (!strcmp(relation, ">"))
520                 proxrel = 5;
521             else if (!strcmp(relation, "<"))
522                 proxrel = 1;
523             else if (!strcmp(relation, ">=")) 
524                 proxrel = 4;
525             else if (!strcmp(relation, "<="))
526                 proxrel = 2;
527             else if (!strcmp(relation, "<>"))
528                 proxrel = 6;
529             else 
530             {
531                 ct->error = YAZ_SRW_UNSUPP_PROX_RELATION;
532                 ct->addinfo = xstrdup(relation);
533                 return 0;
534             }
535         } 
536         else if (!strcmp(name, "ordered"))
537             ordered = 1;
538         else if (!strcmp(name, "unordered"))
539             ordered = 0;
540         else if (!strcmp(name, "unit"))
541         {
542             if (!strcmp(term, "word"))
543                 unit = 2;
544             else if (!strcmp(term, "sentence"))
545                 unit = 3;
546             else if (!strcmp(term, "paragraph"))
547                 unit = 4;
548             else if (!strcmp(term, "element"))
549                 unit = 8;
550             else 
551             {
552                 ct->error = YAZ_SRW_UNSUPP_PROX_UNIT;
553                 ct->addinfo = xstrdup(term);
554                 return 0;
555             }
556         } 
557         else 
558         {
559             ct->error = YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
560             ct->addinfo = xstrdup(name);
561             return 0;
562         }
563         mods = mods->u.st.modifiers;
564     }
565
566     if (!distance_defined)
567         distance = (unit == 2) ? 1 : 0;
568
569     cql_pr_int(exclusion, pr, client_data);
570     cql_pr_int(distance, pr, client_data);
571     cql_pr_int(ordered, pr, client_data);
572     cql_pr_int(proxrel, pr, client_data);
573     (*pr)("k ", client_data);
574     cql_pr_int(unit, pr, client_data);
575
576     return 1;
577 }
578
579 /* Returns location of first wildcard character in the `length'
580  * characters starting at `term', or a null pointer of there are
581  * none -- like memchr().
582  */
583 static const char *wcchar(int start, const char *term, int length)
584 {
585     while (length > 0)
586     {
587         if (start || term[-1] != '\\')
588             if (strchr("*?", *term))
589                 return term;
590         term++;
591         length--;
592         start = 0;
593     }
594     return 0;
595 }
596
597
598 /* ### checks for CQL relation-name rather than Type-1 attribute */
599 static int has_modifier(struct cql_node *cn, const char *name) {
600     struct cql_node *mod;
601     for (mod = cn->u.st.modifiers; mod != 0; mod = mod->u.st.modifiers) {
602         if (!strcmp(mod->u.st.index, name))
603             return 1;
604     }
605
606     return 0;
607 }
608
609
610 void emit_term(cql_transform_t ct,
611                struct cql_node *cn,
612                const char *term, int length,
613                void (*pr)(const char *buf, void *client_data),
614                void *client_data)
615 {
616     int i;
617     const char *ns = cn->u.st.index_uri;
618     int process_term = !has_modifier(cn, "regexp");
619     char *z3958_mem = 0;
620
621     assert(cn->which == CQL_NODE_ST);
622
623     if (process_term && length > 0)
624     {
625         if (length > 1 && term[0] == '^' && term[length-1] == '^')
626         {
627             cql_pr_attr(ct, "position", "firstAndLast", 0,
628                         pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
629             term++;
630             length -= 2;
631         }
632         else if (term[0] == '^')
633         {
634             cql_pr_attr(ct, "position", "first", 0,
635                         pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
636             term++;
637             length--;
638         }
639         else if (term[length-1] == '^')
640         {
641             cql_pr_attr(ct, "position", "last", 0,
642                         pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
643             length--;
644         }
645         else
646         {
647             cql_pr_attr(ct, "position", "any", 0,
648                         pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
649         }
650     }
651
652     if (process_term && length > 0)
653     {
654         const char *first_wc = wcchar(1, term, length);
655         const char *second_wc = first_wc ?
656             wcchar(0, first_wc+1, length-(first_wc-term)-1) : 0;
657
658         /* Check for well-known globbing patterns that represent
659          * simple truncation attributes as expected by, for example,
660          * Bath-compliant server.  If we find such a pattern but
661          * there's no mapping for it, that's fine: we just use a
662          * general pattern-matching attribute.
663          */
664         if (first_wc == term && second_wc == term + length-1 
665             && *first_wc == '*' && *second_wc == '*' 
666             && cql_pr_attr(ct, "truncation", "both", 0, pr, client_data, 0)) 
667         {
668             term++;
669             length -= 2;
670         }
671         else if (first_wc == term && second_wc == 0 && *first_wc == '*'
672                  && cql_pr_attr(ct, "truncation", "left", 0,
673                                 pr, client_data, 0))
674         {
675             term++;
676             length--;
677         }
678         else if (first_wc == term + length-1 && second_wc == 0
679                  && *first_wc == '*'
680                  && cql_pr_attr(ct, "truncation", "right", 0, 
681                                 pr, client_data, 0))
682         {
683             length--;
684         }
685         else if (first_wc)
686         {
687             /* We have one or more wildcard characters, but not in a
688              * way that can be dealt with using only the standard
689              * left-, right- and both-truncation attributes.  We need
690              * to translate the pattern into a Z39.58-type pattern,
691              * which has been supported in BIB-1 since 1996.  If
692              * there's no configuration element for "truncation.z3958"
693              * we indicate this as error 28 "Masking character not
694              * supported".
695              */
696             int i;
697             cql_pr_attr(ct, "truncation", "z3958", 0,
698                         pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP);
699             z3958_mem = (char *) xmalloc(length+1);
700             for (i = 0; i < length; i++)
701             {
702                 if (i > 0 && term[i-1] == '\\')
703                     z3958_mem[i] = term[i];
704                 else if (term[i] == '*')
705                     z3958_mem[i] = '?';
706                 else if (term[i] == '?')
707                     z3958_mem[i] = '#';
708                 else
709                     z3958_mem[i] = term[i];
710             }
711             z3958_mem[length] = '\0';
712             term = z3958_mem;
713         }
714         else {
715             /* No masking characters.  Use "truncation.none" if given. */
716             cql_pr_attr(ct, "truncation", "none", 0,
717                         pr, client_data, 0);
718         }
719     }
720     if (ns) {
721         cql_pr_attr_uri(ct, "index", ns,
722                         cn->u.st.index, "serverChoice",
723                         pr, client_data, YAZ_SRW_UNSUPP_INDEX);
724     }
725     if (cn->u.st.modifiers)
726     {
727         struct cql_node *mod = cn->u.st.modifiers;
728         for (; mod; mod = mod->u.st.modifiers)
729         {
730             cql_pr_attr(ct, "relationModifier", mod->u.st.index, 0,
731                         pr, client_data, YAZ_SRW_UNSUPP_RELATION_MODIFIER);
732         }
733     }
734
735     (*pr)("\"", client_data);
736     for (i = 0; i<length; i++)
737     {
738         /* pr(int) each character */
739         /* we do not need to deal with \-sequences because the
740            CQL and PQF terms have same \-format, bug #1988 */
741         char buf[2];
742
743         buf[0] = term[i];
744         buf[1] = '\0';
745         (*pr)(buf, client_data);
746     }
747     (*pr)("\" ", client_data);
748     xfree(z3958_mem);
749 }
750
751 void emit_terms(cql_transform_t ct,
752                 struct cql_node *cn,
753                 void (*pr)(const char *buf, void *client_data),
754                 void *client_data,
755                 const char *op)
756 {
757     struct cql_node *ne = cn->u.st.extra_terms;
758     if (ne)
759     {
760         (*pr)("@", client_data);
761         (*pr)(op, client_data);
762         (*pr)(" ", client_data);
763     }
764     emit_term(ct, cn, cn->u.st.term, strlen(cn->u.st.term),
765               pr, client_data);
766     for (; ne; ne = ne->u.st.extra_terms)
767     {
768         if (ne->u.st.extra_terms)
769         {
770             (*pr)("@", client_data);
771             (*pr)(op, client_data);
772             (*pr)(" ", client_data);
773         }            
774         emit_term(ct, cn, ne->u.st.term, strlen(ne->u.st.term),
775                   pr, client_data);
776     }
777 }
778
779 void emit_wordlist(cql_transform_t ct,
780                    struct cql_node *cn,
781                    void (*pr)(const char *buf, void *client_data),
782                    void *client_data,
783                    const char *op)
784 {
785     const char *cp0 = cn->u.st.term;
786     const char *cp1;
787     const char *last_term = 0;
788     int last_length = 0;
789     while(cp0)
790     {
791         while (*cp0 == ' ')
792             cp0++;
793         cp1 = strchr(cp0, ' ');
794         if (last_term)
795         {
796             (*pr)("@", client_data);
797             (*pr)(op, client_data);
798             (*pr)(" ", client_data);
799             emit_term(ct, cn, last_term, last_length, pr, client_data);
800         }
801         last_term = cp0;
802         if (cp1)
803             last_length = cp1 - cp0;
804         else
805             last_length = strlen(cp0);
806         cp0 = cp1;
807     }
808     if (last_term)
809         emit_term(ct, cn, last_term, last_length, pr, client_data);
810 }
811
812 void cql_transform_r(cql_transform_t ct,
813                      struct cql_node *cn,
814                      void (*pr)(const char *buf, void *client_data),
815                      void *client_data)
816 {
817     const char *ns;
818     struct cql_node *mods;
819
820     if (!cn)
821         return;
822     switch (cn->which)
823     {
824     case CQL_NODE_ST:
825         ns = cn->u.st.index_uri;
826         if (ns)
827         {
828             if (!strcmp(ns, cql_uri())
829                 && cn->u.st.index && !cql_strcmp(cn->u.st.index, "resultSet"))
830             {
831                 (*pr)("@set \"", client_data);
832                 (*pr)(cn->u.st.term, client_data);
833                 (*pr)("\" ", client_data);
834                 return ;
835             }
836         }
837         else
838         {
839             if (!ct->error)
840             {
841                 ct->error = YAZ_SRW_UNSUPP_CONTEXT_SET;
842                 ct->addinfo = 0;
843             }
844         }
845         cql_pr_attr(ct, "always", 0, 0, pr, client_data, 0);
846         cql_pr_attr(ct, "relation", cn->u.st.relation, 0, pr, client_data,
847                     YAZ_SRW_UNSUPP_RELATION);
848         cql_pr_attr(ct, "structure", cn->u.st.relation, 0,
849                     pr, client_data, YAZ_SRW_UNSUPP_COMBI_OF_RELATION_AND_TERM);
850         if (cn->u.st.relation && !cql_strcmp(cn->u.st.relation, "all"))
851             emit_wordlist(ct, cn, pr, client_data, "and");
852         else if (cn->u.st.relation && !cql_strcmp(cn->u.st.relation, "any"))
853             emit_wordlist(ct, cn, pr, client_data, "or");
854         else
855             emit_terms(ct, cn, pr, client_data, "and");
856         break;
857     case CQL_NODE_BOOL:
858         (*pr)("@", client_data);
859         (*pr)(cn->u.boolean.value, client_data);
860         (*pr)(" ", client_data);
861         mods = cn->u.boolean.modifiers;
862         if (!strcmp(cn->u.boolean.value, "prox")) 
863         {
864             if (!cql_pr_prox(ct, mods, pr, client_data))
865                 return;
866         } 
867         else if (mods)
868         {
869             /* Boolean modifiers other than on proximity not supported */
870             ct->error = YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
871             ct->addinfo = xstrdup(mods->u.st.index);
872             return;
873         }
874
875         cql_transform_r(ct, cn->u.boolean.left, pr, client_data);
876         cql_transform_r(ct, cn->u.boolean.right, pr, client_data);
877         break;
878
879     default:
880         fprintf(stderr, "Fatal: impossible CQL node-type %d\n", cn->which);
881         abort();
882     }
883 }
884
885 int cql_transform(cql_transform_t ct, struct cql_node *cn,
886                   void (*pr)(const char *buf, void *client_data),
887                   void *client_data)
888 {
889     struct cql_prop_entry *e;
890     NMEM nmem = nmem_create();
891
892     ct->error = 0;
893     xfree(ct->addinfo);
894     ct->addinfo = 0;
895
896     for (e = ct->entry; e ; e = e->next)
897     {
898         if (!cql_strncmp(e->pattern, "set.", 4))
899             cql_apply_prefix(nmem, cn, e->pattern+4, e->value);
900         else if (!cql_strcmp(e->pattern, "set"))
901             cql_apply_prefix(nmem, cn, 0, e->value);
902     }
903     cql_transform_r(ct, cn, pr, client_data);
904     nmem_destroy(nmem);
905     return ct->error;
906 }
907
908
909 int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f)
910 {
911     return cql_transform(ct, cn, cql_fputs, f);
912 }
913
914 int cql_transform_buf(cql_transform_t ct, struct cql_node *cn, char *out, int max)
915 {
916     struct cql_buf_write_info info;
917     int r;
918
919     info.off = 0;
920     info.max = max;
921     info.buf = out;
922     r = cql_transform(ct, cn, cql_buf_write_handler, &info);
923     if (info.off < 0) {
924         /* Attempt to write past end of buffer.  For some reason, this
925            SRW diagnostic is deprecated, but it's so perfect for our
926            purposes that it would be stupid not to use it. */
927         char numbuf[30];
928         ct->error = YAZ_SRW_TOO_MANY_CHARS_IN_QUERY;
929         sprintf(numbuf, "%ld", (long) info.max);
930         ct->addinfo = xstrdup(numbuf);
931         return -1;
932     }
933     if (info.off >= 0)
934         info.buf[info.off] = '\0';
935     return r;
936 }
937
938 int cql_transform_error(cql_transform_t ct, const char **addinfo)
939 {
940     *addinfo = ct->addinfo;
941     return ct->error;
942 }
943
944 void cql_transform_set_error(cql_transform_t ct, int error, const char *addinfo)
945 {
946     xfree(ct->addinfo);
947     ct->addinfo = addinfo ? xstrdup(addinfo) : 0;
948     ct->error = error;
949 }
950
951 /*
952  * Local variables:
953  * c-basic-offset: 4
954  * c-file-style: "Stroustrup"
955  * indent-tabs-mode: nil
956  * End:
957  * vim: shiftwidth=4 tabstop=8 expandtab
958  */
959