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