Reformat
[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(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 == ' ')
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             }
134             pr_term(&cp, split_op ? 1 : 0, pr, client_data);
135             if (!split_op)
136                 break;
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         if (cn->u.st.extra_terms)
146         {
147             pr(" ", client_data);
148             if (split_op)
149             {
150                 pr(split_op, client_data);
151                 pr(" ", client_data);
152             }
153         }
154     }
155     return 0;
156 }
157
158
159 static int bool(struct cql_node *cn, 
160                 void (*pr)(const char *buf, void *client_data),
161                 void *client_data)
162 {
163     char *value = cn->u.boolean.value;
164     int r;
165
166     pr("(", client_data);
167     r = cql_to_ccl_r(cn->u.boolean.left, pr, client_data);
168     if (r)
169         return r;
170     
171     pr(" ", client_data);
172
173     if (strcmp(value, "prox"))
174     {   /* not proximity. assuming boolean */
175         pr(value, client_data);
176     }
177     else
178     {
179         struct cql_node *n = cn->u.boolean.modifiers;
180         int ordered = 0;
181         int distance = 1;
182         for (; n ; n = n->u.st.modifiers)
183             if (n->which == CQL_NODE_ST)
184             {
185                 if (!strcmp(n->u.st.index, "unit"))
186                 {
187                     if (!strcmp(n->u.st.term, "word"))
188                         ;
189                     else
190                         return -1;
191                 }
192                 else if (!strcmp(n->u.st.index, "distance"))
193                 {
194                     if (!strcmp(n->u.st.relation, "<="))
195                         distance = atoi(n->u.st.term);
196                     else if (!strcmp(n->u.st.relation, "<"))
197                             distance = atoi(n->u.st.term) - 1;
198                     else
199                         return -1;
200                 }
201                 else if (!strcmp(n->u.st.index, "unordered"))
202                 {
203                     ordered = 0;
204                 }
205                 else if (!strcmp(n->u.st.index, "ordered"))
206                 {
207                     ordered = 1;
208                 }
209                 else
210                     return -1;
211             }
212         pr(ordered ? "!" : "%", client_data);
213         if (distance != 1)
214         {
215             char x[40];
216             sprintf(x, "%d", distance);
217             pr(x, client_data);
218         }
219     }
220     pr(" ", client_data);
221
222     r = cql_to_ccl_r(cn->u.boolean.right, pr, client_data);
223     pr(")", client_data);
224     return r;
225 }
226
227 static int cql_to_ccl_r(struct cql_node *cn, 
228                         void (*pr)(const char *buf, void *client_data),
229                         void *client_data)
230 {
231     if (!cn)
232         return -1;
233
234     switch (cn->which)
235     {
236     case CQL_NODE_ST:
237         return node(cn, pr, client_data);
238     case CQL_NODE_BOOL:
239         return bool(cn, pr, client_data);
240     case CQL_NODE_SORT:
241         return cql_to_ccl_r(cn->u.sort.search, pr, client_data);
242     }
243     return -1;
244 }
245
246 int cql_to_ccl(struct cql_node *cn, 
247                void (*pr)(const char *buf, void *client_data),
248                void *client_data)
249 {
250     return cql_to_ccl_r(cn, pr, client_data);
251 }
252
253 void cql_to_ccl_stdio(struct cql_node *cn, FILE *f)
254 {
255     cql_to_ccl(cn, cql_fputs, f);
256 }
257
258 int cql_to_ccl_buf(struct cql_node *cn, char *out, int max)
259 {
260     struct cql_buf_write_info info;
261     int r;
262     info.off = 0;
263     info.max = max;
264     info.buf = out;
265     r = cql_to_ccl(cn, cql_buf_write_handler, &info);
266     if (info.off >= 0)
267         info.buf[info.off] = '\0';
268     else
269         return -2; /* buffer overflow */
270     return r;
271 }
272
273 /*
274  * Local variables:
275  * c-basic-offset: 4
276  * c-file-style: "Stroustrup"
277  * indent-tabs-mode: nil
278  * End:
279  * vim: shiftwidth=4 tabstop=8 expandtab
280  */
281