1 /* $Id: cqltransform.c,v 1.8 2004-03-15 21:39:06 adam Exp $
2 Copyright (C) 2002-2004
5 This file is part of the YAZ toolkit.
13 #include <yaz/xmalloc.h>
15 struct cql_prop_entry {
18 struct cql_prop_entry *next;
21 struct cql_transform_t_ {
22 struct cql_prop_entry *entry;
27 cql_transform_t cql_transform_open_FILE(FILE *f)
30 cql_transform_t ct = (cql_transform_t) xmalloc (sizeof(*ct));
31 struct cql_prop_entry **pp = &ct->entry;
35 while (fgets(line, sizeof(line)-1, f))
37 const char *cp_value_start;
38 const char *cp_value_end;
39 const char *cp_pattern_end;
40 const char *cp = line;
41 while (*cp && !strchr(" \t=\r\n#", *cp))
46 while (*cp && strchr(" \t\r\n", *cp))
51 while (*cp && strchr(" \t\r\n", *cp))
54 if (!(cp_value_end = strchr(cp, '#')))
55 cp_value_end = strlen(line) + line;
57 if (cp_value_end != cp_value_start &&
58 strchr(" \t\r\n", cp_value_end[-1]))
60 *pp = (struct cql_prop_entry *) xmalloc (sizeof(**pp));
61 (*pp)->pattern = (char *) xmalloc (cp_pattern_end - line + 1);
62 memcpy ((*pp)->pattern, line, cp_pattern_end - line);
63 (*pp)->pattern[cp_pattern_end-line] = 0;
65 (*pp)->value = (char *) xmalloc (cp_value_end - cp_value_start + 1);
66 if (cp_value_start != cp_value_end)
67 memcpy ((*pp)->value, cp_value_start, cp_value_end-cp_value_start);
68 (*pp)->value[cp_value_end - cp_value_start] = 0;
75 void cql_transform_close(cql_transform_t ct)
77 struct cql_prop_entry *pe;
83 struct cql_prop_entry *pe_next = pe->next;
94 cql_transform_t cql_transform_open_fname(const char *fname)
97 FILE *f = fopen(fname, "r");
100 ct = cql_transform_open_FILE(f);
105 static const char *cql_lookup_property(cql_transform_t ct,
106 const char *pat1, const char *pat2,
110 struct cql_prop_entry *e;
112 if (pat1 && pat2 && pat3)
113 sprintf (pattern, "%.39s.%.39s.%.39s", pat1, pat2, pat3);
114 else if (pat1 && pat2)
115 sprintf (pattern, "%.39s.%.39s", pat1, pat2);
116 else if (pat1 && pat3)
117 sprintf (pattern, "%.39s.%.39s", pat1, pat3);
119 sprintf (pattern, "%.39s", pat1);
123 for (e = ct->entry; e; e = e->next)
125 if (!strcmp(e->pattern, pattern))
131 int cql_pr_attr_uri(cql_transform_t ct, const char *category,
132 const char *uri, const char *val, const char *default_val,
133 void (*pr)(const char *buf, void *client_data),
138 const char *eval = val ? val : default_val;
139 const char *prefix = 0;
143 struct cql_prop_entry *e;
145 for (e = ct->entry; e; e = e->next)
146 if (!memcmp(e->pattern, "set.", 4) && e->value &&
147 !strcmp(e->value, uri))
149 prefix = e->pattern+4;
152 /* must have a prefix now - if not it's an error */
158 res = cql_lookup_property(ct, category, prefix, eval);
160 res = cql_lookup_property(ct, category, prefix, "*");
166 const char *cp0 = res, *cp1;
167 while ((cp1 = strchr(cp0, '=')))
169 while (*cp1 && *cp1 != ' ')
171 if (cp1 - cp0 >= sizeof(buf))
173 memcpy (buf, cp0, cp1 - cp0);
175 (*pr)("@attr ", client_data);
176 (*pr)(buf, client_data);
177 (*pr)(" ", client_data);
185 if (errcode && !ct->error)
189 ct->addinfo = xstrdup(val);
196 int cql_pr_attr(cql_transform_t ct, const char *category,
197 const char *val, const char *default_val,
198 void (*pr)(const char *buf, void *client_data),
202 return cql_pr_attr_uri(ct, category, 0 /* uri */,
203 val, default_val, pr, client_data, errcode);
207 /* Returns location of first wildcard character in the `length'
208 * characters starting at `term', or a null pointer of there are
209 * none -- like memchr().
211 static char *wcchar(const char *term, int length)
217 for (whichp = "*?"; *whichp != '\0'; whichp++) {
218 current = memchr(term, *whichp, length);
219 if (current != 0 && (best == 0 || current < best))
227 void emit_term(cql_transform_t ct,
228 const char *term, int length,
229 void (*pr)(const char *buf, void *client_data),
235 if (length > 1 && term[0] == '^' && term[length-1] == '^')
237 cql_pr_attr(ct, "position", "firstAndLast", 0,
238 pr, client_data, 32);
242 else if (term[0] == '^')
244 cql_pr_attr(ct, "position", "first", 0,
245 pr, client_data, 32);
249 else if (term[length-1] == '^')
251 cql_pr_attr(ct, "position", "last", 0,
252 pr, client_data, 32);
257 cql_pr_attr(ct, "position", "any", 0,
258 pr, client_data, 32);
264 /* Check for well-known globbing patterns that represent
265 * simple truncation attributes as expected by, for example,
266 * Bath-compliant server. If we find such a pattern but
267 * there's no mapping for it, that's fine: we just use a
268 * general pattern-matching attribute.
270 if (length > 1 && term[0] == '*' && term[length-1] == '*' &&
271 wcchar(term+1, length-2) == 0 &&
272 cql_pr_attr(ct, "truncation", "both", 0,
273 pr, client_data, 0)) {
277 else if (term[0] == '*' &&
278 wcchar(term+1, length-1) == 0 &&
279 cql_pr_attr(ct, "truncation", "left", 0,
280 pr, client_data, 0)) {
284 else if (term[length-1] == '*' &&
285 wcchar(term, length-1) == 0 &&
286 cql_pr_attr(ct, "truncation", "right", 0,
287 pr, client_data, 0)) {
290 else if (wcchar(term, length))
292 /* We have one or more wildcard characters, but not in a
293 * way that can be dealt with using only the standard
294 * left-, right- and both-truncation attributes. We need
295 * to translate the pattern into a Z39.58-type pattern,
296 * which has been supported in BIB-1 since 1996. If
297 * there's no configuration element for "truncation.z3958"
298 * we indicate this as error 28 "Masking character not
303 cql_pr_attr(ct, "truncation", "z3958", 0,
304 pr, client_data, 28);
305 mem = xmalloc(length+1);
306 for (i = 0; i < length; i++) {
307 if (term[i] == '*') mem[i] = '?';
308 else if (term[i] == '?') mem[i] = '#';
309 else mem[i] = term[i];
315 /* No masking characters. If there's no "truncation.none"
316 * configuration element, that's an error which we
317 * indicate (rather tangentially) as 30 "Too many masking
318 * characters in term". 28 would be equally meaningful
319 * (or meaningless) but using a different value allows us
320 * to differentiate between this case and the previous
323 cql_pr_attr(ct, "truncation", "none", 0,
324 pr, client_data, 30);
328 (*pr)("\"", client_data);
329 for (i = 0; i<length; i++)
334 (*pr)(buf, client_data);
336 (*pr)("\" ", client_data);
339 void emit_wordlist(cql_transform_t ct,
341 void (*pr)(const char *buf, void *client_data),
345 const char *cp0 = cn->u.st.term;
347 const char *last_term = 0;
353 cp1 = strchr(cp0, ' ');
356 (*pr)("@", client_data);
357 (*pr)(op, client_data);
358 (*pr)(" ", client_data);
359 emit_term(ct, last_term, last_length, pr, client_data);
363 last_length = cp1 - cp0;
365 last_length = strlen(cp0);
369 emit_term(ct, last_term, last_length, pr, client_data);
372 void cql_transform_r(cql_transform_t ct,
374 void (*pr)(const char *buf, void *client_data),
384 ns = cn->u.st.index_uri;
387 if (!strcmp(ns, cql_uri())
388 && cn->u.st.index && !strcmp(cn->u.st.index, "resultSet"))
390 (*pr)("@set \"", client_data);
391 (*pr)(cn->u.st.term, client_data);
392 (*pr)("\" ", client_data);
395 cql_pr_attr_uri(ct, "index", ns,
396 cn->u.st.index, "serverChoice",
397 pr, client_data, 16);
407 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "="))
408 cql_pr_attr(ct, "relation", "eq", "scr",
409 pr, client_data, 19);
410 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "<="))
411 cql_pr_attr(ct, "relation", "le", "scr",
412 pr, client_data, 19);
413 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, ">="))
414 cql_pr_attr(ct, "relation", "ge", "scr",
415 pr, client_data, 19);
417 cql_pr_attr(ct, "relation", cn->u.st.relation, "eq",
418 pr, client_data, 19);
419 if (cn->u.st.modifiers)
421 struct cql_node *mod = cn->u.st.modifiers;
422 for (; mod; mod = mod->u.st.modifiers)
424 cql_pr_attr(ct, "relationModifier", mod->u.st.term, 0,
425 pr, client_data, 20);
428 cql_pr_attr(ct, "structure", cn->u.st.relation, 0,
429 pr, client_data, 24);
430 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "all"))
432 emit_wordlist(ct, cn, pr, client_data, "and");
434 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "any"))
436 emit_wordlist(ct, cn, pr, client_data, "or");
440 emit_term(ct, cn->u.st.term, strlen(cn->u.st.term),
445 (*pr)("@", client_data);
446 (*pr)(cn->u.boolean.value, client_data);
447 (*pr)(" ", client_data);
449 cql_transform_r(ct, cn->u.boolean.left, pr, client_data);
450 cql_transform_r(ct, cn->u.boolean.right, pr, client_data);
454 int cql_transform(cql_transform_t ct,
456 void (*pr)(const char *buf, void *client_data),
459 struct cql_prop_entry *e;
466 for (e = ct->entry; e ; e = e->next)
468 if (!memcmp(e->pattern, "set.", 4))
469 cql_apply_prefix(cn, e->pattern+4, e->value);
470 else if (!strcmp(e->pattern, "set"))
471 cql_apply_prefix(cn, 0, e->value);
473 cql_transform_r (ct, cn, pr, client_data);
478 int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f)
480 return cql_transform(ct, cn, cql_fputs, f);
483 int cql_transform_buf(cql_transform_t ct, struct cql_node *cn,
486 struct cql_buf_write_info info;
492 r = cql_transform(ct, cn, cql_buf_write_handler, &info);
494 info.buf[info.off] = '\0';
498 int cql_transform_error(cql_transform_t ct, const char **addinfo)
500 *addinfo = ct->addinfo;