cql2ccl: handle CQL \-sequences differently
[yaz-moved-to-github.git] / src / cql2ccl.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) 1995-2011 Index Data
3  * See the file LICENSE for details.
4  */
5 /**
6  * \file cql2ccl.c
7  * \brief Implements CQL to XCQL conversion.
8  */
9 #if HAVE_CONFIG_H
10 #include <config.h>
11 #endif
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdio.h>
16
17 #include <yaz/cql.h>
18
19 static int cql_to_ccl_r(struct cql_node *cn, 
20                         void (*pr)(const char *buf, void *client_data),
21                         void *client_data);
22
23 static void pr_term(struct cql_node *cn,
24                     void (*pr)(const char *buf, void *client_data),
25                     void *client_data)
26 {
27     while (cn)
28     {
29         if (! *cn->u.st.term) /* empty term special case */
30             pr("\"\"", client_data);
31         else
32         {
33             const char *cp;
34             int quote_mode = 0;
35             for (cp = cn->u.st.term; *cp; cp++)
36             {
37                 char x[4];
38                 
39                 if (*cp == '\\' && cp[1])
40                 {
41                     if (!quote_mode)
42                     {
43                         pr("\"", client_data);
44                         quote_mode = 1;
45                     }
46                     cp++;
47                     if (*cp == '\"' || *cp == '\\')
48                         pr("\\\"", client_data);
49                     else
50                     {
51                         x[0] = *cp;
52                         x[1] = '\0';
53                         pr(x, client_data);
54                     }
55                 }
56                 else if (*cp == '*')
57                 {
58                     if (quote_mode)
59                     {
60                         pr("\"", client_data);
61                         quote_mode = 0;
62                     }
63                     pr("?", client_data);
64                 }
65                 else if (*cp == '?')
66                 {
67                     if (quote_mode)
68                     {
69                         pr("\"", client_data);
70                         quote_mode = 0;
71                     }
72                     pr("#", client_data);
73                 }
74                 else
75                 {
76                     if (!quote_mode)
77                     {
78                         pr("\"", client_data);
79                         quote_mode = 1;
80                     }
81                     x[0] = *cp;
82                     x[1] = '\0';
83                     pr(x, client_data);
84                 }
85             }
86             if (quote_mode)
87                 pr("\"", client_data);
88         }
89         if (cn->u.st.extra_terms)
90             pr(" ", client_data);
91         cn = cn->u.st.extra_terms;
92     }
93 }
94
95 static int node(struct cql_node *cn, 
96                 void (*pr)(const char *buf, void *client_data),
97                 void *client_data)
98 {
99     const char *ccl_field = 0;
100     const char *split_op = 0;
101     const char *ccl_rel = 0;
102     const char *rel = cn->u.st.relation;
103
104     if (cn->u.st.index && strcmp(cn->u.st.index,
105                                  "cql.serverChoice"))
106         ccl_field = cn->u.st.index;
107
108     if (!rel)
109         ;
110     else if (!strcmp(rel, "<") || !strcmp(rel, "<=")
111              || !strcmp(rel, ">") || !strcmp(rel, ">=")
112              || !strcmp(rel, "<>") || !strcmp(rel, "="))
113         ccl_rel = rel;
114     else if (!strcmp(rel, "all"))
115     {
116         ccl_rel = "=";
117         split_op = "and";
118     }
119     else if (!strcmp(rel, "any"))
120     {
121         ccl_rel = "=";
122         split_op = "or";
123     }
124     else if (!strcmp(rel, "==") || !strcmp(rel, "adj"))
125     {
126         ccl_rel = "=";
127     }
128     else
129     {
130         /* unsupported relation */
131         return -1;
132     }
133     if (!split_op)
134     {
135         if (ccl_field && ccl_rel)
136         {
137             pr(ccl_field, client_data);
138             pr(ccl_rel, client_data);
139         }
140         pr_term(cn, pr, client_data);
141     }
142     else
143     {
144         const char *cp = cn->u.st.term;
145         
146         while (1)
147         {
148             if (*cp == '\0')
149                 break;
150             if (ccl_field && ccl_rel)
151             {
152                 pr(ccl_field, client_data);
153                 pr(ccl_rel, client_data);
154             }
155             while (*cp && *cp != ' ')
156             {
157                 char x[2];
158                 if (*cp == '*')
159                     x[0] = '?';
160                 else
161                     x[0] = *cp;
162                 x[1] = '\0';
163                 pr(x, client_data);
164                 cp++;
165             }
166             while (*cp == ' ')
167                 cp++;
168             if (*cp == '\0')
169                 break;
170             pr(" ", client_data);
171             pr(split_op, client_data);
172             pr(" ", client_data);            
173         }
174     }
175     return 0;
176 }
177
178
179 static int bool(struct cql_node *cn, 
180                 void (*pr)(const char *buf, void *client_data),
181                 void *client_data)
182 {
183     char *value = cn->u.boolean.value;
184     int r;
185
186     pr("(", client_data);
187     r = cql_to_ccl_r(cn->u.boolean.left, pr, client_data);
188     if (r)
189         return r;
190     
191     pr(" ", client_data);
192
193     if (strcmp(value, "prox"))
194     {   /* not proximity. assuming boolean */
195         pr(value, client_data);
196     }
197     else
198     {
199         struct cql_node *n = cn->u.boolean.modifiers;
200         int ordered = 0;
201         int distance = 1;
202         for (; n ; n = n->u.st.modifiers)
203             if (n->which == CQL_NODE_ST)
204             {
205                 if (!strcmp(n->u.st.index, "unit"))
206                 {
207                     if (!strcmp(n->u.st.term, "word"))
208                         ;
209                     else
210                         return -1;
211                 }
212                 else if (!strcmp(n->u.st.index, "distance"))
213                 {
214                     if (!strcmp(n->u.st.relation, "<="))
215                         distance = atoi(n->u.st.term);
216                     else if (!strcmp(n->u.st.relation, "<"))
217                         distance = atoi(n->u.st.term) - 1;
218                     else
219                         return -1;
220                 }
221                 else if (!strcmp(n->u.st.index, "unordered"))
222                 {
223                     ordered = 0;
224                 }
225                 else if (!strcmp(n->u.st.index, "ordered"))
226                 {
227                     ordered = 1;
228                 }
229                 else
230                     return -1;
231             }
232         pr(ordered ? "!" : "%", client_data);
233         if (distance != 1)
234         {
235             char x[40];
236             sprintf(x, "%d", distance);
237             pr(x, client_data);
238         }
239     }
240     pr(" ", client_data);
241
242     r = cql_to_ccl_r(cn->u.boolean.right, pr, client_data);
243     pr(")", client_data);
244     return r;
245 }
246
247 static int cql_to_ccl_r(struct cql_node *cn, 
248                         void (*pr)(const char *buf, void *client_data),
249                         void *client_data)
250 {
251     if (!cn)
252         return -1;
253
254     switch (cn->which)
255     {
256     case CQL_NODE_ST:
257         return node(cn, pr, client_data);
258     case CQL_NODE_BOOL:
259         return bool(cn, pr, client_data);
260     case CQL_NODE_SORT:
261         return cql_to_ccl_r(cn->u.sort.search, pr, client_data);
262     }
263     return -1;
264 }
265
266 int cql_to_ccl(struct cql_node *cn, 
267                void (*pr)(const char *buf, void *client_data),
268                void *client_data)
269 {
270     return cql_to_ccl_r(cn, pr, client_data);
271 }
272
273 void cql_to_ccl_stdio(struct cql_node *cn, FILE *f)
274 {
275     cql_to_ccl(cn, cql_fputs, f);
276 }
277
278 int cql_to_ccl_buf(struct cql_node *cn, char *out, int max)
279 {
280     struct cql_buf_write_info info;
281     int r;
282     info.off = 0;
283     info.max = max;
284     info.buf = out;
285     r = cql_to_ccl(cn, cql_buf_write_handler, &info);
286     if (info.off >= 0)
287         info.buf[info.off] = '\0';
288     else
289         return -2; /* buffer overflow */
290     return r;
291 }
292
293 /*
294  * Local variables:
295  * c-basic-offset: 4
296  * c-file-style: "Stroustrup"
297  * indent-tabs-mode: nil
298  * End:
299  * vim: shiftwidth=4 tabstop=8 expandtab
300  */
301