X-Git-Url: http://git.indexdata.com/?p=yaz-moved-to-github.git;a=blobdiff_plain;f=src%2Fzoom-c.c;h=cd13a010f568eb60c6c1fe2eb9b2ba8d34316707;hp=3461431976090687bb93fbc489dbca2dce1a2da9;hb=6202be57112ed26410889081bb0ab87a779ae0bf;hpb=cf3ff0df41755297543b14f007842e1dc216ec25 diff --git a/src/zoom-c.c b/src/zoom-c.c index 3461431..cd13a01 100644 --- a/src/zoom-c.c +++ b/src/zoom-c.c @@ -2,7 +2,7 @@ * Copyright (C) 1995-2005, Index Data ApS * See the file LICENSE for details. * - * $Id: zoom-c.c,v 1.50 2005-11-03 16:49:00 mike Exp $ + * $Id: zoom-c.c,v 1.62 2005-12-21 16:41:36 mike Exp $ */ /** * \file zoom-c.c @@ -11,6 +11,7 @@ #include #include +#include #include "zoom-p.h" #include @@ -23,6 +24,7 @@ #include #include #include +#include #if HAVE_SYS_TYPES_H #include @@ -50,6 +52,8 @@ typedef enum { static zoom_ret ZOOM_connection_send_init (ZOOM_connection c); static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out); +static char *cql2pqf(ZOOM_connection c, const char *cql); + static void initlog() { @@ -163,7 +167,13 @@ static void set_ZOOM_error (ZOOM_connection c, int error, static void clear_error (ZOOM_connection c) { - + /* + * If an error is tied to an operation then it's ok to clear: for + * example, a diagnostic returned from a search is cleared by a + * subsequent search. However, problems such as Connection Lost + * or Init Refused are not cleared, because they are not + * recoverable: doing another search doesn't help. + */ switch (c->error) { case ZOOM_ERROR_CONNECT: @@ -388,6 +398,37 @@ ZOOM_connection_connect(ZOOM_connection c, else c->host_port = xstrdup(host); + { + /* + * If the ":" part of the host string is preceded by one + * or more comma-separated = pairs, these are taken + * to be options to be set on the connection object. Among other + * applications, this facility can be used to embed authentication + * in a host string: + * user=admin,password=secret,tcp:localhost:9999 + */ + char *remainder = c->host_port; + char *pcolon = strchr(remainder, ':'); + char *pcomma; + char *pequals; + while ((pcomma = strchr(remainder, ',')) != 0 && + (pcolon == 0 || pcomma < pcolon)) { + *pcomma = '\0'; + if ((pequals = strchr(remainder, '=')) != 0) { + *pequals = '\0'; + /*printf("# setting '%s'='%s'\n", remainder, pequals+1);*/ + ZOOM_connection_option_set(c, remainder, pequals+1); + } + remainder = pcomma+1; + } + + if (remainder != c->host_port) { + xfree(c->host_port); + c->host_port = xstrdup(remainder); + /*printf("# reset hp='%s'\n", remainder);*/ + } + } + ZOOM_options_set(c->options, "host", c->host_port); val = ZOOM_options_get (c->options, "cookie"); @@ -489,6 +530,31 @@ ZOOM_query_cql(ZOOM_query s, const char *str) return 0; } +/* + * Translate the CQL string client-side into RPN which is passed to + * the server. This is useful for server's that don't themselves + * support CQL, for which ZOOM_query_cql() is useless. `conn' is used + * only as a place to stash diagnostics if compilation fails; if this + * information is not needed, a null pointer may be used. + */ +ZOOM_API(int) +ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn) +{ + char *rpn; + int ret; + + yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn); + if (conn == 0) + conn = ZOOM_connection_create(0); + + if ((rpn = cql2pqf(conn, str)) == 0) + return -1; + + ret = ZOOM_query_prefix(s, rpn); + xfree(rpn); + return ret; +} + ZOOM_API(int) ZOOM_query_sortby(ZOOM_query s, const char *criteria) { @@ -644,17 +710,34 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q) return r; } +/* + * This is the old result-set sorting API, which is maintained only + * for the sake of binary compatibility. There is no reason ever to + * use this rather than ZOOM_resultset_sort1(). + */ ZOOM_API(void) ZOOM_resultset_sort(ZOOM_resultset r, const char *sort_type, const char *sort_spec) { + (void) ZOOM_resultset_sort1(r, sort_type, sort_spec); +} + +ZOOM_API(int) +ZOOM_resultset_sort1(ZOOM_resultset r, + const char *sort_type, const char *sort_spec) +{ ZOOM_connection c = r->connection; ZOOM_task task; + ZOOM_query newq; + + newq = ZOOM_query_create(); + if (ZOOM_query_sortby(newq, sort_spec) < 0) + return -1; yaz_log(log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s", r, r, sort_type, sort_spec); if (!c) - return; + return 0; if (c->host_port && c->proto == PROTO_HTTP) { @@ -674,8 +757,7 @@ ZOOM_resultset_sort(ZOOM_resultset r, ZOOM_resultset_cache_reset(r); task = ZOOM_connection_add_task (c, ZOOM_TASK_SORT); task->u.sort.resultset = r; - task->u.sort.q = ZOOM_query_create(); - ZOOM_query_sortby(task->u.sort.q, sort_spec); + task->u.sort.q = newq; ZOOM_resultset_addref (r); @@ -684,6 +766,8 @@ ZOOM_resultset_sort(ZOOM_resultset r, while (ZOOM_event (1, &c)) ; } + + return 0; } ZOOM_API(void) @@ -905,7 +989,7 @@ static zoom_ret do_connect (ZOOM_connection c) } } c->state = STATE_IDLE; - set_ZOOM_error(c, ZOOM_ERROR_CONNECT, effective_host); + set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port); return zoom_complete; } @@ -1045,7 +1129,7 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c) ZOOM_options_get(c->options, "implementationName"), odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName)); - version = odr_strdup(c->odr_out, "$Revision: 1.50 $"); + version = odr_strdup(c->odr_out, "$Revision: 1.62 $"); if (strlen(version) > 10) /* check for unexpanded CVS strings */ version[strlen(version)-2] = '\0'; ireq->implementationVersion = odr_prepend(c->odr_out, @@ -2035,7 +2119,8 @@ static void handle_queryExpressionTerm(ZOOM_options opt, const char *name, { case Z_Term_general: ZOOM_options_setl(opt, name, - term->u.general->buf, term->u.general->len); + (const char *)(term->u.general->buf), + term->u.general->len); break; case Z_Term_characterString: ZOOM_options_set(opt, name, term->u.characterString); @@ -2348,7 +2433,23 @@ static zoom_ret send_present(ZOOM_connection c) ZOOM_API(ZOOM_scanset) ZOOM_connection_scan (ZOOM_connection c, const char *start) { + ZOOM_scanset s; + ZOOM_query q = ZOOM_query_create(); + + ZOOM_query_prefix (q, start); + + s = ZOOM_connection_scan1(c, q); + ZOOM_query_destroy (q); + return s; + +} + +ZOOM_API(ZOOM_scanset) +ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q) +{ ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan)); + char *start; + char *freeme = 0; scan->connection = c; scan->odr = odr_createmem (ODR_DECODE); @@ -2356,9 +2457,34 @@ ZOOM_connection_scan (ZOOM_connection c, const char *start) scan->refcount = 1; scan->scan_response = 0; - if ((scan->termListAndStartPoint = - p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, - start))) + /* + * We need to check the query-type, so we can recognise CQL and + * compile it into a form that we can use here. The ZOOM_query + * structure has no explicit `type' member, but inspection of the + * ZOOM_query_prefix() and ZOOM_query_cql() functions shows how + * the structure is set up in each case. + */ + + if (q->z_query->which == Z_Query_type_1) { + yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'", + c, q, q->query_string); + start = q->query_string; + } else if (q->z_query->which == Z_Query_type_104) { + yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'", + c, q, q->query_string); + start = freeme = cql2pqf(c, q->query_string); + if (start == 0) + return 0; + } else { + yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'", + c, q, q->query_string); + abort(); + } + + scan->termListAndStartPoint = + p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start); + xfree (freeme); + if (scan->termListAndStartPoint != 0) { ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN); task->u.scan.scan = scan; @@ -3237,7 +3363,7 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu) } else { - set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port); do_close(c); } break; @@ -3417,7 +3543,7 @@ static int do_read (ZOOM_connection c) } else { - set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port); do_close (c); } } @@ -3480,9 +3606,9 @@ static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out) return zoom_pending; } if (c->state == STATE_CONNECTING) - set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port); else - set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port); do_close (c); return zoom_complete; } @@ -3574,6 +3700,14 @@ ZOOM_connection_addinfo (ZOOM_connection c) } ZOOM_API(const char *) +ZOOM_connection_diagset (ZOOM_connection c) +{ + const char *diagset; + ZOOM_connection_error_x (c, 0, 0, &diagset); + return diagset; +} + +ZOOM_API(const char *) ZOOM_diag_str (int error) { switch (error) @@ -3602,6 +3736,10 @@ ZOOM_diag_str (int error) return "Unsupported query type"; case ZOOM_ERROR_INVALID_QUERY: return "Invalid query"; + case ZOOM_ERROR_CQL_PARSE: + return "CQL parsing error"; + case ZOOM_ERROR_CQL_TRANSFORM: + return "CQL transformation error"; default: return diagbib1_str (error); } @@ -3649,7 +3787,7 @@ static int ZOOM_connection_do_io(ZOOM_connection c, int mask) if (r == CS_NONE) { event = ZOOM_Event_create (ZOOM_EVENT_CONNECT); - set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port); do_close (c); ZOOM_connection_put_event (c, event); } @@ -3688,7 +3826,7 @@ static int ZOOM_connection_do_io(ZOOM_connection c, int mask) } else { - set_ZOOM_error(c, ZOOM_ERROR_CONNECT, 0); + set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port); do_close (c); ZOOM_connection_put_event (c, event); } @@ -3895,6 +4033,69 @@ ZOOM_event (int no, ZOOM_connection *cs) } return 0; } + + +/* + * Returns an xmalloc()d string containing RPN that corresponds to the + * CQL passed in. On error, sets the Connection object's error state + * and returns a null pointer. + * ### We could cache CQL parser and/or transformer in Connection. + */ +static char *cql2pqf(ZOOM_connection c, const char *cql) +{ + CQL_parser parser; + int error; + struct cql_node *node; + const char *cqlfile; + static cql_transform_t trans; + char pqfbuf[512]; + + parser = cql_parser_create(); + if ((error = cql_parser_string(parser, cql)) != 0) { + cql_parser_destroy(parser); + set_ZOOM_error(c, ZOOM_ERROR_CQL_PARSE, cql); + return 0; + } + + node = cql_parser_result(parser); + /* ### Do not call cql_parser_destroy() yet: it destroys `node'! */ + + cqlfile = ZOOM_connection_option_get(c, "cqlfile"); + if (cqlfile == 0) { + cql_parser_destroy(parser); + cql_node_destroy(node); + set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file"); + return 0; + } + + if ((trans = cql_transform_open_fname(cqlfile)) == 0) { + char buf[512]; + cql_parser_destroy(parser); + cql_node_destroy(node); + sprintf(buf, "can't open CQL transform file '%.200s': %.200s", + cqlfile, strerror(errno)); + set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf); + return 0; + } + + error = cql_transform_buf(trans, node, pqfbuf, sizeof pqfbuf); + cql_parser_destroy(parser); + cql_node_destroy(node); + if (error != 0) { + char buf[512]; + const char *addinfo; + error = cql_transform_error(trans, &addinfo); + cql_transform_close(trans); + sprintf(buf, "%.200s (addinfo=%.200s)", cql_strerror(error), addinfo); + set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf); + return 0; + } + + cql_transform_close(trans); + return xstrdup(pqfbuf); +} + + /* * Local variables: * c-basic-offset: 4