1 /* $Id: cqltransform.c,v 1.11 2004-10-03 22:34:07 adam Exp $
2 Copyright (C) 2002-2004
5 This file is part of the YAZ toolkit.
11 * \file cqltransform.c
12 * \brief Implements CQL transform (CQL to RPN conversion).
18 #include <yaz/xmalloc.h>
20 struct cql_prop_entry {
23 struct cql_prop_entry *next;
26 struct cql_transform_t_ {
27 struct cql_prop_entry *entry;
32 cql_transform_t cql_transform_open_FILE(FILE *f)
35 cql_transform_t ct = (cql_transform_t) xmalloc (sizeof(*ct));
36 struct cql_prop_entry **pp = &ct->entry;
40 while (fgets(line, sizeof(line)-1, f))
42 const char *cp_value_start;
43 const char *cp_value_end;
44 const char *cp_pattern_end;
45 const char *cp = line;
46 while (*cp && !strchr(" \t=\r\n#", *cp))
51 while (*cp && strchr(" \t\r\n", *cp))
56 while (*cp && strchr(" \t\r\n", *cp))
59 if (!(cp_value_end = strchr(cp, '#')))
60 cp_value_end = strlen(line) + line;
62 if (cp_value_end != cp_value_start &&
63 strchr(" \t\r\n", cp_value_end[-1]))
65 *pp = (struct cql_prop_entry *) xmalloc (sizeof(**pp));
66 (*pp)->pattern = (char *) xmalloc (cp_pattern_end - line + 1);
67 memcpy ((*pp)->pattern, line, cp_pattern_end - line);
68 (*pp)->pattern[cp_pattern_end-line] = 0;
70 (*pp)->value = (char *) xmalloc (cp_value_end - cp_value_start + 1);
71 if (cp_value_start != cp_value_end)
72 memcpy ((*pp)->value, cp_value_start, cp_value_end-cp_value_start);
73 (*pp)->value[cp_value_end - cp_value_start] = 0;
80 void cql_transform_close(cql_transform_t ct)
82 struct cql_prop_entry *pe;
88 struct cql_prop_entry *pe_next = pe->next;
99 cql_transform_t cql_transform_open_fname(const char *fname)
102 FILE *f = fopen(fname, "r");
105 ct = cql_transform_open_FILE(f);
110 static const char *cql_lookup_property(cql_transform_t ct,
111 const char *pat1, const char *pat2,
115 struct cql_prop_entry *e;
117 if (pat1 && pat2 && pat3)
118 sprintf (pattern, "%.39s.%.39s.%.39s", pat1, pat2, pat3);
119 else if (pat1 && pat2)
120 sprintf (pattern, "%.39s.%.39s", pat1, pat2);
121 else if (pat1 && pat3)
122 sprintf (pattern, "%.39s.%.39s", pat1, pat3);
124 sprintf (pattern, "%.39s", pat1);
128 for (e = ct->entry; e; e = e->next)
130 if (!strcmp(e->pattern, pattern))
136 int cql_pr_attr_uri(cql_transform_t ct, const char *category,
137 const char *uri, const char *val, const char *default_val,
138 void (*pr)(const char *buf, void *client_data),
143 const char *eval = val ? val : default_val;
144 const char *prefix = 0;
148 struct cql_prop_entry *e;
150 for (e = ct->entry; e; e = e->next)
151 if (!memcmp(e->pattern, "set.", 4) && e->value &&
152 !strcmp(e->value, uri))
154 prefix = e->pattern+4;
157 /* must have a prefix now - if not it's an error */
163 res = cql_lookup_property(ct, category, prefix, eval);
165 res = cql_lookup_property(ct, category, prefix, "*");
171 const char *cp0 = res, *cp1;
172 while ((cp1 = strchr(cp0, '=')))
174 while (*cp1 && *cp1 != ' ')
176 if (cp1 - cp0 >= sizeof(buf))
178 memcpy (buf, cp0, cp1 - cp0);
180 (*pr)("@attr ", client_data);
181 (*pr)(buf, client_data);
182 (*pr)(" ", client_data);
190 if (errcode && !ct->error)
194 ct->addinfo = xstrdup(val);
201 int cql_pr_attr(cql_transform_t ct, const char *category,
202 const char *val, const char *default_val,
203 void (*pr)(const char *buf, void *client_data),
207 return cql_pr_attr_uri(ct, category, 0 /* uri */,
208 val, default_val, pr, client_data, errcode);
212 /* Returns location of first wildcard character in the `length'
213 * characters starting at `term', or a null pointer of there are
214 * none -- like memchr().
216 static const char *wcchar(const char *term, int length)
218 const char *best = 0;
222 for (whichp = "*?"; *whichp != '\0'; whichp++) {
223 current = (const char *) memchr(term, *whichp, length);
224 if (current != 0 && (best == 0 || current < best))
232 void emit_term(cql_transform_t ct,
233 const char *term, int length,
234 void (*pr)(const char *buf, void *client_data),
240 if (length > 1 && term[0] == '^' && term[length-1] == '^')
242 cql_pr_attr(ct, "position", "firstAndLast", 0,
243 pr, client_data, 32);
247 else if (term[0] == '^')
249 cql_pr_attr(ct, "position", "first", 0,
250 pr, client_data, 32);
254 else if (term[length-1] == '^')
256 cql_pr_attr(ct, "position", "last", 0,
257 pr, client_data, 32);
262 cql_pr_attr(ct, "position", "any", 0,
263 pr, client_data, 32);
269 /* Check for well-known globbing patterns that represent
270 * simple truncation attributes as expected by, for example,
271 * Bath-compliant server. If we find such a pattern but
272 * there's no mapping for it, that's fine: we just use a
273 * general pattern-matching attribute.
275 if (length > 1 && term[0] == '*' && term[length-1] == '*' &&
276 wcchar(term+1, length-2) == 0 &&
277 cql_pr_attr(ct, "truncation", "both", 0,
278 pr, client_data, 0)) {
282 else if (term[0] == '*' &&
283 wcchar(term+1, length-1) == 0 &&
284 cql_pr_attr(ct, "truncation", "left", 0,
285 pr, client_data, 0)) {
289 else if (term[length-1] == '*' &&
290 wcchar(term, length-1) == 0 &&
291 cql_pr_attr(ct, "truncation", "right", 0,
292 pr, client_data, 0)) {
295 else if (wcchar(term, length))
297 /* We have one or more wildcard characters, but not in a
298 * way that can be dealt with using only the standard
299 * left-, right- and both-truncation attributes. We need
300 * to translate the pattern into a Z39.58-type pattern,
301 * which has been supported in BIB-1 since 1996. If
302 * there's no configuration element for "truncation.z3958"
303 * we indicate this as error 28 "Masking character not
308 cql_pr_attr(ct, "truncation", "z3958", 0,
309 pr, client_data, 28);
310 mem = (char *) xmalloc(length+1);
311 for (i = 0; i < length; i++) {
312 if (term[i] == '*') mem[i] = '?';
313 else if (term[i] == '?') mem[i] = '#';
314 else mem[i] = term[i];
320 /* No masking characters. Use "truncation.none" if given. */
321 cql_pr_attr(ct, "truncation", "none", 0,
326 (*pr)("\"", client_data);
327 for (i = 0; i<length; i++)
332 (*pr)(buf, client_data);
334 (*pr)("\" ", client_data);
337 void emit_wordlist(cql_transform_t ct,
339 void (*pr)(const char *buf, void *client_data),
343 const char *cp0 = cn->u.st.term;
345 const char *last_term = 0;
351 cp1 = strchr(cp0, ' ');
354 (*pr)("@", client_data);
355 (*pr)(op, client_data);
356 (*pr)(" ", client_data);
357 emit_term(ct, last_term, last_length, pr, client_data);
361 last_length = cp1 - cp0;
363 last_length = strlen(cp0);
367 emit_term(ct, last_term, last_length, pr, client_data);
370 void cql_transform_r(cql_transform_t ct,
372 void (*pr)(const char *buf, void *client_data),
382 ns = cn->u.st.index_uri;
385 if (!strcmp(ns, cql_uri())
386 && cn->u.st.index && !strcmp(cn->u.st.index, "resultSet"))
388 (*pr)("@set \"", client_data);
389 (*pr)(cn->u.st.term, client_data);
390 (*pr)("\" ", client_data);
393 cql_pr_attr_uri(ct, "index", ns,
394 cn->u.st.index, "serverChoice",
395 pr, client_data, 16);
405 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "="))
406 cql_pr_attr(ct, "relation", "eq", "scr",
407 pr, client_data, 19);
408 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "<="))
409 cql_pr_attr(ct, "relation", "le", "scr",
410 pr, client_data, 19);
411 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, ">="))
412 cql_pr_attr(ct, "relation", "ge", "scr",
413 pr, client_data, 19);
415 cql_pr_attr(ct, "relation", cn->u.st.relation, "eq",
416 pr, client_data, 19);
417 if (cn->u.st.modifiers)
419 struct cql_node *mod = cn->u.st.modifiers;
420 for (; mod; mod = mod->u.st.modifiers)
422 cql_pr_attr(ct, "relationModifier", mod->u.st.term, 0,
423 pr, client_data, 20);
426 cql_pr_attr(ct, "structure", cn->u.st.relation, 0,
427 pr, client_data, 24);
428 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "all"))
430 emit_wordlist(ct, cn, pr, client_data, "and");
432 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "any"))
434 emit_wordlist(ct, cn, pr, client_data, "or");
438 emit_term(ct, cn->u.st.term, strlen(cn->u.st.term),
443 (*pr)("@", client_data);
444 (*pr)(cn->u.boolean.value, client_data);
445 (*pr)(" ", client_data);
447 cql_transform_r(ct, cn->u.boolean.left, pr, client_data);
448 cql_transform_r(ct, cn->u.boolean.right, pr, client_data);
452 int cql_transform(cql_transform_t ct,
454 void (*pr)(const char *buf, void *client_data),
457 struct cql_prop_entry *e;
464 for (e = ct->entry; e ; e = e->next)
466 if (!memcmp(e->pattern, "set.", 4))
467 cql_apply_prefix(cn, e->pattern+4, e->value);
468 else if (!strcmp(e->pattern, "set"))
469 cql_apply_prefix(cn, 0, e->value);
471 cql_transform_r (ct, cn, pr, client_data);
476 int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f)
478 return cql_transform(ct, cn, cql_fputs, f);
481 int cql_transform_buf(cql_transform_t ct, struct cql_node *cn,
484 struct cql_buf_write_info info;
490 r = cql_transform(ct, cn, cql_buf_write_handler, &info);
492 info.buf[info.off] = '\0';
496 int cql_transform_error(cql_transform_t ct, const char **addinfo)
498 *addinfo = ct->addinfo;