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