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