1 /* $Id: cqltransform.c,v 1.7 2004-03-10 16:34:29 adam Exp $
2 Copyright (C) 2002-2003
5 This file is part of the YAZ toolkit.
14 struct cql_prop_entry {
17 struct cql_prop_entry *next;
20 struct cql_transform_t_ {
21 struct cql_prop_entry *entry;
26 cql_transform_t cql_transform_open_FILE(FILE *f)
29 cql_transform_t ct = (cql_transform_t) malloc (sizeof(*ct));
30 struct cql_prop_entry **pp = &ct->entry;
34 while (fgets(line, sizeof(line)-1, f))
36 const char *cp_value_start;
37 const char *cp_value_end;
38 const char *cp_pattern_end;
39 const char *cp = line;
40 while (*cp && !strchr(" \t=\r\n#", *cp))
45 while (*cp && strchr(" \t\r\n", *cp))
50 while (*cp && strchr(" \t\r\n", *cp))
53 if (!(cp_value_end = strchr(cp, '#')))
54 cp_value_end = strlen(line) + line;
56 if (cp_value_end != cp_value_start &&
57 strchr(" \t\r\n", cp_value_end[-1]))
59 *pp = (struct cql_prop_entry *) malloc (sizeof(**pp));
60 (*pp)->pattern = (char *) malloc (cp_pattern_end - line + 1);
61 memcpy ((*pp)->pattern, line, cp_pattern_end - line);
62 (*pp)->pattern[cp_pattern_end-line] = 0;
64 (*pp)->value = (char *) malloc (cp_value_end - cp_value_start + 1);
65 if (cp_value_start != cp_value_end)
66 memcpy ((*pp)->value, cp_value_start, cp_value_end-cp_value_start);
67 (*pp)->value[cp_value_end - cp_value_start] = 0;
74 void cql_transform_close(cql_transform_t ct)
76 struct cql_prop_entry *pe;
82 struct cql_prop_entry *pe_next = pe->next;
93 cql_transform_t cql_transform_open_fname(const char *fname)
96 FILE *f = fopen(fname, "r");
99 ct = cql_transform_open_FILE(f);
104 static const char *cql_lookup_property(cql_transform_t ct,
105 const char *pat1, const char *pat2)
108 struct cql_prop_entry *e;
111 sprintf (pattern, "%.39s%.39s", pat1, pat2);
113 sprintf (pattern, "%.39s", pat1);
114 for (e = ct->entry; e; e = e->next)
116 if (!strcmp(e->pattern, pattern))
122 static const char *cql_lookup_value(cql_transform_t ct,
126 struct cql_prop_entry *e;
127 int len = strlen(prefix);
129 for (e = ct->entry; e; e = e->next)
131 if (!memcmp(e->pattern, prefix, len) && !strcmp(e->value, value))
132 return e->pattern + len;
138 int cql_pr_attr(cql_transform_t ct, const char *category,
140 const char *default_val,
141 void (*pr)(const char *buf, void *client_data),
146 res = cql_lookup_property(ct, category, val ? val : default_val);
148 res = cql_lookup_property(ct, category, "*");
153 const char *cp0 = res, *cp1;
154 while ((cp1 = strchr(cp0, '=')))
156 while (*cp1 && *cp1 != ' ')
158 if (cp1 - cp0 >= sizeof(buf))
160 memcpy (buf, cp0, cp1 - cp0);
162 (*pr)("@attr ", client_data);
163 (*pr)(buf, client_data);
164 (*pr)(" ", client_data);
172 if (errcode && !ct->error)
176 ct->addinfo = strdup(val);
184 /* Returns location of first wildcard character in the `length'
185 * characters starting at `term', or a null pointer of there are
186 * none -- like memchr().
188 static char *wcchar(const char *term, int length)
194 for (whichp = "*?"; *whichp != '\0'; whichp++) {
195 current = memchr(term, *whichp, length);
196 if (current != 0 && (best == 0 || current < best))
204 void emit_term(cql_transform_t ct,
205 const char *term, int length,
206 void (*pr)(const char *buf, void *client_data),
212 if (length > 1 && term[0] == '^' && term[length-1] == '^')
214 cql_pr_attr(ct, "position.", "firstAndLast", 0,
215 pr, client_data, 32);
219 else if (term[0] == '^')
221 cql_pr_attr(ct, "position.", "first", 0,
222 pr, client_data, 32);
226 else if (term[length-1] == '^')
228 cql_pr_attr(ct, "position.", "last", 0,
229 pr, client_data, 32);
234 cql_pr_attr(ct, "position.", "any", 0,
235 pr, client_data, 32);
241 /* Check for well-known globbing patterns that represent
242 * simple truncation attributes as expected by, for example,
243 * Bath-compliant server. If we find such a pattern but
244 * there's no mapping for it, that's fine: we just use a
245 * general pattern-matching attribute.
247 if (length > 1 && term[0] == '*' && term[length-1] == '*' &&
248 wcchar(term+1, length-2) == 0 &&
249 cql_pr_attr(ct, "truncation.", "both", 0,
250 pr, client_data, 0)) {
254 else if (term[0] == '*' &&
255 wcchar(term+1, length-1) == 0 &&
256 cql_pr_attr(ct, "truncation.", "left", 0,
257 pr, client_data, 0)) {
261 else if (term[length-1] == '*' &&
262 wcchar(term, length-1) == 0 &&
263 cql_pr_attr(ct, "truncation.", "right", 0,
264 pr, client_data, 0)) {
267 else if (wcchar(term, length))
269 /* We have one or more wildcard characters, but not in a
270 * way that can be dealt with using only the standard
271 * left-, right- and both-truncation attributes. We need
272 * to translate the pattern into a Z39.58-type pattern,
273 * which has been supported in BIB-1 since 1996. If
274 * there's no configuration element for "truncation.z3958"
275 * we indicate this as error 28 "Masking character not
280 cql_pr_attr(ct, "truncation.", "z3958", 0,
281 pr, client_data, 28);
282 mem = malloc(length+1);
283 for (i = 0; i < length; i++) {
284 if (term[i] == '*') mem[i] = '?';
285 else if (term[i] == '?') mem[i] = '#';
286 else mem[i] = term[i];
292 /* No masking characters. If there's no "truncation.none"
293 * configuration element, that's an error which we
294 * indicate (rather tangentially) as 30 "Too many masking
295 * characters in term". 28 would be equally meaningful
296 * (or meaningless) but using a different value allows us
297 * to differentiate between this case and the previous
300 cql_pr_attr(ct, "truncation.", "none", 0,
301 pr, client_data, 30);
305 (*pr)("\"", client_data);
306 for (i = 0; i<length; i++)
311 (*pr)(buf, client_data);
313 (*pr)("\" ", client_data);
316 void emit_wordlist(cql_transform_t ct,
318 void (*pr)(const char *buf, void *client_data),
322 const char *cp0 = cn->u.st.term;
324 const char *last_term = 0;
330 cp1 = strchr(cp0, ' ');
333 (*pr)("@", client_data);
334 (*pr)(op, client_data);
335 (*pr)(" ", client_data);
336 emit_term(ct, last_term, last_length, pr, client_data);
340 last_length = cp1 - cp0;
342 last_length = strlen(cp0);
346 emit_term(ct, last_term, last_length, pr, client_data);
350 static const char *cql_get_ns(cql_transform_t ct,
352 struct cql_node **prefix_ar, int prefix_level,
353 const char **n_prefix,
354 const char **n_suffix)
359 const char *cp = cn->u.st.index;
360 const char *cp_dot = strchr(cp, '.');
362 /* strz current prefix (empty if not given) */
363 if (cp_dot && cp_dot-cp < sizeof(prefix))
365 memcpy (prefix, cp, cp_dot - cp);
366 prefix[cp_dot - cp] = 0;
371 /* 2. lookup in prefix_ar. and return NS */
372 for (i = prefix_level; !ns && --i >= 0; )
374 struct cql_node *cn_prefix = prefix_ar[i];
375 for (; cn_prefix; cn_prefix = cn_prefix->u.st.modifiers)
377 if (*prefix && cn_prefix->u.st.index &&
378 !strcmp(prefix, cn_prefix->u.st.index))
380 ns = cn_prefix->u.st.term;
383 else if (!*prefix && !cn_prefix->u.st.index)
385 ns = cn_prefix->u.st.term;
395 ct->addinfo = strdup(prefix);
399 /* 3. lookup in set.NS for new prefix */
400 *n_prefix = cql_lookup_value(ct, "set.", ns);
406 ct->addinfo = strdup(ns);
410 /* 4. lookup index.prefix. */
413 cp_dot = strchr(cp, '.');
415 *n_suffix = cp_dot ? cp_dot+1 : cp;
419 void cql_transform_r(cql_transform_t ct,
421 void (*pr)(const char *buf, void *client_data),
423 struct cql_node **prefix_ar, int prefix_level)
425 const char *ns, *n_prefix, *n_suffix;
432 if (cn->u.st.prefixes && prefix_level < 20)
433 prefix_ar[prefix_level++] = cn->u.st.prefixes;
434 ns = cql_get_ns(ct, cn, prefix_ar, prefix_level, &n_prefix, &n_suffix);
438 sprintf (n_full, "%.20s.%.40s", n_prefix, n_suffix);
440 if ((!strcmp(ns, "http://www.loc.gov/zing/cql/context-sets/cql/v1.1/") ||
441 !strcmp(ns, "http://www.loc.gov/zing/cql/srw-indexes/v1.0/"))
442 && !strcmp(n_suffix, "resultSet"))
444 (*pr)("@set \"", client_data);
445 (*pr)(cn->u.st.term, client_data);
446 (*pr)("\" ", client_data);
449 /* ### It would be nice if this could fall back to whichever
450 of cql.serverChoice and srw.serverChoice is defined */
451 if (!cql_pr_attr(ct, "index.", n_full, "cql.serverChoice",
452 pr, client_data, 16)) {
453 /* No index.foo; reset error and fall back to qualifier.foo */
454 if (ct->error == 16) ct->error = 0;
455 cql_pr_attr(ct, "qualifier.", n_full, "cql.serverChoice",
456 pr, client_data, 16);
460 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "="))
461 cql_pr_attr(ct, "relation.", "eq", "scr",
462 pr, client_data, 19);
463 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "<="))
464 cql_pr_attr(ct, "relation.", "le", "scr",
465 pr, client_data, 19);
466 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, ">="))
467 cql_pr_attr(ct, "relation.", "ge", "scr",
468 pr, client_data, 19);
470 cql_pr_attr(ct, "relation.", cn->u.st.relation, "eq",
471 pr, client_data, 19);
472 if (cn->u.st.modifiers)
474 struct cql_node *mod = cn->u.st.modifiers;
475 for (; mod; mod = mod->u.st.modifiers)
477 cql_pr_attr(ct, "relationModifier.", mod->u.st.term, 0,
478 pr, client_data, 20);
481 cql_pr_attr(ct, "structure.", cn->u.st.relation, 0,
482 pr, client_data, 24);
483 if (cn->u.st.relation && !strcmp(cn->u.st.relation, "all"))
485 emit_wordlist(ct, cn, pr, client_data, "and");
487 else if (cn->u.st.relation && !strcmp(cn->u.st.relation, "any"))
489 emit_wordlist(ct, cn, pr, client_data, "or");
493 emit_term(ct, cn->u.st.term, strlen(cn->u.st.term),
498 if (cn->u.boolean.prefixes && prefix_level < 20)
499 prefix_ar[prefix_level++] = cn->u.boolean.prefixes;
500 (*pr)("@", client_data);
501 (*pr)(cn->u.boolean.value, client_data);
502 (*pr)(" ", client_data);
504 cql_transform_r(ct, cn->u.boolean.left, pr, client_data,
505 prefix_ar, prefix_level);
506 cql_transform_r(ct, cn->u.boolean.right, pr, client_data,
507 prefix_ar, prefix_level);
511 int cql_transform(cql_transform_t ct,
513 void (*pr)(const char *buf, void *client_data),
516 struct cql_node *prefix_ar[20], **pp;
517 struct cql_prop_entry *e;
526 for (e = ct->entry; e ; e = e->next)
528 if (!memcmp(e->pattern, "set.", 4))
530 *pp = cql_node_mk_sc(e->pattern+4, "=", e->value);
531 pp = &(*pp)->u.st.modifiers;
533 else if (!strcmp(e->pattern, "set"))
535 *pp = cql_node_mk_sc(e->value, 0, 0);
536 pp = &(*pp)->u.st.modifiers;
539 cql_transform_r (ct, cn, pr, client_data, prefix_ar, 1);
540 cql_node_destroy(prefix_ar[0]);
545 int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f)
547 return cql_transform(ct, cn, cql_fputs, f);
550 int cql_transform_buf(cql_transform_t ct, struct cql_node *cn,
553 struct cql_buf_write_info info;
559 r = cql_transform(ct, cn, cql_buf_write_handler, &info);
561 info.buf[info.off] = '\0';
565 int cql_transform_error(cql_transform_t ct, const char **addinfo)
567 *addinfo = ct->addinfo;