X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Fcqltransform.c;h=f17af20bf8777e80452242656f322d5442b57ae0;hp=199425d8b08c2a04811d8c9ebb0f21328d956e18;hb=b70c72a5a3914502cbdd17588e74e6c74bd282b6;hpb=7cf46a0f93d9118904335544412f5b15ee0345d3 diff --git a/src/cqltransform.c b/src/cqltransform.c index 199425d..f17af20 100644 --- a/src/cqltransform.c +++ b/src/cqltransform.c @@ -1,8 +1,7 @@ /* This file is part of the YAZ toolkit. - * Copyright (C) 1995-2008 Index Data + * Copyright (C) 1995-2012 Index Data * See the file LICENSE for details. */ - /** * \file cqltransform.c * \brief Implements CQL transform (CQL to RPN conversion). @@ -17,19 +16,27 @@ * index * relationModifier */ +#if HAVE_CONFIG_H +#include +#endif #include #include #include -#include +#include #include #include #include #include +#include +#include +#include +#include struct cql_prop_entry { char *pattern; char *value; + Z_AttributeList attr_list; struct cql_prop_entry *next; }; @@ -39,19 +46,177 @@ struct cql_transform_t_ { int error; char *addinfo; WRBUF w; + NMEM nmem; }; -cql_transform_t cql_transform_open_FILE(FILE *f) + +cql_transform_t cql_transform_create(void) { - char line[1024]; cql_transform_t ct = (cql_transform_t) xmalloc(sizeof(*ct)); - struct cql_prop_entry **pp = &ct->entry; ct->tok_cfg = yaz_tok_cfg_create(); ct->w = wrbuf_alloc(); - - yaz_tok_cfg_single_tokens(ct->tok_cfg, "="); ct->error = 0; ct->addinfo = 0; + ct->entry = 0; + ct->nmem = nmem_create(); + return ct; +} + +static int cql_transform_parse_tok_line(cql_transform_t ct, + const char *pattern, + yaz_tok_parse_t tp) +{ + int ae_num = 0; + Z_AttributeElement *ae[20]; + int ret = 0; /* 0=OK, != 0 FAIL */ + int t; + t = yaz_tok_move(tp); + + while (t == YAZ_TOK_STRING && ae_num < 20) + { + WRBUF type_str = wrbuf_alloc(); + WRBUF set_str = 0; + Z_AttributeElement *elem = 0; + const char *value_str = 0; + /* attset type=value OR type=value */ + + elem = (Z_AttributeElement *) nmem_malloc(ct->nmem, sizeof(*elem)); + elem->attributeSet = 0; + ae[ae_num] = elem; + wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); + wrbuf_puts(type_str, yaz_tok_parse_string(tp)); + t = yaz_tok_move(tp); + if (t == YAZ_TOK_EOF) + { + wrbuf_destroy(type_str); + if (set_str) + wrbuf_destroy(set_str); + break; + } + if (t == YAZ_TOK_STRING) + { + wrbuf_puts(ct->w, " "); + wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); + set_str = type_str; + + elem->attributeSet = + yaz_string_to_oid_nmem(yaz_oid_std(), CLASS_ATTSET, + wrbuf_cstr(set_str), ct->nmem); + + type_str = wrbuf_alloc(); + wrbuf_puts(type_str, yaz_tok_parse_string(tp)); + t = yaz_tok_move(tp); + } + elem->attributeType = nmem_intdup(ct->nmem, 0); + if (sscanf(wrbuf_cstr(type_str), ODR_INT_PRINTF, elem->attributeType) + != 1) + { + wrbuf_destroy(type_str); + if (set_str) + wrbuf_destroy(set_str); + yaz_log(YLOG_WARN, "Expected numeric attribute type"); + ret = -1; + break; + } + + wrbuf_destroy(type_str); + if (set_str) + wrbuf_destroy(set_str); + + if (t != '=') + { + yaz_log(YLOG_WARN, "Expected = after after attribute type"); + ret = -1; + break; + } + t = yaz_tok_move(tp); + if (t != YAZ_TOK_STRING) /* value */ + { + yaz_log(YLOG_WARN, "Missing attribute value"); + ret = -1; + break; + } + value_str = yaz_tok_parse_string(tp); + if (yaz_isdigit(*value_str)) + { + elem->which = Z_AttributeValue_numeric; + elem->value.numeric = + nmem_intdup(ct->nmem, atoi(value_str)); + } + else + { + Z_ComplexAttribute *ca = (Z_ComplexAttribute *) + nmem_malloc(ct->nmem, sizeof(*ca)); + elem->which = Z_AttributeValue_complex; + elem->value.complex = ca; + ca->num_list = 1; + ca->list = (Z_StringOrNumeric **) + nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric *)); + ca->list[0] = (Z_StringOrNumeric *) + nmem_malloc(ct->nmem, sizeof(Z_StringOrNumeric)); + ca->list[0]->which = Z_StringOrNumeric_string; + ca->list[0]->u.string = nmem_strdup(ct->nmem, value_str); + ca->num_semanticAction = 0; + ca->semanticAction = 0; + } + wrbuf_puts(ct->w, "="); + wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); + t = yaz_tok_move(tp); + wrbuf_puts(ct->w, " "); + ae_num++; + } + if (ret == 0) /* OK? */ + { + struct cql_prop_entry **pp = &ct->entry; + while (*pp) + pp = &(*pp)->next; + *pp = (struct cql_prop_entry *) xmalloc(sizeof(**pp)); + (*pp)->pattern = xstrdup(pattern); + (*pp)->value = xstrdup(wrbuf_cstr(ct->w)); + + (*pp)->attr_list.num_attributes = ae_num; + if (ae_num == 0) + (*pp)->attr_list.attributes = 0; + else + { + (*pp)->attr_list.attributes = (Z_AttributeElement **) + nmem_malloc(ct->nmem, + ae_num * sizeof(Z_AttributeElement *)); + memcpy((*pp)->attr_list.attributes, ae, + ae_num * sizeof(Z_AttributeElement *)); + } + (*pp)->next = 0; + + if (0) + { + ODR pr = odr_createmem(ODR_PRINT); + Z_AttributeList *alp = &(*pp)->attr_list; + odr_setprint(pr, yaz_log_file()); + z_AttributeList(pr, &alp, 0, 0); + odr_setprint(pr, 0); + odr_destroy(pr); + } + } + return ret; +} + +int cql_transform_define_pattern(cql_transform_t ct, const char *pattern, + const char *value) +{ + int r; + yaz_tok_parse_t tp = yaz_tok_parse_buf(ct->tok_cfg, value); + yaz_tok_cfg_single_tokens(ct->tok_cfg, "="); + r = cql_transform_parse_tok_line(ct, pattern, tp); + yaz_tok_parse_destroy(tp); + return r; +} + +cql_transform_t cql_transform_open_FILE(FILE *f) +{ + cql_transform_t ct = cql_transform_create(); + char line[1024]; + + yaz_tok_cfg_single_tokens(ct->tok_cfg, "="); while (fgets(line, sizeof(line)-1, f)) { @@ -69,43 +234,13 @@ cql_transform_t cql_transform_open_FILE(FILE *f) cql_transform_close(ct); return 0; } - t = yaz_tok_move(tp); - - while (t == YAZ_TOK_STRING) + if (cql_transform_parse_tok_line(ct, pattern, tp)) { - /* attset type=value OR type=value */ - wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); - t = yaz_tok_move(tp); - if (t == YAZ_TOK_EOF) - break; - if (t == YAZ_TOK_STRING) - { - wrbuf_puts(ct->w, " "); - wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); - t = yaz_tok_move(tp); - } - if (t != '=') - { - yaz_tok_parse_destroy(tp); - cql_transform_close(ct); - return 0; - } - t = yaz_tok_move(tp); - if (t != YAZ_TOK_STRING) /* value */ - { - yaz_tok_parse_destroy(tp); - cql_transform_close(ct); - return 0; - } - wrbuf_puts(ct->w, "="); - wrbuf_puts(ct->w, yaz_tok_parse_string(tp)); - t = yaz_tok_move(tp); - wrbuf_puts(ct->w, " "); + yaz_tok_parse_destroy(tp); + cql_transform_close(ct); + return 0; } - *pp = (struct cql_prop_entry *) xmalloc(sizeof(**pp)); - (*pp)->pattern = pattern; - (*pp)->value = xstrdup(wrbuf_cstr(ct->w)); - pp = &(*pp)->next; + xfree(pattern); } else if (t != YAZ_TOK_EOF) { @@ -115,7 +250,6 @@ cql_transform_t cql_transform_open_FILE(FILE *f) } yaz_tok_parse_destroy(tp); } - *pp = 0; return ct; } @@ -136,6 +270,7 @@ void cql_transform_close(cql_transform_t ct) xfree(ct->addinfo); yaz_tok_cfg_destroy(ct->tok_cfg); wrbuf_destroy(ct->w); + nmem_destroy(ct->nmem); xfree(ct); } @@ -150,6 +285,78 @@ cql_transform_t cql_transform_open_fname(const char *fname) return ct; } +#if 0 +struct Z_AttributeElement { + Z_AttributeSetId *attributeSet; /* OPT */ + int *attributeType; + int which; + union { + int *numeric; + Z_ComplexAttribute *complex; +#define Z_AttributeValue_numeric 1 +#define Z_AttributeValue_complex 2 + } value; +}; +#endif + +static int compare_attr(Z_AttributeElement *a, Z_AttributeElement *b) +{ + ODR odr_a = odr_createmem(ODR_ENCODE); + ODR odr_b = odr_createmem(ODR_ENCODE); + int len_a, len_b; + char *buf_a, *buf_b; + int ret; + + z_AttributeElement(odr_a, &a, 0, 0); + z_AttributeElement(odr_b, &b, 0, 0); + + buf_a = odr_getbuf(odr_a, &len_a, 0); + buf_b = odr_getbuf(odr_b, &len_b, 0); + + ret = yaz_memcmp(buf_a, buf_b, len_a, len_b); + + odr_destroy(odr_a); + odr_destroy(odr_b); + return ret; +} + +const char *cql_lookup_reverse(cql_transform_t ct, + const char *category, + Z_AttributeList *attributes) +{ + struct cql_prop_entry *e; + size_t clen = strlen(category); + for (e = ct->entry; e; e = e->next) + { + if (!strncmp(e->pattern, category, clen)) + { + /* category matches.. See if attributes in pattern value + are all listed in actual attributes */ + int i; + for (i = 0; i < e->attr_list.num_attributes; i++) + { + /* entry attribute */ + Z_AttributeElement *e_ae = e->attr_list.attributes[i]; + int j; + for (j = 0; j < attributes->num_attributes; j++) + { + /* actual attribute */ + Z_AttributeElement *a_ae = attributes->attributes[j]; + int r = compare_attr(e_ae, a_ae); + if (r == 0) + break; + } + if (j == attributes->num_attributes) + break; /* i was not found at all.. try next pattern */ + + } + if (i == e->attr_list.num_attributes) + return e->pattern + clen; + } + } + return 0; +} + static const char *cql_lookup_property(cql_transform_t ct, const char *pat1, const char *pat2, const char *pat3) @@ -167,7 +374,7 @@ static const char *cql_lookup_property(cql_transform_t ct, sprintf(pattern, "%.39s", pat1); else return 0; - + for (e = ct->entry; e; e = e->next) { if (!cql_strcmp(e->pattern, pattern)) @@ -185,11 +392,11 @@ int cql_pr_attr_uri(cql_transform_t ct, const char *category, const char *res = 0; const char *eval = val ? val : default_val; const char *prefix = 0; - + if (uri) { struct cql_prop_entry *e; - + for (e = ct->entry; e; e = e->next) if (!memcmp(e->pattern, "set.", 4) && e->value && !strcmp(e->value, uri)) @@ -229,7 +436,7 @@ int cql_pr_attr_uri(cql_transform_t ct, const char *category, int i; while (*cp1 && *cp1 != ' ') cp1++; - if (cp1 - cp0 >= sizeof(buf)) + if (cp1 - cp0 >= (ptrdiff_t) sizeof(buf)) break; memcpy(buf, cp0, cp1 - cp0); buf[cp1-cp0] = 0; @@ -293,8 +500,7 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods, void *client_data) { int exclusion = 0; - int distance; /* to be filled in later depending on unit */ - int distance_defined = 0; + int distance = -1; int ordered = 0; int proxrel = 2; /* less than or equal */ int unit = 2; /* word */ @@ -307,26 +513,25 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods, if (!strcmp(name, "distance")) { distance = strtol(term, (char**) 0, 0); - distance_defined = 1; if (!strcmp(relation, "=")) proxrel = 3; else if (!strcmp(relation, ">")) proxrel = 5; else if (!strcmp(relation, "<")) proxrel = 1; - else if (!strcmp(relation, ">=")) + else if (!strcmp(relation, ">=")) proxrel = 4; else if (!strcmp(relation, "<=")) proxrel = 2; else if (!strcmp(relation, "<>")) proxrel = 6; - else + else { ct->error = YAZ_SRW_UNSUPP_PROX_RELATION; ct->addinfo = xstrdup(relation); return 0; } - } + } else if (!strcmp(name, "ordered")) ordered = 1; else if (!strcmp(name, "unordered")) @@ -341,14 +546,14 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods, unit = 4; else if (!strcmp(term, "element")) unit = 8; - else + else { ct->error = YAZ_SRW_UNSUPP_PROX_UNIT; ct->addinfo = xstrdup(term); return 0; } - } - else + } + else { ct->error = YAZ_SRW_UNSUPP_BOOLEAN_MODIFIER; ct->addinfo = xstrdup(name); @@ -357,7 +562,7 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods, mods = mods->u.st.modifiers; } - if (!distance_defined) + if (distance == -1) distance = (unit == 2) ? 1 : 0; cql_pr_int(exclusion, pr, client_data); @@ -370,25 +575,6 @@ static int cql_pr_prox(cql_transform_t ct, struct cql_node *mods, return 1; } -/* Returns location of first wildcard character in the `length' - * characters starting at `term', or a null pointer of there are - * none -- like memchr(). - */ -static const char *wcchar(int start, const char *term, int length) -{ - while (length > 0) - { - if (start || term[-1] != '\\') - if (strchr("*?", *term)) - return term; - term++; - length--; - start = 0; - } - return 0; -} - - /* ### checks for CQL relation-name rather than Type-1 attribute */ static int has_modifier(struct cql_node *cn, const char *name) { struct cql_node *mod; @@ -401,36 +587,74 @@ static int has_modifier(struct cql_node *cn, const char *name) { } -void emit_term(cql_transform_t ct, - struct cql_node *cn, - const char *term, int length, - void (*pr)(const char *buf, void *client_data), - void *client_data) +static void emit_term(cql_transform_t ct, + struct cql_node *cn, + const char *term, int length, + void (*pr)(const char *buf, void *client_data), + void *client_data) { int i; const char *ns = cn->u.st.index_uri; - int process_term = !has_modifier(cn, "regexp"); - char *z3958_mem = 0; + int z3958_mode = 0; + int process_term = 1; + if (has_modifier(cn, "regexp")) + process_term = 0; + else if (cql_lookup_property(ct, "truncation", 0, "cql")) + { + process_term = 0; + cql_pr_attr(ct, "truncation", "cql", 0, + pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP); + } assert(cn->which == CQL_NODE_ST); - if (process_term && length > 0) + if (process_term) { - if (length > 1 && term[0] == '^' && term[length-1] == '^') + unsigned anchor = 0; + unsigned trunc = 0; + for (i = 0; i < length; i++) + { + if (term[i] == '\\' && i < length - 1) + i++; + else + { + switch (term[i]) + { + case '^': + if (i == 0) + anchor |= 1; + else if (i == length - 1) + anchor |= 2; + break; + case '*': + if (i == 0) + trunc |= 1; + else if (i == length - 1) + trunc |= 2; + else + z3958_mode = 1; + break; + case '?': + z3958_mode = 1; + break; + } + } + } + if (anchor == 3) { cql_pr_attr(ct, "position", "firstAndLast", 0, pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION); term++; length -= 2; } - else if (term[0] == '^') + else if (anchor == 1) { cql_pr_attr(ct, "position", "first", 0, pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION); term++; length--; } - else if (term[length-1] == '^') + else if (anchor == 2) { cql_pr_attr(ct, "position", "last", 0, pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION); @@ -441,75 +665,34 @@ void emit_term(cql_transform_t ct, cql_pr_attr(ct, "position", "any", 0, pr, client_data, YAZ_SRW_ANCHORING_CHAR_IN_UNSUPP_POSITION); } - } - - if (process_term && length > 0) - { - const char *first_wc = wcchar(1, term, length); - const char *second_wc = first_wc ? - wcchar(0, first_wc+1, length-(first_wc-term)-1) : 0; - - /* Check for well-known globbing patterns that represent - * simple truncation attributes as expected by, for example, - * Bath-compliant server. If we find such a pattern but - * there's no mapping for it, that's fine: we just use a - * general pattern-matching attribute. - */ - if (first_wc == term && second_wc == term + length-1 - && *first_wc == '*' && *second_wc == '*' - && cql_pr_attr(ct, "truncation", "both", 0, pr, client_data, 0)) + if (z3958_mode == 0) { - term++; - length -= 2; - } - else if (first_wc == term && second_wc == 0 && *first_wc == '*' - && cql_pr_attr(ct, "truncation", "left", 0, - pr, client_data, 0)) - { - term++; - length--; - } - else if (first_wc == term + length-1 && second_wc == 0 - && *first_wc == '*' - && cql_pr_attr(ct, "truncation", "right", 0, - pr, client_data, 0)) - { - length--; - } - else if (first_wc) - { - /* We have one or more wildcard characters, but not in a - * way that can be dealt with using only the standard - * left-, right- and both-truncation attributes. We need - * to translate the pattern into a Z39.58-type pattern, - * which has been supported in BIB-1 since 1996. If - * there's no configuration element for "truncation.z3958" - * we indicate this as error 28 "Masking character not - * supported". - */ - int i; - cql_pr_attr(ct, "truncation", "z3958", 0, - pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP); - z3958_mem = (char *) xmalloc(length+1); - for (i = 0; i < length; i++) + if (trunc == 3 && cql_pr_attr(ct, "truncation", + "both", 0, pr, client_data, 0)) { - if (i > 0 && term[i-1] == '\\') - z3958_mem[i] = term[i]; - else if (term[i] == '*') - z3958_mem[i] = '?'; - else if (term[i] == '?') - z3958_mem[i] = '#'; - else - z3958_mem[i] = term[i]; + term++; + length -= 2; } - z3958_mem[length] = '\0'; - term = z3958_mem; - } - else { - /* No masking characters. Use "truncation.none" if given. */ - cql_pr_attr(ct, "truncation", "none", 0, - pr, client_data, 0); + else if (trunc == 1 && cql_pr_attr(ct, "truncation", + "left", 0, pr, client_data, 0)) + { + term++; + length--; + } + else if (trunc == 2 && cql_pr_attr(ct, "truncation", "right", 0, + pr, client_data, 0)) + { + length--; + } + else if (trunc) + z3958_mode = 1; + else + cql_pr_attr(ct, "truncation", "none", 0, + pr, client_data, 0); } + if (z3958_mode) + cql_pr_attr(ct, "truncation", "z3958", 0, + pr, client_data, YAZ_SRW_MASKING_CHAR_UNSUPP); } if (ns) { cql_pr_attr_uri(ct, "index", ns, @@ -525,28 +708,61 @@ void emit_term(cql_transform_t ct, pr, client_data, YAZ_SRW_UNSUPP_RELATION_MODIFIER); } } - (*pr)("\"", client_data); - for (i = 0; iu.st.extra_terms; if (ne) @@ -564,17 +780,17 @@ void emit_terms(cql_transform_t ct, (*pr)("@", client_data); (*pr)(op, client_data); (*pr)(" ", client_data); - } + } emit_term(ct, cn, ne->u.st.term, strlen(ne->u.st.term), pr, client_data); } } -void emit_wordlist(cql_transform_t ct, - struct cql_node *cn, - void (*pr)(const char *buf, void *client_data), - void *client_data, - const char *op) +static void emit_wordlist(cql_transform_t ct, + struct cql_node *cn, + void (*pr)(const char *buf, void *client_data), + void *client_data, + const char *op) { const char *cp0 = cn->u.st.term; const char *cp1; @@ -653,11 +869,11 @@ void cql_transform_r(cql_transform_t ct, (*pr)(cn->u.boolean.value, client_data); (*pr)(" ", client_data); mods = cn->u.boolean.modifiers; - if (!strcmp(cn->u.boolean.value, "prox")) + if (!strcmp(cn->u.boolean.value, "prox")) { if (!cql_pr_prox(ct, mods, pr, client_data)) return; - } + } else if (mods) { /* Boolean modifiers other than on proximity not supported */ @@ -669,7 +885,9 @@ void cql_transform_r(cql_transform_t ct, cql_transform_r(ct, cn->u.boolean.left, pr, client_data); cql_transform_r(ct, cn->u.boolean.right, pr, client_data); break; - + case CQL_NODE_SORT: + cql_transform_r(ct, cn->u.sort.search, pr, client_data); + break; default: fprintf(stderr, "Fatal: impossible CQL node-type %d\n", cn->which); abort(); @@ -705,7 +923,8 @@ int cql_transform_FILE(cql_transform_t ct, struct cql_node *cn, FILE *f) return cql_transform(ct, cn, cql_fputs, f); } -int cql_transform_buf(cql_transform_t ct, struct cql_node *cn, char *out, int max) +int cql_transform_buf(cql_transform_t ct, struct cql_node *cn, + char *out, int max) { struct cql_buf_write_info info; int r; @@ -745,6 +964,7 @@ void cql_transform_set_error(cql_transform_t ct, int error, const char *addinfo) /* * Local variables: * c-basic-offset: 4 + * c-file-style: "Stroustrup" * indent-tabs-mode: nil * End: * vim: shiftwidth=4 tabstop=8 expandtab