Add Ubuntu Vivid 15.04
[yaz-moved-to-github.git] / src / cql2ccl.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 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(const char **cpp, int stop_at_space,
24                     void (*pr)(const char *buf, void *client_data),
25                     void *client_data)
26 {
27     const char *cp;
28     int quote_mode = 0;
29     for (cp = *cpp; *cp; cp++)
30     {
31         char x[4];
32
33         if (*cp == '\\' && cp[1])
34         {
35             if (!quote_mode)
36             {
37                 pr("\"", client_data);
38                 quote_mode = 1;
39             }
40             cp++;
41             if (*cp == '\"' || *cp == '\\')
42                 pr("\\", client_data);
43             x[0] = *cp;
44             x[1] = '\0';
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 if (*cp == ' ' && stop_at_space)
66             break;
67         else
68         {
69             if (!quote_mode)
70             {
71                 pr("\"", client_data);
72                 quote_mode = 1;
73             }
74             x[0] = *cp;
75             x[1] = '\0';
76             pr(x, client_data);
77         }
78     }
79     if (quote_mode)
80         pr("\"", client_data);
81     if (cp == *cpp)
82         pr("\"\"", client_data);
83     *cpp = cp;
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     for (; cn; cn = cn->u.st.extra_terms)
125     {
126         const char *cp = cn->u.st.term;
127         while (1)
128         {
129             if (ccl_field && ccl_rel)
130             {
131                 pr(ccl_field, client_data);
132                 pr(ccl_rel, client_data);
133                 if (!split_op)
134                     ccl_rel = 0;
135             }
136             pr_term(&cp, split_op ? 1 : 0, pr, client_data);
137             while (*cp == ' ')
138                 cp++;
139             if (*cp == '\0')
140                 break;
141             pr(" ", client_data);
142             if (split_op)
143             {
144                 pr(split_op, client_data);
145                 pr(" ", client_data);
146             }
147         }
148         if (cn->u.st.extra_terms)
149         {
150             pr(" ", client_data);
151             if (split_op)
152             {
153                 pr(split_op, client_data);
154                 pr(" ", client_data);
155             }
156         }
157     }
158     return 0;
159 }
160
161
162 static int bool(struct cql_node *cn,
163                 void (*pr)(const char *buf, void *client_data),
164                 void *client_data)
165 {
166     char *value = cn->u.boolean.value;
167     int r;
168
169     pr("(", client_data);
170     r = cql_to_ccl_r(cn->u.boolean.left, pr, client_data);
171     if (r)
172         return r;
173
174     pr(") ", client_data);
175
176     if (strcmp(value, "prox"))
177     {   /* not proximity. assuming boolean */
178         pr(value, client_data);
179     }
180     else
181     {
182         struct cql_node *n = cn->u.boolean.modifiers;
183         int ordered = 0;
184         int distance = 1;
185         for (; n ; n = n->u.st.modifiers)
186             if (n->which == CQL_NODE_ST)
187             {
188                 if (!strcmp(n->u.st.index, "unit"))
189                 {
190                     if (!strcmp(n->u.st.term, "word"))
191                         ;
192                     else
193                         return -1;
194                 }
195                 else if (!strcmp(n->u.st.index, "distance"))
196                 {
197                     if (!strcmp(n->u.st.relation, "<="))
198                         distance = atoi(n->u.st.term);
199                     else if (!strcmp(n->u.st.relation, "<"))
200                             distance = atoi(n->u.st.term) - 1;
201                     else
202                         return -1;
203                 }
204                 else if (!strcmp(n->u.st.index, "unordered"))
205                 {
206                     ordered = 0;
207                 }
208                 else if (!strcmp(n->u.st.index, "ordered"))
209                 {
210                     ordered = 1;
211                 }
212                 else
213                     return -1;
214             }
215         pr(ordered ? "!" : "%", client_data);
216         if (distance != 1)
217         {
218             char x[40];
219             sprintf(x, "%d", distance);
220             pr(x, client_data);
221         }
222     }
223     pr(" (", client_data);
224
225     r = cql_to_ccl_r(cn->u.boolean.right, pr, client_data);
226     pr(")", client_data);
227     return r;
228 }
229
230 static int cql_to_ccl_r(struct cql_node *cn,
231                         void (*pr)(const char *buf, void *client_data),
232                         void *client_data)
233 {
234     if (!cn)
235         return -1;
236
237     switch (cn->which)
238     {
239     case CQL_NODE_ST:
240         return node(cn, pr, client_data);
241     case CQL_NODE_BOOL:
242         return bool(cn, pr, client_data);
243     case CQL_NODE_SORT:
244         return cql_to_ccl_r(cn->u.sort.search, pr, client_data);
245     }
246     return -1;
247 }
248
249 int cql_to_ccl(struct cql_node *cn,
250                void (*pr)(const char *buf, void *client_data),
251                void *client_data)
252 {
253     return cql_to_ccl_r(cn, pr, client_data);
254 }
255
256 void cql_to_ccl_stdio(struct cql_node *cn, FILE *f)
257 {
258     cql_to_ccl(cn, cql_fputs, f);
259 }
260
261 int cql_to_ccl_buf(struct cql_node *cn, char *out, int max)
262 {
263     struct cql_buf_write_info info;
264     int r;
265     info.off = 0;
266     info.max = max;
267     info.buf = out;
268     r = cql_to_ccl(cn, cql_buf_write_handler, &info);
269     if (info.off >= 0)
270         info.buf[info.off] = '\0';
271     else
272         return -2; /* buffer overflow */
273     return r;
274 }
275
276 /*
277  * Local variables:
278  * c-basic-offset: 4
279  * c-file-style: "Stroustrup"
280  * indent-tabs-mode: nil
281  * End:
282  * vim: shiftwidth=4 tabstop=8 expandtab
283  */
284