Avoid using non-portable debian.h
[yaz-moved-to-github.git] / src / cqltransform.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file cqltransform.c
7  * \brief Implements CQL transform (CQL to RPN conversion).
8  *
9  * Evaluation order of rules:
10  *
11  * always
12  * relation
13  * structure
14  * position
15  * truncation
16  * index
17  * relationModifier
18  */
19 #if HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <assert.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <yaz/rpn2cql.h>
27 #include <yaz/xmalloc.h>
28 #include <yaz/diagsrw.h>
29 #include <yaz/tokenizer.h>
30 #include <yaz/wrbuf.h>
31 #include <yaz/z-core.h>
32 #include <yaz/matchstr.h>
33 #include <yaz/oid_db.h>
34 #include <yaz/log.h>
35
36 struct cql_prop_entry {
37     char *pattern;
38     char *value;
39     Z_AttributeList attr_list;
40     struct cql_prop_entry *next;
41 };
42
43 struct cql_transform_t_ {
44     struct cql_prop_entry *entry;
45     yaz_tok_cfg_t tok_cfg;
46     int error;
47     WRBUF addinfo;
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->error = 0;
57     ct->addinfo = wrbuf_alloc();
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     WRBUF w = wrbuf_alloc();
72
73     t = yaz_tok_move(tp);
74
75     while (t == YAZ_TOK_STRING && ae_num < 20)
76     {
77         WRBUF type_str = wrbuf_alloc();
78         WRBUF set_str = 0;
79         Z_AttributeElement *elem = 0;
80         const char *value_str = 0;
81         /* attset type=value  OR  type=value */
82
83         elem = (Z_AttributeElement *) nmem_malloc(ct->nmem, sizeof(*elem));
84         elem->attributeSet = 0;
85         ae[ae_num] = elem;
86         wrbuf_puts(w, yaz_tok_parse_string(tp));
87         wrbuf_puts(type_str, yaz_tok_parse_string(tp));
88         t = yaz_tok_move(tp);
89         if (t == YAZ_TOK_EOF)
90         {
91             wrbuf_destroy(type_str);
92             if (set_str)
93                 wrbuf_destroy(set_str);
94             break;
95         }
96         if (t == YAZ_TOK_STRING)
97         {
98             wrbuf_puts(w, " ");
99             wrbuf_puts(w, yaz_tok_parse_string(tp));
100             set_str = type_str;
101
102             elem->attributeSet =
103                 yaz_string_to_oid_nmem(yaz_oid_std(), CLASS_ATTSET,
104                                        wrbuf_cstr(set_str), ct->nmem);
105
106             type_str = wrbuf_alloc();
107             wrbuf_puts(type_str, yaz_tok_parse_string(tp));
108             t = yaz_tok_move(tp);
109         }
110         elem->attributeType = nmem_intdup(ct->nmem, 0);
111         if (sscanf(wrbuf_cstr(type_str), ODR_INT_PRINTF, elem->attributeType)
112             != 1)
113         {
114             wrbuf_destroy(type_str);
115             if (set_str)
116                 wrbuf_destroy(set_str);
117             yaz_log(YLOG_WARN, "Expected numeric attribute type");
118             ret = -1;
119             break;
120         }
121
122         wrbuf_destroy(type_str);
123         if (set_str)
124             wrbuf_destroy(set_str);
125
126         if (t != '=')
127         {
128             yaz_log(YLOG_WARN, "Expected = after after attribute type");
129             ret = -1;
130             break;
131         }
132         t = yaz_tok_move(tp);
133         if (t != YAZ_TOK_STRING) /* value */
134         {
135             yaz_log(YLOG_WARN, "Missing attribute value");
136             ret = -1;
137             break;
138         }
139         value_str = yaz_tok_parse_string(tp);
140         if (yaz_isdigit(*value_str))
141         {
142             elem->which = Z_AttributeValue_numeric;
143             elem->value.numeric =
144                 nmem_intdup(ct->nmem, atoi(value_str));
145         }
146         else
147         {
148             Z_ComplexAttribute *ca = (Z_ComplexAttribute *)
149                 nmem_malloc(ct->nmem, sizeof(*ca));
150             elem->which = Z_AttributeValue_complex;
151             elem->value.complex = ca;
152             ca->num_list = 1;
153             ca->list = (Z_StringOrNumeric **)
154                 nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric *));
155             ca->list[0] = (Z_StringOrNumeric *)
156                 nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric));
157             ca->list[0]->which = Z_StringOrNumeric_string;
158             ca->list[0]->u.string = nmem_strdup(ct->nmem, value_str);
159             ca->num_semanticAction = 0;
160             ca->semanticAction = 0;
161         }
162         wrbuf_puts(w, "=");
163         wrbuf_puts(w, yaz_tok_parse_string(tp));
164         t = yaz_tok_move(tp);
165         wrbuf_puts(w, " ");
166         ae_num++;
167     }
168     if (ret == 0) /* OK? */
169     {
170         struct cql_prop_entry **pp = &ct->entry;
171         while (*pp)
172             pp = &(*pp)->next;
173         *pp = (struct cql_prop_entry *) xmalloc(sizeof(**pp));
174         (*pp)->pattern = xstrdup(pattern);
175         (*pp)->value = xstrdup(wrbuf_cstr(w));
176
177         (*pp)->attr_list.num_attributes = ae_num;
178         if (ae_num == 0)
179             (*pp)->attr_list.attributes = 0;
180         else
181         {
182             (*pp)->attr_list.attributes = (Z_AttributeElement **)
183                 nmem_malloc(ct->nmem,
184                             ae_num * sizeof(Z_AttributeElement *));
185             memcpy((*pp)->attr_list.attributes, ae,
186                    ae_num * sizeof(Z_AttributeElement *));
187         }
188         (*pp)->next = 0;
189
190         if (0)
191         {
192             ODR pr = odr_createmem(ODR_PRINT);
193             Z_AttributeList *alp = &(*pp)->attr_list;
194             odr_setprint_noclose(pr, yaz_log_file());
195             z_AttributeList(pr, &alp, 0, 0);
196             odr_destroy(pr);
197         }
198     }
199     wrbuf_destroy(w);
200     return ret;
201 }
202
203 int cql_transform_define_pattern(cql_transform_t ct, const char *pattern,
204                                  const char *value)
205 {
206     int r;
207     yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, value);
208     yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
209     r = cql_transform_parse_tok_line(ct, pattern, tp);
210     yaz_tok_parse_destroy(tp);
211     return r;
212 }
213
214 cql_transform_t cql_transform_open_FILE(FILE *f)
215 {
216     cql_transform_t ct = cql_transform_create();
217     char line[1024];
218
219     yaz_tok_cfg_single_tokens(ct->tok_cfg, "=");
220
221     while (fgets(line, sizeof(line)-1, f))
222     {
223         yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, line);
224         int t;
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     wrbuf_destroy(ct->addinfo);
270     yaz_tok_cfg_destroy(ct->tok_cfg);
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                     if (!compare_attr(e_ae, &a_ae))
344                         break;
345                     if (a_ae.attributeSet && &e_ae->attributeSet &&
346                         !oid_oidcmp(a_ae.attributeSet, yaz_oid_attset_bib_1))
347                         a_ae.attributeSet = 0;
348                     if (!compare_attr(e_ae, &a_ae))
349                         break;
350                 }
351                 if (j == attributes->num_attributes)
352                     break; /* i was not found at all.. try next pattern */
353
354             }
355             if (i == e->attr_list.num_attributes)
356                 return e->pattern + clen;
357         }
358     }
359     return 0;
360 }
361
362 static const char *cql_lookup_property(cql_transform_t ct,
363                                        const char *pat1, const char *pat2,
364                                        const char *pat3)
365 {
366     char pattern[120];
367     struct cql_prop_entry *e;
368
369     if (pat1 && pat2 && pat3)
370         sprintf(pattern, "%.39s.%.39s.%.39s", pat1, pat2, pat3);
371     else if (pat1 && pat2)
372         sprintf(pattern, "%.39s.%.39s", pat1, pat2);
373     else if (pat1 && pat3)
374         sprintf(pattern, "%.39s.%.39s", pat1, pat3);
375     else if (pat1)
376         sprintf(pattern, "%.39s", pat1);
377     else
378         return 0;
379
380     for (e = ct->entry; e; e = e->next)
381     {
382         if (!cql_strcmp(e->pattern, pattern))
383             return e->value;
384     }
385     return 0;
386 }
387
388 int cql_pr_attr_uri(cql_transform_t ct, WRBUF addinfo, const char *category,
389                    const char *uri, const char *val, const char *default_val,
390                    void (*pr)(const char *buf, void *client_data),
391                    void *client_data,
392                    int errcode)
393 {
394     const char *res = 0;
395     const char *eval = val ? val : default_val;
396     const char *prefix = 0;
397
398     if (uri)
399     {
400         struct cql_prop_entry *e;
401
402         for (e = ct->entry; e; e = e->next)
403             if (!memcmp(e->pattern, "set.", 4) && e->value &&
404                 !strcmp(e->value, uri))
405             {
406                 prefix = e->pattern+4;
407                 break;
408             }
409         /* must have a prefix now - if not it's an error */
410     }
411
412     if (!uri || prefix)
413     {
414         if (!res)
415             res = cql_lookup_property(ct, category, prefix, eval);
416         /* we have some aliases for some relations unfortunately.. */
417         if (!res && !prefix && !strcmp(category, "relation"))
418         {
419             if (!strcmp(val, "=="))
420                 res = cql_lookup_property(ct, category, prefix, "exact");
421             if (!strcmp(val, "="))
422                 res = cql_lookup_property(ct, category, prefix, "eq");
423             if (!strcmp(val, "<="))
424                 res = cql_lookup_property(ct, category, prefix, "le");
425             if (!strcmp(val, ">="))
426                 res = cql_lookup_property(ct, category, prefix, "ge");
427         }
428         if (!res)
429             res = cql_lookup_property(ct, category, prefix, "*");
430     }
431     if (res)
432     {
433         char buf[64];
434
435         const char *cp0 = res, *cp1;
436         while ((cp1 = strchr(cp0, '=')))
437         {
438             int i;
439             while (*cp1 && *cp1 != ' ')
440                 cp1++;
441             if (cp1 - cp0 >= (ptrdiff_t) sizeof(buf))
442                 break;
443             memcpy(buf, cp0, cp1 - cp0);
444             buf[cp1-cp0] = 0;
445             (*pr)("@attr ", client_data);
446
447             for (i = 0; buf[i]; i++)
448             {
449                 if (buf[i] == '*')
450                     (*pr)(eval, client_data);
451                 else
452                 {
453                     char tmp[2];
454                     tmp[0] = buf[i];
455                     tmp[1] = '\0';
456                     (*pr)(tmp, client_data);
457                 }
458             }
459             (*pr)(" ", client_data);
460             cp0 = cp1;
461             while (*cp0 == ' ')
462                 cp0++;
463         }
464         return 0;
465     }
466     /* error ... */
467     if (errcode == 0)
468         return 1; /* signal error, but do not set addinfo */
469     if (val)
470         wrbuf_puts(addinfo, val);
471     return errcode;
472 }
473
474 int cql_pr_attr(cql_transform_t ct, WRBUF addinfo, 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, addinfo, 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                        WRBUF addinfo,
498                        void (*pr)(const char *buf, void *client_data),
499                        void *client_data)
500 {
501     int exclusion = 0;
502     int distance = -1;
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             if (!strcmp(relation, "="))
516                 proxrel = 3;
517             else if (!strcmp(relation, ">"))
518                 proxrel = 5;
519             else if (!strcmp(relation, "<"))
520                 proxrel = 1;
521             else if (!strcmp(relation, ">="))
522                 proxrel = 4;
523             else if (!strcmp(relation, "<="))
524                 proxrel = 2;
525             else if (!strcmp(relation, "<>"))
526                 proxrel = 6;
527             else
528             {
529                 wrbuf_puts(addinfo, relation);
530                 return YAZ_SRW_UNSUPP_PROX_RELATION;
531             }
532         }
533         else if (!strcmp(name, "ordered"))
534             ordered = 1;
535         else if (!strcmp(name, "unordered"))
536             ordered = 0;
537         else if (!strcmp(name, "unit"))
538         {
539             if (!strcmp(term, "word"))
540                 unit = 2;
541             else if (!strcmp(term, "sentence"))
542                 unit = 3;
543             else if (!strcmp(term, "paragraph"))
544                 unit = 4;
545             else if (!strcmp(term, "element"))
546                 unit = 8;
547             else
548             {
549                 wrbuf_puts(addinfo, term);
550                 return YAZ_SRW_UNSUPP_PROX_UNIT;
551             }
552         }
553         else
554         {
555             wrbuf_puts(addinfo, name);
556             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
557         }
558         mods = mods->u.st.modifiers;
559     }
560
561     if (distance == -1)
562         distance = (unit == 2) ? 1 : 0;
563
564     cql_pr_int(exclusion, pr, client_data);
565     cql_pr_int(distance, pr, client_data);
566     cql_pr_int(ordered, pr, client_data);
567     cql_pr_int(proxrel, pr, client_data);
568     (*pr)("k ", client_data);
569     cql_pr_int(unit, pr, client_data);
570
571     return 0;
572 }
573
574 /* ### checks for CQL relation-name rather than Type-1 attribute */
575 static int has_modifier(struct cql_node *cn, const char *name) {
576     struct cql_node *mod;
577     for (mod = cn->u.st.modifiers; mod != 0; mod = mod->u.st.modifiers) {
578         if (!strcmp(mod->u.st.index, name))
579             return 1;
580     }
581
582     return 0;
583 }
584
585 static int emit_term(cql_transform_t ct,
586                      struct cql_node *cn, WRBUF addinfo,
587                      const char *term, int length,
588                      void (*pr)(const char *buf, void *client_data),
589                      void *client_data)
590 {
591     int i, r;
592     const char *ns = cn->u.st.index_uri;
593     int z3958_mode = 0;
594     int process_term = 1;
595
596     if (has_modifier(cn, "regexp"))
597         process_term = 0;
598     else if (has_modifier(cn, "unmasked"))
599         process_term = 0;
600     else if (cql_lookup_property(ct, "truncation", 0, "cql"))
601     {
602         process_term = 0;
603         r = cql_pr_attr(ct, addinfo, "truncation", "cql", 0,
604                         pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP);
605         if (r)
606             return r;
607     }
608     assert(cn->which == CQL_NODE_ST);
609
610     if (process_term)
611     {   /* convert term via truncation.things */
612         unsigned anchor = 0;
613         unsigned trunc = 0;
614         for (i = 0; i < length; i++)
615         {
616             if (term[i] == '\\' && i < length - 1)
617                 i++;
618             else
619             {
620                 switch (term[i])
621                 {
622                 case '^':
623                     if (i == 0)
624                         anchor |= 1;
625                     else if (i == length - 1)
626                         anchor |= 2;
627                     break;
628                 case '*':
629                     if (i == 0)
630                         trunc |= 1;
631                     else if (i == length - 1)
632                         trunc |= 2;
633                     else
634                         z3958_mode = 1;
635                     break;
636                 case '?':
637                     z3958_mode = 1;
638                     break;
639                 }
640             }
641         }
642         if (anchor == 3)
643         {
644             r = cql_pr_attr(ct, addinfo, "position", "firstAndLast", 0,
645                             pr, client_data,
646                             YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
647             if (r)
648                 return r;
649             term++;
650             length -= 2;
651         }
652         else if (anchor == 1)
653         {
654             r = cql_pr_attr(ct, addinfo, "position", "first", 0,
655                             pr, client_data,
656                             YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
657             if (r)
658                 return r;
659             term++;
660             length--;
661         }
662         else if (anchor == 2)
663         {
664             r = cql_pr_attr(ct, addinfo, "position", "last", 0,
665                             pr, client_data,
666                             YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
667             if (r)
668                 return r;
669             length--;
670         }
671         else
672         {
673             r = cql_pr_attr(ct, addinfo, "position", "any", 0,
674                         pr, client_data,
675                             YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION);
676             if (r)
677                 return r;
678         }
679         if (z3958_mode == 0)
680         {
681             if (trunc == 3 && !cql_pr_attr(ct, addinfo, "truncation",
682                                           "both", 0, pr, client_data, 0))
683             {
684                 term++;
685                 length -= 2;
686             }
687             else if (trunc == 1 && !cql_pr_attr(ct, addinfo, "truncation",
688                                                "left", 0, pr, client_data, 0))
689             {
690                 term++;
691                 length--;
692             }
693             else if (trunc == 2 && !cql_pr_attr(ct, addinfo, "truncation",
694                                                 "right", 0, pr, client_data, 0))
695             {
696                 length--;
697             }
698             else if (trunc)
699                 z3958_mode = 1;
700             else
701                 cql_pr_attr(ct, addinfo, "truncation", "none", 0,
702                             pr, client_data, 0);
703         }
704         if (z3958_mode)
705         {
706             r = cql_pr_attr(ct, addinfo, "truncation", "z3958", 0,
707                             pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP);
708             if (r)
709                 return r;
710         }
711     }
712     if (ns)
713     {
714         r = cql_pr_attr_uri(ct, addinfo, "index", ns,
715                             cn->u.st.index, "serverChoice",
716                             pr, client_data, YAZ_SRW_UNSUPP_INDEX);
717         if (r)
718             return r;
719     }
720     if (cn->u.st.modifiers)
721     {
722         struct cql_node *mod = cn->u.st.modifiers;
723         for (; mod; mod = mod->u.st.modifiers)
724         {
725             r = cql_pr_attr(ct, addinfo,
726                             "relationModifier", mod->u.st.index, 0,
727                             pr, client_data, YAZ_SRW_UNSUPP_RELATION_MODIFIER);
728             if (r)
729                 return r;
730         }
731     }
732     (*pr)("\"", client_data);
733     if (process_term)
734         for (i = 0; i < length; i++)
735         {
736             char x[2]; /* temp buffer */
737             if (term[i] == '\\' && i < length - 1)
738             {
739                 i++;
740                 if (strchr("\"\\", term[i]))
741                     pr("\\", client_data);
742                 if (z3958_mode && strchr("#?", term[i]))
743                     pr("\\\\", client_data); /* double \\ to survive PQF parse */
744                 x[0] = term[i];
745                 x[1] = '\0';
746                 pr(x, client_data);
747             }
748             else if (z3958_mode && term[i] == '*')
749             {
750                 pr("?", client_data);
751                 if (i < length - 1 && yaz_isdigit(term[i+1]))
752                     pr("\\\\", client_data); /* dbl \\ to survive PQF parse */
753             }
754             else if (z3958_mode && term[i] == '?')
755             {
756                 pr("#", client_data);
757             }
758             else
759             {
760                 if (term[i] == '\"')
761                     pr("\\", client_data);
762                 if (z3958_mode && strchr("#?", term[i]))
763                     pr("\\\\", client_data); /* dbl \\ to survive PQF parse */
764                 x[0] = term[i];
765                 x[1] = '\0';
766                 pr(x, client_data);
767             }
768         }
769     else
770     {
771         for (i = 0; i < length; i++)
772         {
773             char x[2];
774             x[0] = term[i];
775             x[1] = '\0';
776             pr(x, client_data);
777         }
778     }
779     (*pr)("\" ", client_data);
780     return 0;
781 }
782
783 static int emit_terms(cql_transform_t ct, struct cql_node *cn,
784                       WRBUF addinfo,
785                       void (*pr)(const char *buf, void *client_data),
786                       void *client_data,
787                       const char *op)
788 {
789     struct cql_node *ne = cn->u.st.extra_terms;
790     int r;
791     if (ne)
792     {
793         (*pr)("@", client_data);
794         (*pr)(op, client_data);
795         (*pr)(" ", client_data);
796     }
797     r = emit_term(ct, cn, addinfo, cn->u.st.term, strlen(cn->u.st.term),
798                   pr, client_data);
799     for (; !r && ne; ne = ne->u.st.extra_terms)
800     {
801         if (ne->u.st.extra_terms)
802         {
803             (*pr)("@", client_data);
804             (*pr)(op, client_data);
805             (*pr)(" ", client_data);
806         }
807         r = emit_term(ct, cn, addinfo, ne->u.st.term, strlen(ne->u.st.term),
808                       pr, client_data);
809     }
810     return r;
811 }
812
813 static int emit_wordlist(cql_transform_t ct, struct cql_node *cn,
814                          WRBUF addinfo,
815                          void (*pr)(const char *buf, void *client_data),
816                          void *client_data,
817                          const char *op)
818 {
819     int r = 0;
820     const char *cp0 = cn->u.st.term;
821     const char *cp1;
822     const char *last_term = 0;
823     int last_length = 0;
824     while (!r && cp0)
825     {
826         while (*cp0 == ' ')
827             cp0++;
828         cp1 = strchr(cp0, ' ');
829         if (last_term)
830         {
831             (*pr)("@", client_data);
832             (*pr)(op, client_data);
833             (*pr)(" ", client_data);
834             r = emit_term(ct, cn, addinfo, last_term, last_length,
835                           pr, client_data);
836         }
837         last_term = cp0;
838         if (cp1)
839             last_length = cp1 - cp0;
840         else
841             last_length = strlen(cp0);
842         cp0 = cp1;
843     }
844     if (!r && last_term)
845         r = emit_term(ct, cn, addinfo, last_term, last_length, pr, client_data);
846     return r;
847 }
848
849 static int emit_node(cql_transform_t ct, struct cql_node *cn,
850                      WRBUF addinfo,
851                      void (*pr)(const char *buf, void *client_data),
852                      void *client_data)
853 {
854     const char *ns;
855     int r = 0;
856     struct cql_node *mods;
857
858     if (!cn)
859         return 0;
860     switch (cn->which)
861     {
862     case CQL_NODE_ST:
863         ns = cn->u.st.index_uri;
864         if (ns)
865         {
866             if (!strcmp(ns, cql_uri())
867                 && cn->u.st.index && !cql_strcmp(cn->u.st.index, "resultSet"))
868             {
869                 (*pr)("@set \"", client_data);
870                 (*pr)(cn->u.st.term, client_data);
871                 (*pr)("\" ", client_data);
872                 return 0;
873             }
874         }
875         else
876         {
877             return YAZ_SRW_UNSUPP_CONTEXT_SET;
878         }
879         cql_pr_attr(ct, addinfo, "always", 0, 0, pr, client_data, 0);
880         r = cql_pr_attr(ct, addinfo, "relation", cn->u.st.relation, 0,
881                         pr, client_data, YAZ_SRW_UNSUPP_RELATION);
882         if (r)
883             return r;
884         r = cql_pr_attr(ct, addinfo, "structure", cn->u.st.relation, 0,
885                         pr, client_data,
886                         YAZ_SRW_UNSUPP_COMBI_OF_RELATION_AND_TERM);
887         if (r)
888             return r;
889         if (cn->u.st.relation && !cql_strcmp(cn->u.st.relation, "all"))
890             r = emit_wordlist(ct, cn, addinfo, pr, client_data, "and");
891         else if (cn->u.st.relation && !cql_strcmp(cn->u.st.relation, "any"))
892             r = emit_wordlist(ct, cn, addinfo, pr, client_data, "or");
893         else
894             r = emit_terms(ct, cn, addinfo, pr, client_data, "and");
895         break;
896     case CQL_NODE_BOOL:
897         (*pr)("@", client_data);
898         (*pr)(cn->u.boolean.value, client_data);
899         (*pr)(" ", client_data);
900         mods = cn->u.boolean.modifiers;
901         if (!strcmp(cn->u.boolean.value, "prox"))
902         {
903             r = cql_pr_prox(ct, mods, addinfo, pr, client_data);
904             if (r)
905                 return r;
906         }
907         else if (mods)
908         {
909             /* Boolean modifiers other than on proximity not supported */
910             wrbuf_puts(addinfo, mods->u.st.index);
911             return YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER;
912         }
913
914         r = emit_node(ct, cn->u.boolean.left, addinfo, pr, client_data);
915         if (r)
916             return r;
917         r = emit_node(ct, cn->u.boolean.right, addinfo, pr, client_data);
918         if (r)
919             return r;
920         break;
921     case CQL_NODE_SORT:
922         r = emit_node(ct, cn->u.sort.search, addinfo, pr, client_data);
923         break;
924     default:
925         fprintf(stderr, "Fatal: impossible CQL node-type %d\n", cn->which);
926         abort();
927     }
928     return r;
929 }
930
931 int cql_transform_r(cql_transform_t ct, struct cql_node *cn,
932                     WRBUF addinfo,
933                     void (*pr)(const char *buf, void *client_data),
934                     void *client_data)
935 {
936     struct cql_prop_entry *e;
937     NMEM nmem = nmem_create();
938     int r;
939
940     for (e = ct->entry; e ; e = e->next)
941     {
942         if (!cql_strncmp(e->pattern, "set.", 4))
943             cql_apply_prefix(nmem, cn, e->pattern+4, e->value);
944         else if (!cql_strcmp(e->pattern, "set"))
945             cql_apply_prefix(nmem, cn, 0, e->value);
946     }
947     r  = emit_node(ct, cn, addinfo, pr, client_data);
948     nmem_destroy(nmem);
949     return r;
950 }
951
952 int cql_transform(cql_transform_t ct, struct cql_node *cn,
953                   void (*pr)(const char *buf, void *client_data),
954                   void *client_data)
955 {
956     WRBUF addinfo = wrbuf_alloc();
957     int r = cql_transform_r(ct, cn, addinfo, pr, client_data);
958     cql_transform_set_error(ct, r, wrbuf_cstr(addinfo));
959     wrbuf_destroy(addinfo);
960     return r;
961 }
962
963 int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f)
964 {
965     return cql_transform(ct, cn, cql_fputs, f);
966 }
967
968 int cql_transform_buf(cql_transform_t ct, struct cql_node *cn,
969                       char *out, int max)
970 {
971     struct cql_buf_write_info info;
972     int r;
973
974     info.off = 0;
975     info.max = max;
976     info.buf = out;
977     r = cql_transform(ct, cn, cql_buf_write_handler, &info);
978     if (info.off < 0) {
979         /* Attempt to write past end of buffer.  For some reason, this
980            SRW diagnostic is deprecated, but it's so perfect for our
981            purposes that it would be stupid not to use it. */
982         char numbuf[30];
983         sprintf(numbuf, "%ld", (long) info.max);
984         cql_transform_set_error(ct, YAZ_SRW_TOO_MANY_CHARS_IN_QUERY, numbuf);
985         return -1;
986     }
987     if (info.off >= 0)
988         info.buf[info.off] = '\0';
989     return r;
990 }
991
992 int cql_transform_error(cql_transform_t ct, const char **addinfo)
993 {
994     *addinfo = wrbuf_len(ct->addinfo) ? wrbuf_cstr(ct->addinfo) : 0;
995     return ct->error;
996 }
997
998 void cql_transform_set_error(cql_transform_t ct, int error, const char *addinfo)
999 {
1000     wrbuf_rewind(ct->addinfo);
1001     if (addinfo)
1002         wrbuf_puts(ct->addinfo, addinfo);
1003     ct->error = error;
1004 }
1005
1006 /*
1007  * Local variables:
1008  * c-basic-offset: 4
1009  * c-file-style: "Stroustrup"
1010  * indent-tabs-mode: nil
1011  * End:
1012  * vim: shiftwidth=4 tabstop=8 expandtab
1013  */
1014