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