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