Merge branch 'solr_client' of ssh://git.indexdata.com/home/git/pub/yaz into solr_client
authorDennis Schafroth <dennis@indexdata.com>
Wed, 11 Aug 2010 14:37:06 +0000 (16:37 +0200)
committerDennis Schafroth <dennis@indexdata.com>
Wed, 11 Aug 2010 14:37:06 +0000 (16:37 +0200)
12 files changed:
include/yaz/Makefile.am
include/yaz/record_render.h [new file with mode: 0644]
include/yaz/zoom.h
src/Makefile.am
src/record_render.c [new file with mode: 0644]
src/zoom-c.c
src/zoom-p.h
src/zoom-query.c [new file with mode: 0644]
src/zoom-record-cache.c [new file with mode: 0644]
src/zoom-sru.c [new file with mode: 0644]
src/zoom-z3950.c [new file with mode: 0644]
win/makefile

index f4e6548..125dfa0 100644 (file)
@@ -7,7 +7,7 @@ pkginclude_HEADERS= backend.h ccl.h ccl_xml.h cql.h rpn2cql.h comstack.h \
  diagbib1.h diagsrw.h diagsru_update.h sortspec.h log.h logrpn.h marcdisp.h \
  nmem.h nmem_xml.h odr.h errno.h facet.h \
  options.h otherinfo.h pquery.h prt-ext.h querytowrbuf.h \
- readconf.h record_conv.h retrieval.h statserv.h \
+ readconf.h record_conv.h record_render.h retrieval.h statserv.h \
  tcpip.h test.h timing.h unix.h tpath.h wrbuf.h xmalloc.h \
  yaz-ccl.h yaz-iconv.h yaz-util.h yaz-version.h yconfig.h proto.h \
  xmlquery.h xmltypes.h snprintf.h query-charset.h \
diff --git a/include/yaz/record_render.h b/include/yaz/record_render.h
new file mode 100644 (file)
index 0000000..bdc0889
--- /dev/null
@@ -0,0 +1,68 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Index Data nor the names of its contributors
+ *       may be used to endorse or promote products derived from this
+ *       software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file record_render.h
+ * \brief Record render header
+ */
+
+#ifndef YAZ_RECORD_RENDER_H
+#define YAZ_RECORD_RENDER_H
+
+#include <stddef.h>
+#include <yaz/yconfig.h>
+#include <yaz/z-core.h>
+#include <yaz/wrbuf.h>
+
+YAZ_BEGIN_CDECL
+
+/** \brief render records (ZOOM style)
+    \param npr record structure to be rendered (Z39.50)
+    \param schema for record (if known)
+    \param wrbuf Working WRBUF
+    \param type_spec format spec
+    \param len length of returned buffer
+    \retval !=0 buffer
+    \retval =0 record could not be rendered
+*/
+YAZ_EXPORT
+const char *yaz_record_render(Z_NamePlusRecord *npr, const char *schema,
+                              WRBUF wrbuf,
+                              const char *type_spec, int *len);
+
+YAZ_END_CDECL
+
+#endif
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 3551a77..ca38b0d 100644 (file)
@@ -293,6 +293,9 @@ ZOOM_query_prefix(ZOOM_query s, const char *str);
 ZOOM_API(int)
 ZOOM_query_sortby(ZOOM_query s, const char *criteria);
 
+ZOOM_API(void)
+ZOOM_query_addref(ZOOM_query s);
+
 /* ----------------------------------------------------------- */
 /* scan */
 ZOOM_API(ZOOM_scanset)
index b5bb28f..b27d4ac 100644 (file)
@@ -88,7 +88,8 @@ libyaz_la_SOURCES=version.c options.c log.c \
   zget.c yaz-ccl.c diag-entry.c diag-entry.h \
   logrpn.c \
   otherinfo.c pquery.c sortspec.c charneg.c initopt.c \
-  zoom-c.c zoom-socket.c zoom-opt.c zoom-p.h \
+  zoom-c.c zoom-z3950.c zoom-sru.c zoom-query.c zoom-record-cache.c \
+  record_render.c zoom-socket.c zoom-opt.c zoom-p.h \
   grs1disp.c zgdu.c soap.c srw.c srwutil.c uri.c \
   opacdisp.c cclfind.c ccltoken.c cclerrms.c cclqual.c cclptree.c cclp.h \
   cclqfile.c cclstr.c cclxmlconfig.c ccl_stop_words.c \
diff --git a/src/record_render.c b/src/record_render.c
new file mode 100644 (file)
index 0000000..d4180dd
--- /dev/null
@@ -0,0 +1,319 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file record_render.c
+ * \brief Render Z39.50 records (NamePlusRecord)
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+
+#include <yaz/marcdisp.h>
+#include <yaz/record_render.h>
+#include <yaz/yaz-iconv.h>
+#include <yaz/proto.h>
+#include <yaz/oid_db.h>
+
+static yaz_iconv_t iconv_create_charset(const char *record_charset)
+{
+    char to[40];
+    char from[40];
+    yaz_iconv_t cd = 0;
+
+    *from = '\0';
+    strcpy(to, "UTF-8");
+    if (record_charset && *record_charset)
+    {
+        /* Use "from,to" or just "from" */
+        const char *cp = strchr(record_charset, ',');
+        size_t clen = strlen(record_charset);
+        if (cp && cp[1])
+        {
+            strncpy( to, cp+1, sizeof(to)-1);
+            to[sizeof(to)-1] = '\0';
+            clen = cp - record_charset;
+        }
+        if (clen > sizeof(from)-1)
+            clen = sizeof(from)-1;
+        
+        if (clen)
+            strncpy(from, record_charset, clen);
+        from[clen] = '\0';
+    }
+    if (*from && *to)
+        cd = yaz_iconv_open(to, from);
+    return cd;
+}
+
+static const char *return_marc_record(WRBUF wrbuf,
+                                      int marc_type,
+                                      int *len,
+                                      const char *buf, int sz,
+                                      const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+    yaz_marc_t mt = yaz_marc_create();
+    const char *ret_string = 0;
+
+    if (cd)
+        yaz_marc_iconv(mt, cd);
+    yaz_marc_xml(mt, marc_type);
+    if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
+    {
+        if (len)
+            *len = wrbuf_len(wrbuf);
+        ret_string = wrbuf_cstr(wrbuf);
+    }
+    yaz_marc_destroy(mt);
+    if (cd)
+        yaz_iconv_close(cd);
+    return ret_string;
+}
+
+static const char *return_opac_record(WRBUF wrbuf,
+                                      int marc_type,
+                                      int *len,
+                                      Z_OPACRecord *opac_rec,
+                                      const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+    yaz_marc_t mt = yaz_marc_create();
+
+    if (cd)
+        yaz_marc_iconv(mt, cd);
+    yaz_marc_xml(mt, marc_type);
+
+    yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
+    yaz_marc_destroy(mt);
+
+    if (cd)
+        yaz_iconv_close(cd);
+    if (len)
+        *len = wrbuf_len(wrbuf);
+    return wrbuf_cstr(wrbuf);
+}
+
+static const char *return_string_record(WRBUF wrbuf,
+                                        int *len,
+                                        const char *buf, int sz,
+                                        const char *record_charset)
+{
+    yaz_iconv_t cd = iconv_create_charset(record_charset);
+
+    if (cd)
+    {
+        wrbuf_iconv_write(wrbuf, cd, buf, sz);
+        wrbuf_iconv_reset(wrbuf, cd);
+
+        buf = wrbuf_cstr(wrbuf);
+        sz = wrbuf_len(wrbuf);
+        yaz_iconv_close(cd);
+    }
+    if (len)
+        *len = sz;
+    return buf;
+}
+
+static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
+                                       Z_NamePlusRecord *npr,
+                                       int marctype, const char *charset)
+{
+    Z_External *r = (Z_External *) npr->u.databaseRecord;
+    const Odr_oid *oid = r->direct_reference;
+
+    wrbuf_rewind(wrbuf);
+    /* render bibliographic record .. */
+    if (r->which == Z_External_OPAC)
+    {
+        return return_opac_record(wrbuf, marctype, len,
+                                  r->u.opac, charset);
+    }
+    if (r->which == Z_External_sutrs)
+        return return_string_record(wrbuf, len,
+                                    (char*) r->u.sutrs->buf,
+                                    r->u.sutrs->len,
+                                    charset);
+    else if (r->which == Z_External_octet)
+    {
+        if (yaz_oid_is_iso2709(oid))
+        {
+            const char *ret_buf = return_marc_record(
+                wrbuf, marctype, len,
+                (const char *) r->u.octet_aligned->buf,
+                r->u.octet_aligned->len,
+                charset);
+            if (ret_buf)
+                return ret_buf;
+            /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
+            if (marctype != YAZ_MARC_ISO2709)
+                return 0;
+        }
+        return return_string_record(wrbuf, len,
+                                    (const char *) r->u.octet_aligned->buf,
+                                    r->u.octet_aligned->len,
+                                    charset);
+    }
+    else if (r->which == Z_External_grs1)
+    {
+        yaz_display_grs1(wrbuf, r->u.grs1, 0);
+        return return_string_record(wrbuf, len,
+                                    wrbuf_buf(wrbuf),
+                                    wrbuf_len(wrbuf),
+                                    charset);
+    }
+    return 0;
+}
+    
+static const char *get_record_format(WRBUF wrbuf, int *len,
+                                     Z_NamePlusRecord *npr,
+                                     int marctype, const char *charset,
+                                     const char *format)
+{
+    const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
+#if YAZ_HAVE_XML2
+    if (*format == '1' && len)
+    {
+        /* try to XML format res */
+        xmlDocPtr doc;
+        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
+        doc = xmlParseMemory(res, *len);
+        if (doc)
+        {
+            xmlChar *xml_mem;
+            int xml_size;
+            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
+            wrbuf_rewind(wrbuf);
+            wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
+            xmlFree(xml_mem);
+            xmlFreeDoc(doc);
+            res = wrbuf_cstr(wrbuf);
+            *len = wrbuf_len(wrbuf);
+        } 
+    }
+#endif
+    return res;
+}
+
+const char *yaz_record_render(Z_NamePlusRecord *npr, const char *schema,
+                              WRBUF wrbuf,
+                              const char *type_spec, int *len)
+{
+    size_t i;
+    char type[40];
+    char charset[40];
+    char format[3];
+    const char *cp = type_spec;
+
+    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
+         i++)
+        type[i] = cp[i];
+    type[i] = '\0';
+    charset[0] = '\0';
+    format[0] = '\0';
+    while (1)
+    {
+        while (cp[i] == ' ')
+            i++;
+        if (cp[i] != ';')
+            break;
+        i++;
+        while (cp[i] == ' ')
+            i++;
+        if (!strncmp(cp + i, "charset=", 8))
+        {
+            size_t j = 0;
+            i = i + 8; /* skip charset= */
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
+            {
+                if (j < sizeof(charset)-1)
+                    charset[j++] = cp[i];
+            }
+            charset[j] = '\0';
+        }
+        else if (!strncmp(cp + i, "format=", 7))
+        {
+            size_t j = 0; 
+            i = i + 7;
+            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
+            {
+                if (j < sizeof(format)-1)
+                    format[j++] = cp[i];
+            }
+            format[j] = '\0';
+        } 
+    }
+    if (!strcmp(type, "database"))
+    {
+        if (len)
+            *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
+        return npr->databaseName;
+    }
+    else if (!strcmp(type, "schema"))
+    {
+        if (len)
+            *len = schema ? strlen(schema) : 0;
+        return schema;
+    }
+    else if (!strcmp(type, "syntax"))
+    {
+        const char *desc = 0;   
+        if (npr->which == Z_NamePlusRecord_databaseRecord)
+        {
+            Z_External *r = (Z_External *) npr->u.databaseRecord;
+            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
+        }
+        if (!desc)
+            desc = "none";
+        if (len)
+            *len = strlen(desc);
+        return desc;
+    }
+    if (npr->which != Z_NamePlusRecord_databaseRecord)
+        return 0;
+
+    /* from now on - we have a database record .. */
+    if (!strcmp(type, "render"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
+    }
+    else if (!strcmp(type, "xml"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
+                                 format);
+    }
+    else if (!strcmp(type, "txml"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
+                                 format);
+    }
+    else if (!strcmp(type, "raw"))
+    {
+        return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
+            format);
+    }
+    else if (!strcmp(type, "ext"))
+    {
+        if (len) *len = -1;
+        return (const char *) npr->u.databaseRecord;
+    }
+    else if (!strcmp(type, "opac"))
+    {
+        if (npr->u.databaseRecord->which == Z_External_OPAC)
+            return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
+                                     format);
+    }
+    return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index d42e394..f5356b9 100644 (file)
 #include <yaz/xmalloc.h>
 #include <yaz/otherinfo.h>
 #include <yaz/log.h>
-#include <yaz/pquery.h>
-#include <yaz/marcdisp.h>
 #include <yaz/diagbib1.h>
 #include <yaz/charneg.h>
-#include <yaz/ill.h>
-#include <yaz/srw.h>
-#include <yaz/cql.h>
-#include <yaz/ccl.h>
 #include <yaz/query-charset.h>
-#include <yaz/copy_types.h>
 #include <yaz/snprintf.h>
 #include <yaz/facet.h>
 
 YAZ_SHPTR_TYPE(WRBUF)
 #endif
 
-static int log_api = 0;
-static int log_details = 0;
-
-typedef enum {
-    zoom_pending,
-    zoom_complete
-} zoom_ret;
+static int log_api0 = 0;
+static int log_details0 = 0;
 
 static void resultset_destroy(ZOOM_resultset r);
-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);
 
 ZOOM_API(const char *) ZOOM_get_event_str(int event)
 {
@@ -66,39 +52,23 @@ ZOOM_API(const char *) ZOOM_get_event_str(int event)
     return ar[event];
 }
 
-/*
- * This wrapper is just for logging failed lookups.  It would be nicer
- * if it could cause failure when a lookup fails, but that's hard.
- */
-static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
-                                     oid_class oid_class, const char *str) {
-    Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
-                                     c->odr_out);
-    if (res == 0)
-        yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
-                c, (int) oid_class, str);
-    return res;
-}
-
-
 static void initlog(void)
 {
     static int log_level_initialized = 0;
     if (!log_level_initialized)
     {
-        log_api = yaz_log_module_level("zoom");
-        log_details = yaz_log_module_level("zoomdetails");
+        log_api0 = yaz_log_module_level("zoom");
+        log_details0 = yaz_log_module_level("zoomdetails");
         log_level_initialized = 1;
     }
 }
 
-static ZOOM_Event ZOOM_Event_create(int kind)
+ZOOM_Event ZOOM_Event_create(int kind)
 {
     ZOOM_Event event = (ZOOM_Event) xmalloc(sizeof(*event));
     event->kind = kind;
     event->next = 0;
     event->prev = 0;
-    yaz_log(log_details, "ZOOM_Event_create(kind=%d)", kind);
     return event;
 }
 
@@ -107,7 +77,7 @@ static void ZOOM_Event_destroy(ZOOM_Event event)
     xfree(event);
 }
 
-static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
 {
     if (c->m_queue_back)
     {
@@ -161,9 +131,9 @@ ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
 
 void ZOOM_connection_remove_tasks(ZOOM_connection c);
 
-static void set_dset_error(ZOOM_connection c, int error,
-                           const char *dset,
-                           const char *addinfo, const char *addinfo2)
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2)
 {
     char *cp;
 
@@ -188,7 +158,7 @@ static void set_dset_error(ZOOM_connection c, int error,
         c->addinfo = xstrdup(addinfo);
     if (error != ZOOM_ERROR_NONE)
     {
-        yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
+        yaz_log(c->log_api, "%p set_dset_error %s %s:%d %s %s",
                 c, c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
@@ -196,7 +166,7 @@ static void set_dset_error(ZOOM_connection c, int error,
     }
 }
 
-static int uri_to_code(const char *uri)
+int ZOOM_uri_to_code(const char *uri)
 {
     int code = 0;       
     const char *cp;
@@ -205,27 +175,11 @@ static int uri_to_code(const char *uri)
     return code;
 }
 
-#if YAZ_HAVE_XML2
-static void set_HTTP_error(ZOOM_connection c, int error,
-                           const char *addinfo, const char *addinfo2)
-{
-    set_dset_error(c, error, "HTTP", addinfo, addinfo2);
-}
-
-static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
-{
-    const char *uri = d->uri;
-    if (uri)
-        set_dset_error(c, uri_to_code(uri), uri, d->details, 0);
-}
-
-#endif
 
 
-static void set_ZOOM_error(ZOOM_connection c, int error,
-                           const char *addinfo)
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo)
 {
-    set_dset_error(c, error, "ZOOM", addinfo, 0);
+    ZOOM_set_dset_error(c, error, "ZOOM", addinfo, 0);
 }
 
 static void clear_error(ZOOM_connection c)
@@ -250,7 +204,7 @@ static void clear_error(ZOOM_connection c)
     case ZOOM_ERROR_UNSUPPORTED_PROTOCOL:
         break;
     default:
-        set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+        ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     }
 }
 
@@ -362,9 +316,6 @@ void ZOOM_connection_remove_tasks(ZOOM_connection c)
         ZOOM_connection_remove_task(c);
 }
 
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName);
 
 ZOOM_API(ZOOM_connection)
     ZOOM_connection_create(ZOOM_options options)
@@ -373,7 +324,10 @@ ZOOM_API(ZOOM_connection)
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_create", c);
+    c->log_api = log_api0;
+    c->log_details = log_details0;
+
+    yaz_log(c->log_api, "%p ZOOM_connection_create", c);
 
     c->proto = PROTO_Z3950;
     c->cs = 0;
@@ -382,7 +336,7 @@ ZOOM_API(ZOOM_connection)
     c->state = STATE_IDLE;
     c->addinfo = 0;
     c->diagset = 0;
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     c->buf_in = 0;
     c->len_in = 0;
     c->buf_out = 0;
@@ -428,8 +382,8 @@ ZOOM_API(ZOOM_connection)
 
 /* set database names. Take local databases (if set); otherwise
    take databases given in ZURL (if set); otherwise use Default */
-static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
-                                int *num, ODR odr)
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr)
 {
     char **databaseNames;
     const char *cp = ZOOM_options_get(options, "databaseName");
@@ -480,10 +434,10 @@ ZOOM_API(void)
 
     initlog();
 
-    yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
+    yaz_log(c->log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
             c, host ? host : "null", portnum);
 
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_set_error(c, ZOOM_ERROR_NONE, 0);
     ZOOM_connection_remove_tasks(c);
 
     if (c->odr_print)
@@ -501,17 +455,17 @@ ZOOM_API(void)
 
     if (c->cs)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect reconnect ok", c);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect reconnect ok", c);
         c->reconnect_ok = 1;
         return;
     }
-    yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect connect", c);
     xfree(c->proxy);
     c->proxy = 0;
     val = ZOOM_options_get(c->options, "proxy");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect proxy=%s", c, val);
         c->proxy = xstrdup(val);
     }
 
@@ -520,7 +474,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "charset");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect charset=%s", c, val);
         c->charset = xstrdup(val);
     }
 
@@ -528,7 +482,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "lang");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect lang=%s", c, val);
         c->lang = xstrdup(val);
     }
     else
@@ -592,7 +546,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "cookie");
     if (val && *val)
     { 
-        yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
+        yaz_log(c->log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
         c->cookie_out = xstrdup(val);
     }
 
@@ -601,7 +555,7 @@ ZOOM_API(void)
     val = ZOOM_options_get(c->options, "clientIP");
     if (val && *val)
     {
-        yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
+        yaz_log(c->log_details, "%p ZOOM_connection_connect clientIP=%s",
                 c, val);
         c->client_IP = xstrdup(val);
     }
@@ -633,7 +587,7 @@ ZOOM_API(void)
         ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
 
     c->async = ZOOM_options_get_bool(c->options, "async", 0);
-    yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
+    yaz_log(c->log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
     task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
 
@@ -644,156 +598,6 @@ ZOOM_API(void)
     }
 }
 
-ZOOM_API(ZOOM_query)
-    ZOOM_query_create(void)
-{
-    ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
-
-    yaz_log(log_details, "%p ZOOM_query_create", s);
-    s->refcount = 1;
-    s->z_query = 0;
-    s->sort_spec = 0;
-    s->odr = odr_createmem(ODR_ENCODE);
-    s->query_string = 0;
-
-    return s;
-}
-
-ZOOM_API(void)
-    ZOOM_query_destroy(ZOOM_query s)
-{
-    if (!s)
-        return;
-
-    (s->refcount)--;
-    yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
-    if (s->refcount == 0)
-    {
-        odr_destroy(s->odr);
-        xfree(s);
-    }
-}
-
-ZOOM_API(int)
-    ZOOM_query_prefix(ZOOM_query s, const char *str)
-{
-    s->query_string = odr_strdup(s->odr, str);
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    s->z_query->which = Z_Query_type_1;
-    s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
-    if (!s->z_query->u.type_1)
-    {
-        yaz_log(log_details, "%p ZOOM_query_prefix str=%s failed", s, str);
-        s->z_query = 0;
-        return -1;
-    }
-    yaz_log(log_details, "%p ZOOM_query_prefix str=%s", s, str);
-    return 0;
-}
-
-ZOOM_API(int)
-    ZOOM_query_cql(ZOOM_query s, const char *str)
-{
-    Z_External *ext;
-
-    s->query_string = odr_strdup(s->odr, str);
-
-    ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
-    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
-    ext->indirect_reference = 0;
-    ext->descriptor = 0;
-    ext->which = Z_External_CQL;
-    ext->u.cql = s->query_string;
-    
-    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
-    s->z_query->which = Z_Query_type_104;
-    s->z_query->u.type_104 =  ext;
-
-    yaz_log(log_details, "%p ZOOM_query_cql str=%s", s, 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;
-    ZOOM_connection freeme = 0;
-
-    yaz_log(log_details, "%p ZOOM_query_cql2rpn str=%s conn=%p", s, str, conn);
-    if (conn == 0)
-        conn = freeme = ZOOM_connection_create(0);
-
-    rpn = cql2pqf(conn, str);
-    if (freeme != 0)
-        ZOOM_connection_destroy(freeme);
-    if (rpn == 0)
-        return -1;
-
-    ret = ZOOM_query_prefix(s, rpn);
-    xfree(rpn);
-    return ret;
-}
-
-/*
- * Analogous in every way to ZOOM_query_cql2rpn(), except that there
- * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
- * to the server, as the YAZ GFS doesn't know how to handle this.
- */
-ZOOM_API(int)
-    ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
-                       int *ccl_error, const char **error_string,
-                       int *error_pos)
-{
-    int ret;
-    struct ccl_rpn_node *rpn;
-    CCL_bibset bibset = ccl_qual_mk();
-
-    if (config)
-        ccl_qual_buf(bibset, config);
-
-    rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
-    if (!rpn)
-    {
-        *error_string = ccl_err_msg(*ccl_error);
-        ret = -1;
-    }
-    else
-    {
-        WRBUF wr = wrbuf_alloc();
-        ccl_pquery(wr, rpn);
-        ccl_rpn_delete(rpn);
-        ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
-        wrbuf_destroy(wr);
-    }
-    ccl_qual_rm(&bibset);
-    return ret;
-}
-
-ZOOM_API(int)
-    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
-{
-    s->sort_spec = yaz_sort_spec(s->odr, criteria);
-    if (!s->sort_spec)
-    {
-        yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s failed",
-                s, criteria);
-        return -1;
-    }
-    yaz_log(log_details, "%p ZOOM_query_sortby criteria=%s", s, criteria);
-    return 0;
-}
-
-static zoom_ret do_write(ZOOM_connection c);
-
 ZOOM_API(void) ZOOM_resultset_release(ZOOM_resultset r)
 {
 #if ZOOM_RESULT_LISTS
@@ -827,7 +631,7 @@ ZOOM_API(void)
 #endif
     if (!c)
         return;
-    yaz_log(log_api, "%p ZOOM_connection_destroy", c);
+    yaz_log(c->log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
         cs_close(c->cs);
 
@@ -879,7 +683,7 @@ void ZOOM_resultset_addref(ZOOM_resultset r)
     {
         yaz_mutex_enter(r->mutex);
         (r->refcount)++;
-        yaz_log(log_details, "%p ZOOM_resultset_addref count=%d",
+        yaz_log(log_details0, "%p ZOOM_resultset_addref count=%d",
                 r, r->refcount);
         yaz_mutex_leave(r->mutex);
     }
@@ -892,7 +696,7 @@ ZOOM_resultset ZOOM_resultset_create(void)
 
     initlog();
 
-    yaz_log(log_details, "%p ZOOM_resultset_create", r);
+    yaz_log(log_details0, "%p ZOOM_resultset_create", r);
     r->refcount = 1;
     r->size = 0;
     r->odr = odr_createmem(ODR_ENCODE);
@@ -946,8 +750,8 @@ ZOOM_API(ZOOM_resultset)
     ZOOM_resultsets set;
 #endif
 
-    yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
-    r->r_sort_spec = q->sort_spec;
+    yaz_log(c->log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
+    r->r_sort_spec = ZOOM_query_get_sortspec(q);
     r->query = q;
 
     r->options = ZOOM_options_create_with_parent(c->options);
@@ -968,7 +772,7 @@ ZOOM_API(ZOOM_resultset)
     if (cp)
         r->schema = xstrdup(cp);
 
-    r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
+    r->databaseNames = ZOOM_connection_get_databases(c, c->options, &r->num_databaseNames,
                                          r->odr);
     
     r->connection = c;
@@ -988,12 +792,12 @@ ZOOM_API(ZOOM_resultset)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "ZOOM_connection_search: no comstack");
+            yaz_log(c->log_details, "ZOOM_connection_search: no comstack");
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "ZOOM_connection_search: reconnect");
+            yaz_log(c->log_details, "ZOOM_connection_search: reconnect");
             c->reconnect_ok = 1;
         }
     }
@@ -1012,7 +816,7 @@ ZOOM_API(ZOOM_resultset)
    
     ZOOM_resultset_addref(r);
 
-    (q->refcount)++;
+    ZOOM_query_addref(q);
 
     if (!c->async)
     {
@@ -1041,7 +845,7 @@ ZOOM_API(int)
     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",
+    yaz_log(c->log_api, "%p ZOOM_resultset_sort r=%p sort_type=%s sort_spec=%s",
             r, r, sort_type, sort_spec);
     if (!c)
         return 0;
@@ -1050,12 +854,12 @@ ZOOM_API(int)
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: no comstack", r);
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_sort: prepare reconnect",
+            yaz_log(c->log_details, "%p ZOOM_resultset_sort: prepare reconnect",
                     r);
             c->reconnect_ok = 1;
         }
@@ -1077,38 +881,6 @@ ZOOM_API(int)
     return 0;
 }
 
-static void ZOOM_record_release(ZOOM_record rec)
-{
-    if (!rec)
-        return;
-
-#if SHPTR
-    if (rec->record_wrbuf)
-        YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
-#else
-    if (rec->wrbuf)
-        wrbuf_destroy(rec->wrbuf);
-#endif
-
-    if (rec->odr)
-        odr_destroy(rec->odr);
-}
-
-ZOOM_API(void)
-    ZOOM_resultset_cache_reset(ZOOM_resultset r)
-{
-    int i;
-    for (i = 0; i<RECORD_HASH_SIZE; i++)
-    {
-        ZOOM_record_cache rc;
-        for (rc = r->record_hash[i]; rc; rc = rc->next)
-        {
-            ZOOM_record_release(&rc->rec);
-        }
-        r->record_hash[i] = 0;
-    }
-}
-
 ZOOM_API(void)
     ZOOM_resultset_destroy(ZOOM_resultset r)
 {
@@ -1121,13 +893,13 @@ static void resultset_destroy(ZOOM_resultset r)
         return;
     yaz_mutex_enter(r->mutex);
     (r->refcount)--;
-    yaz_log(log_details, "%p ZOOM_resultset_destroy r=%p count=%d",
+    yaz_log(log_details0, "%p ZOOM_resultset_destroy r=%p count=%d",
             r, r, r->refcount);
     if (r->refcount == 0)
     {
         yaz_mutex_leave(r->mutex);
 
-        yaz_log(log_details, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
+        yaz_log(log_details0, "%p ZOOM_connection resultset_destroy: Deleting resultset (%p) ", r->connection, r);
         ZOOM_resultset_cache_reset(r);
         ZOOM_resultset_release(r);
         ZOOM_query_destroy(r->query);
@@ -1148,27 +920,16 @@ static void resultset_destroy(ZOOM_resultset r)
 ZOOM_API(size_t)
     ZOOM_resultset_size(ZOOM_resultset r)
 {
-    yaz_log(log_details, "ZOOM_resultset_size r=%p count=" ODR_INT_PRINTF,
-            r, r->size);
     return r->size;
 }
 
-static void do_close(ZOOM_connection c)
-{
-    if (c->cs)
-        cs_close(c->cs);
-    c->cs = 0;
-    ZOOM_connection_set_mask(c, 0);
-    c->state = STATE_IDLE;
-}
-
-static int ZOOM_test_reconnect(ZOOM_connection c)
+int ZOOM_test_reconnect(ZOOM_connection c)
 {
     ZOOM_Event event;
 
     if (!c->reconnect_ok)
         return 0;
-    do_close(c);
+    ZOOM_connection_close(c);
     c->reconnect_ok = 0;
     c->tasks->running = 0;
     ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
@@ -1189,7 +950,7 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
 
     if (!r)
         return;
-    yaz_log(log_details, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
+    yaz_log(log_details0, "%p ZOOM_resultset_retrieve force_sync=%d start=%d"
             " count=%d", r, force_sync, start, count);
     c = r->connection;
     if (!c)
@@ -1199,12 +960,12 @@ static void ZOOM_resultset_retrieve(ZOOM_resultset r,
     {
         if (!c->cs)
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: no comstack", r);
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: no comstack", r);
             ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
         }
         else
         {
-            yaz_log(log_details, "%p ZOOM_resultset_retrieve: prepare "
+            yaz_log(log_details0, "%p ZOOM_resultset_retrieve: prepare "
                     "reconnect", r);
             c->reconnect_ok = 1;
         }
@@ -1245,7 +1006,7 @@ ZOOM_API(void)
 
     if (!r)
         return ;
-    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
+    yaz_log(log_api0, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
             r, r, (long) start, (long) count);
     if (count && recs)
         force_present = 1;
@@ -1341,7 +1102,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
 {
     void *add;
 
-    yaz_log(log_details, "%p do_connect effective_host=%s", c, effective_host);
+    yaz_log(c->log_details, "%p do_connect effective_host=%s", c, effective_host);
 
     if (c->cs)
         cs_close(c->cs);
@@ -1362,8 +1123,8 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             yaz_encode_sru_dbpath_buf(c->path, db);
         }
 #else
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
+        ZOOM_connection_close(c);
         return zoom_complete;
 #endif
     }
@@ -1376,7 +1137,7 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -1401,1790 +1162,134 @@ static zoom_ret do_connect_host(ZOOM_connection c, const char *effective_host,
         }
     }
     c->state = STATE_IDLE;
-    set_ZOOM_error(c, ZOOM_ERROR_CONNECT, logical_url);
+    ZOOM_set_error(c, ZOOM_ERROR_CONNECT, logical_url);
     return zoom_complete;
 }
 
-static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
+/* returns 1 if PDU was sent OK (still pending )
+   0 if PDU was not sent OK (nothing to wait for) 
+*/
+
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
 {
-    int i;
-    for (i = 0; i<200; i++)
-    {
-        size_t len;
-        Odr_oid *oid;
-        Z_OtherInformation **oi;
-        char buf[80];
-        const char *val;
-        const char *cp;
-
-        sprintf(buf, "otherInfo%d", i);
-        val = ZOOM_options_get(c->options, buf);
-        if (!val)
-            break;
-        cp = strchr(val, ':');
-        if (!cp)
-            continue;
-        len = cp - val;
-        if (len >= sizeof(buf))
-            len = sizeof(buf)-1;
-        memcpy(buf, val, len);
-        buf[len] = '\0';
-        
-        oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
-                                    buf, out);
-        if (!oid)
-            continue;
-        
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
-    }
+    const char *syntax =
+        ZOOM_options_get(s->options, "preferredRecordSyntax"); 
+    const char *elementSetName =
+        ZOOM_options_get(s->options, "elementSetName");
+
+    return ZOOM_record_cache_lookup(s, pos, syntax, elementSetName);
 }
 
-static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
+ZOOM_API(ZOOM_record)
+    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
 {
-    assert(a);
-    if (c->cookie_out)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
-                              1, c->cookie_out);
-    }
-    if (c->client_IP)
-    {
-        Z_OtherInformation **oi;
-        yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
-                              1, c->client_IP);
-    }
-    otherInfo_attach(c, a, out);
-    if (!z_APDU(out, &a, 0, 0))
+    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
+
+    if (!rec)
     {
-        FILE *outf = fopen("/tmp/apdu.txt", "a");
-        if (a && outf)
-        {
-            ODR odr_pr = odr_createmem(ODR_PRINT);
-            fprintf(outf, "a=%p\n", a);
-            odr_setprint(odr_pr, outf);
-            z_APDU(odr_pr, &a, 0, 0);
-            odr_destroy(odr_pr);
-        }
-        yaz_log(log_api, "%p encoding_APDU: encoding failed", c);
-        set_ZOOM_error(c, ZOOM_ERROR_ENCODE, 0);
-        odr_reset(out);
-        return -1;
+        /*
+         * MIKE: I think force_sync should always be zero, but I don't
+         * want to make this change until I get the go-ahead from
+         * Adam, in case something depends on the old synchronous
+         * behaviour.
+         */
+        int force_sync = 1;
+        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
+        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
+        rec = ZOOM_resultset_record_immediate(r, pos);
     }
-    if (c->odr_print)
-        z_APDU(c->odr_print, &a, 0, 0);
-    yaz_log(log_details, "%p encoding_APDU encoding OK", c);
-    return 0;
+    return rec;
 }
 
-static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan(ZOOM_connection c, const char *start)
 {
-    ZOOM_Event event;
-    assert(a);
-    if (encode_APDU(c, a, c->odr_out))
-        return zoom_complete;
-    yaz_log(log_details, "%p send APDU type=%d", c, a->which);
-    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event(c, event);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
+    ZOOM_scanset s;
+    ZOOM_query q = ZOOM_query_create();
 
-/* returns 1 if PDU was sent OK (still pending )
-   0 if PDU was not sent OK (nothing to wait for) 
-*/
+    ZOOM_query_prefix(q, start);
+
+    s = ZOOM_connection_scan1(c, q);
+    ZOOM_query_destroy(q);
+    return s;
+
+}
 
-static zoom_ret ZOOM_connection_send_init(ZOOM_connection c)
+ZOOM_API(ZOOM_scanset)
+    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
-    Z_InitRequest *ireq = apdu->u.initRequest;
-    Z_IdAuthentication *auth = (Z_IdAuthentication *)
-        odr_malloc(c->odr_out, sizeof(*auth));
-
-    ODR_MASK_SET(ireq->options, Z_Options_search);
-    ODR_MASK_SET(ireq->options, Z_Options_present);
-    ODR_MASK_SET(ireq->options, Z_Options_scan);
-    ODR_MASK_SET(ireq->options, Z_Options_sort);
-    ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
-    ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
-    
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
-    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
-    
-    ireq->implementationId =
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationId"),
-                    ireq->implementationId);
-    
-    ireq->implementationName = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationName"),
-                    odr_prepend(c->odr_out, "ZOOM-C",
-                                ireq->implementationName));
-    
-    ireq->implementationVersion = 
-        odr_prepend(c->odr_out,
-                    ZOOM_options_get(c->options, "implementationVersion"),
-                                ireq->implementationVersion);
-    
-    *ireq->maximumRecordSize = c->maximum_record_size;
-    *ireq->preferredMessageSize = c->preferred_message_size;
-    
-    if (c->group || c->password)
-    {
-        Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
-        pass->groupId = odr_strdup_null(c->odr_out, c->group);
-        pass->userId = odr_strdup_null(c->odr_out, c->user);
-        pass->password = odr_strdup_null(c->odr_out, c->password);
-        auth->which = Z_IdAuthentication_idPass;
-        auth->u.idPass = pass;
-        ireq->idAuthentication = auth;
-    }
-    else if (c->user)
-    {
-        auth->which = Z_IdAuthentication_open;
-        auth->u.open = odr_strdup(c->odr_out, c->user);
-        ireq->idAuthentication = auth;
-    }
-    if (c->proxy)
-    {
-        yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
-                              yaz_oid_userinfo_proxy, 1, c->host_port);
-    }
-    if (c->charset || c->lang)
+    ZOOM_scanset scan = 0;
+    Z_Query *z_query = ZOOM_query_get_Z_Query(q);
+
+    if (!z_query)
+        return 0;
+    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
+    scan->connection = c;
+    scan->odr = odr_createmem(ODR_DECODE);
+    scan->options = ZOOM_options_create_with_parent(c->options);
+    scan->refcount = 1;
+    scan->scan_response = 0;
+    scan->srw_scan_response = 0;
+
+    scan->query = q;
+    ZOOM_query_addref(q);
+    scan->databaseNames = ZOOM_connection_get_databases(c, c->options,
+                                            &scan->num_databaseNames,
+                                            scan->odr);
+
+    if (1)
     {
-        Z_OtherInformation **oi;
-        Z_OtherInformationUnit *oi_unit;
-        
-        yaz_oi_APDU(apdu, &oi);
+        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
+        task->u.scan.scan = scan;
         
-        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
+        (scan->refcount)++;
+        if (!c->async)
         {
-            ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
-            oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
-            oi_unit->information.externallyDefinedInfo =
-                yaz_set_proposal_charneg_list(c->odr_out, " ",
-                                              c->charset, c->lang, 1);
+            while (ZOOM_event(1, &c))
+                ;
         }
     }
-    assert(apdu);
-    return send_APDU(c, apdu);
+    return scan;
 }
 
-#if YAZ_HAVE_XML2
-static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
+ZOOM_API(void)
+    ZOOM_scanset_destroy(ZOOM_scanset scan)
 {
-    Z_GDU *gdu;
-    ZOOM_Event event;
-    const char *database =  ZOOM_options_get(c->options, "databaseName");
-    char *fdatabase = 0;
-    
-    if (database)
-        fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
-    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
-                                       fdatabase ? fdatabase : c->path);
-
-    if (c->sru_mode == zoom_sru_get)
-    {
-        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_post)
-    {
-        yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
-    }
-    else if (c->sru_mode == zoom_sru_soap)
+    if (!scan)
+        return;
+    (scan->refcount)--;
+    if (scan->refcount == 0)
     {
-        yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+        ZOOM_query_destroy(scan->query);
+
+        odr_destroy(scan->odr);
+        
+        ZOOM_options_destroy(scan->options);
+        xfree(scan);
     }
-    if (!z_GDU(c->odr_out, &gdu, 0, 0))
+}
+
+static zoom_ret send_package(ZOOM_connection c)
+{
+    ZOOM_Event event;
+
+    yaz_log(c->log_details, "%p send_package", c);
+    if (!c->tasks)
         return zoom_complete;
-    if (c->odr_print)
-        z_GDU(c->odr_print, &gdu, 0, 0);
-    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
-        
+    assert (c->tasks->which == ZOOM_TASK_PACKAGE);
+    
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
     ZOOM_connection_put_event(c, event);
-    odr_reset(c->odr_out);
-    return do_write(c);
-}
-#endif
+    
+    c->buf_out = c->tasks->u.package->buf_out;
+    c->len_out = c->tasks->u.package->len_out;
 
-#if YAZ_HAVE_XML2
-static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
-{
-    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
-    sr->username = c->user;
-    sr->password = c->password;
-    return sr;
+    return ZOOM_send_buf(c);
 }
-#endif
 
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    int i;
-    int *start, *count;
-    ZOOM_resultset resultset = 0;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    assert(c->tasks);
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        if (!resultset->setname)
-            resultset->setname = xstrdup("default");
-        ZOOM_options_set(resultset->options, "setname", resultset->setname);
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-
-        if (*start >= resultset->size)
-            return zoom_complete;
-        if (*start + *count > resultset->size)
-            *count = resultset->size - *start;
-
-        for (i = 0; i < *count; i++)
-        {
-            ZOOM_record rec =
-                record_cache_lookup(resultset, i + *start,
-                                    c->tasks->u.retrieve.syntax,
-                                    c->tasks->u.retrieve.elementSetName);
-            if (!rec)
-                break;
-            else
-            {
-                ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-                ZOOM_connection_put_event(c, event);
-            }
-        }
-        *start += i;
-        *count -= i;
-
-        if (*count == 0)
-            return zoom_complete;
-        break;
-    default:
-        return zoom_complete;
-    }
-    assert(resultset->query);
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
-    if (resultset->query->z_query->which == Z_Query_type_104
-        && resultset->query->z_query->u.type_104->which == Z_External_CQL)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_cql;
-        sr->u.request->query.cql =resultset->query->z_query->u.type_104->u.cql;
-    }
-    else if (resultset->query->z_query->which == Z_Query_type_1 &&
-             resultset->query->z_query->u.type_1)
-    {
-        sr->u.request->query_type = Z_SRW_query_type_pqf;
-        sr->u.request->query.pqf = resultset->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-    sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
-    sr->u.request->maximumRecords = odr_intdup(
-        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
-        resultset->step : *count);
-    sr->u.request->recordSchema = resultset->schema;
-    
-    option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
-    if (option_val)
-        sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
-
-    option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
-}
-#else
-static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
-{
-    return zoom_complete;
-}
-#endif
-
-static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
-{
-    ZOOM_resultset r;
-    int lslb, ssub, mspn;
-    const char *syntax;
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
-    Z_SearchRequest *search_req = apdu->u.searchRequest;
-    const char *elementSetName;
-    const char *smallSetElementSetName;
-    const char *mediumSetElementSetName;
-    const char *facets;
-
-    assert(c->tasks);
-    assert(c->tasks->which == ZOOM_TASK_SEARCH);
-
-    r = c->tasks->u.search.resultset;
-
-    yaz_log(log_details, "%p ZOOM_connection_send_search set=%p", c, r);
-
-    elementSetName =
-        ZOOM_options_get(r->options, "elementSetName");
-    smallSetElementSetName  =
-        ZOOM_options_get(r->options, "smallSetElementSetName");
-    mediumSetElementSetName =
-        ZOOM_options_get(r->options, "mediumSetElementSetName");
-
-    if (!smallSetElementSetName)
-        smallSetElementSetName = elementSetName;
-
-    if (!mediumSetElementSetName)
-        mediumSetElementSetName = elementSetName;
-
-    facets = ZOOM_options_get(r->options, "facets");
-    if (facets) {
-        Z_FacetList *facet_list = yaz_pqf_parse_facet_list(c->odr_out, facets);
-        if (facet_list) {
-            Z_OtherInformation **oi;
-            yaz_oi_APDU(apdu, &oi);
-            if (facet_list) {
-                yaz_oi_set_facetlist(oi, c->odr_out, facet_list);
-            }
-        }
-    }
-
-    assert(r);
-    assert(r->query);
-
-    /* prepare query for the search request */
-    search_req->query = r->query->z_query;
-    if (!search_req->query)
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-        return zoom_complete;
-    }
-    if (r->query->z_query->which == Z_Query_type_1 || 
-        r->query->z_query->which == Z_Query_type_101)
-    {
-        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                int r;
-                search_req->query = yaz_copy_Z_Query(search_req->query,
-                                                     c->odr_out);
-                
-                r = yaz_query_charset_convert_rpnquery_check(
-                    search_req->query->u.type_1,
-                    c->odr_out, cd);
-                yaz_iconv_close(cd);
-                if (r)
-                {  /* query could not be char converted */
-                    set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-                    return zoom_complete;
-                }
-            }
-        }
-    }
-    search_req->databaseNames = r->databaseNames;
-    search_req->num_databaseNames = r->num_databaseNames;
-
-    /* get syntax (no need to provide unless piggyback is in effect) */
-    syntax = c->tasks->u.search.syntax;
-
-    lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
-    ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
-    mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
-    if (lslb != -1 && ssub != -1 && mspn != -1)
-    {
-        /* So're a Z39.50 expert? Let's hope you don't do sort */
-        *search_req->largeSetLowerBound = lslb;
-        *search_req->smallSetUpperBound = ssub;
-        *search_req->mediumSetPresentNumber = mspn;
-    }
-    else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
-             && r->piggyback && !r->r_sort_spec && !r->schema)
-    {
-        /* Regular piggyback - do it unless we're going to do sort */
-        *search_req->largeSetLowerBound = 2000000000;
-        *search_req->smallSetUpperBound = 1;
-        *search_req->mediumSetPresentNumber = 
-            r->step>0 ? r->step : c->tasks->u.search.count;
-    }
-    else
-    {
-        /* non-piggyback. Need not provide elementsets or syntaxes .. */
-        smallSetElementSetName = 0;
-        mediumSetElementSetName = 0;
-        syntax = 0;
-    }
-    if (smallSetElementSetName && *smallSetElementSetName)
-    {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
-        search_req->smallSetElementSetNames = esn;
-    }
-    if (mediumSetElementSetName && *mediumSetElementSetName)
-    {
-        Z_ElementSetNames *esn =(Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
-        search_req->mediumSetElementSetNames = esn;
-    }
-    if (syntax)
-        search_req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-    
-    if (!r->setname)
-    {
-        if (c->support_named_resultsets)
-        {
-            char setname[14];
-            int ord;
-            /* find the lowest unused ordinal so that we re-use
-               result sets on the server. */
-            for (ord = 1; ; ord++)
-            {
-#if ZOOM_RESULT_LISTS
-                ZOOM_resultsets rsp;
-                sprintf(setname, "%d", ord);
-                for (rsp = c->resultsets; rsp; rsp = rsp->next)
-                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
-                        break;
-                if (!rsp)
-                    break;
-#else
-                ZOOM_resultset rp;
-                sprintf(setname, "%d", ord);
-                for (rp = c->resultsets; rp; rp = rp->next)
-                    if (rp->setname && !strcmp(rp->setname, setname))
-                        break;
-                if (!rp)
-                    break;
-#endif
-
-            }
-            r->setname = xstrdup(setname);
-            yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
-                    "set %s", c, r->setname);
-        }
-        else
-        {
-            yaz_log(log_details, "%p ZOOM_connection_send_search: using "
-                    "default set", c);
-            r->setname = xstrdup("default");
-        }
-        ZOOM_options_set(r->options, "setname", r->setname);
-    }
-    search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
-    return send_APDU(c, apdu);
-}
-
-static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
-{
-    char oid_name_buf[OID_STR_MAX];
-    const char *oid_name;
-    char *addinfo = 0;
-
-    oid_name = yaz_oid_to_string_buf(r->diagnosticSetId, 0, oid_name_buf);
-    switch (r->which)
-    {
-    case Z_DefaultDiagFormat_v2Addinfo:
-        addinfo = r->u.v2Addinfo;
-        break;
-    case Z_DefaultDiagFormat_v3Addinfo:
-        addinfo = r->u.v3Addinfo;
-        break;
-    }
-    xfree(c->addinfo);
-    c->addinfo = 0;
-    set_dset_error(c, *r->condition, oid_name, addinfo, 0);
-}
-
-static void response_diag(ZOOM_connection c, Z_DiagRec *p)
-{
-    if (p->which != Z_DiagRec_defaultFormat)
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-    else
-        response_default_diag(c, p->u.defaultFormat);
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_record_clone(ZOOM_record srec)
-{
-    char *buf;
-    int size;
-    ODR odr_enc;
-    ZOOM_record nrec;
-
-    odr_enc = odr_createmem(ODR_ENCODE);
-    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
-        return 0;
-    buf = odr_getbuf(odr_enc, &size, 0);
-    
-    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
-    yaz_log(log_details, "ZOOM_record create");
-    nrec->odr = odr_createmem(ODR_DECODE);
-#if SHPTR
-    nrec->record_wrbuf = 0;
-#else
-    nrec->wrbuf = 0;
-#endif
-    odr_setbuf(nrec->odr, buf, size, 0);
-    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
-    
-    nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
-    nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
-    nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
-    nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
-    nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
-    odr_destroy(odr_enc);
-    return nrec;
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
-{
-    const char *syntax =
-        ZOOM_options_get(s->options, "preferredRecordSyntax"); 
-    const char *elementSetName =
-        ZOOM_options_get(s->options, "elementSetName");
-
-    return record_cache_lookup(s, pos, syntax, elementSetName);
-}
-
-ZOOM_API(ZOOM_record)
-    ZOOM_resultset_record(ZOOM_resultset r, size_t pos)
-{
-    ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
-
-    if (!rec)
-    {
-        /*
-         * MIKE: I think force_sync should always be zero, but I don't
-         * want to make this change until I get the go-ahead from
-         * Adam, in case something depends on the old synchronous
-         * behaviour.
-         */
-        int force_sync = 1;
-        if (getenv("ZOOM_RECORD_NO_FORCE_SYNC")) force_sync = 0;
-        ZOOM_resultset_retrieve(r, force_sync, pos, 1);
-        rec = ZOOM_resultset_record_immediate(r, pos);
-    }
-    return rec;
-}
-
-ZOOM_API(void)
-    ZOOM_record_destroy(ZOOM_record rec)
-{
-    ZOOM_record_release(rec);
-    yaz_log(log_details, "ZOOM_record destroy");
-    xfree(rec);
-}
-
-
-static yaz_iconv_t iconv_create_charset(const char *record_charset)
-{
-    char to[40];
-    char from[40];
-    yaz_iconv_t cd = 0;
-
-    *from = '\0';
-    strcpy(to, "UTF-8");
-    if (record_charset && *record_charset)
-    {
-        /* Use "from,to" or just "from" */
-        const char *cp = strchr(record_charset, ',');
-        size_t clen = strlen(record_charset);
-        if (cp && cp[1])
-        {
-            strncpy( to, cp+1, sizeof(to)-1);
-            to[sizeof(to)-1] = '\0';
-            clen = cp - record_charset;
-        }
-        if (clen > sizeof(from)-1)
-            clen = sizeof(from)-1;
-        
-        if (clen)
-            strncpy(from, record_charset, clen);
-        from[clen] = '\0';
-    }
-    if (*from && *to)
-        cd = yaz_iconv_open(to, from);
-    return cd;
-}
-
-static const char *return_marc_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      const char *buf, int sz,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-    const char *ret_string = 0;
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-    if (yaz_marc_decode_wrbuf(mt, buf, sz, wrbuf) > 0)
-    {
-        if (len)
-            *len = wrbuf_len(wrbuf);
-        ret_string = wrbuf_cstr(wrbuf);
-    }
-    yaz_marc_destroy(mt);
-    if (cd)
-        yaz_iconv_close(cd);
-    return ret_string;
-}
-
-static const char *return_opac_record(WRBUF wrbuf,
-                                      int marc_type,
-                                      int *len,
-                                      Z_OPACRecord *opac_rec,
-                                      const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-    yaz_marc_t mt = yaz_marc_create();
-
-    if (cd)
-        yaz_marc_iconv(mt, cd);
-    yaz_marc_xml(mt, marc_type);
-
-    yaz_opac_decode_wrbuf(mt, opac_rec, wrbuf);
-    yaz_marc_destroy(mt);
-
-    if (cd)
-        yaz_iconv_close(cd);
-    if (len)
-        *len = wrbuf_len(wrbuf);
-    return wrbuf_cstr(wrbuf);
-}
-
-static const char *return_string_record(WRBUF wrbuf,
-                                        int *len,
-                                        const char *buf, int sz,
-                                        const char *record_charset)
-{
-    yaz_iconv_t cd = iconv_create_charset(record_charset);
-
-    if (cd)
-    {
-        wrbuf_iconv_write(wrbuf, cd, buf, sz);
-        wrbuf_iconv_reset(wrbuf, cd);
-
-        buf = wrbuf_cstr(wrbuf);
-        sz = wrbuf_len(wrbuf);
-        yaz_iconv_close(cd);
-    }
-    if (len)
-        *len = sz;
-    return buf;
-}
-
-static const char *return_record_wrbuf(WRBUF wrbuf, int *len,
-                                       Z_NamePlusRecord *npr,
-                                       int marctype, const char *charset)
-{
-    Z_External *r = (Z_External *) npr->u.databaseRecord;
-    const Odr_oid *oid = r->direct_reference;
-
-    wrbuf_rewind(wrbuf);
-    /* render bibliographic record .. */
-    if (r->which == Z_External_OPAC)
-    {
-        return return_opac_record(wrbuf, marctype, len,
-                                  r->u.opac, charset);
-    }
-    if (r->which == Z_External_sutrs)
-        return return_string_record(wrbuf, len,
-                                    (char*) r->u.sutrs->buf,
-                                    r->u.sutrs->len,
-                                    charset);
-    else if (r->which == Z_External_octet)
-    {
-        if (yaz_oid_is_iso2709(oid))
-        {
-            const char *ret_buf = return_marc_record(
-                wrbuf, marctype, len,
-                (const char *) r->u.octet_aligned->buf,
-                r->u.octet_aligned->len,
-                charset);
-            if (ret_buf)
-                return ret_buf;
-            /* bad ISO2709. Return fail unless raw (ISO2709) is wanted */
-            if (marctype != YAZ_MARC_ISO2709)
-                return 0;
-        }
-        return return_string_record(wrbuf, len,
-                                    (const char *) r->u.octet_aligned->buf,
-                                    r->u.octet_aligned->len,
-                                    charset);
-    }
-    else if (r->which == Z_External_grs1)
-    {
-        yaz_display_grs1(wrbuf, r->u.grs1, 0);
-        return return_string_record(wrbuf, len,
-                                    wrbuf_buf(wrbuf),
-                                    wrbuf_len(wrbuf),
-                                    charset);
-    }
-    return 0;
-}
-    
-ZOOM_API(int)
-    ZOOM_record_error(ZOOM_record rec, const char **cp,
-                      const char **addinfo, const char **diagset)
-{
-    Z_NamePlusRecord *npr;
-    
-    if (!rec)
-        return 0;
-
-    npr = rec->npr;
-    if (rec->diag_uri)
-    {
-        if (cp)
-            *cp = rec->diag_message;
-        if (addinfo)
-            *addinfo = rec->diag_details;
-        if (diagset)
-            *diagset = rec->diag_set;
-        return uri_to_code(rec->diag_uri);
-    }
-    if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
-    {
-        Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
-        int error = YAZ_BIB1_UNSPECIFIED_ERROR;
-        const char *add = 0;
-
-        if (diag_rec->which == Z_DiagRec_defaultFormat)
-        {
-            Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
-            oid_class oclass;
-    
-            error = *ddf->condition;
-            switch (ddf->which)
-            {
-            case Z_DefaultDiagFormat_v2Addinfo:
-                add = ddf->u.v2Addinfo;
-                break;
-            case Z_DefaultDiagFormat_v3Addinfo:
-                add = ddf->u.v3Addinfo;
-                break;
-            }
-            if (diagset)
-                *diagset =
-                    yaz_oid_to_string(yaz_oid_std(),
-                                      ddf->diagnosticSetId, &oclass);
-        }
-        else
-        {
-            if (diagset)
-                *diagset = "Bib-1";
-        }
-        if (addinfo)
-            *addinfo = add ? add : "";
-        if (cp)
-            *cp = diagbib1_str(error);
-        return error;
-    }
-    return 0;
-}
-
-static const char *get_record_format(WRBUF wrbuf, int *len,
-                                     Z_NamePlusRecord *npr,
-                                     int marctype, const char *charset,
-                                     const char *format)
-{
-    const char *res = return_record_wrbuf(wrbuf, len, npr, marctype, charset);
-#if YAZ_HAVE_XML2
-    if (*format == '1' && len)
-    {
-        /* try to XML format res */
-        xmlDocPtr doc;
-        xmlKeepBlanksDefault(0); /* get get xmlDocFormatMemory to work! */
-        doc = xmlParseMemory(res, *len);
-        if (doc)
-        {
-            xmlChar *xml_mem;
-            int xml_size;
-            xmlDocDumpFormatMemory(doc, &xml_mem, &xml_size, 1);
-            wrbuf_rewind(wrbuf);
-            wrbuf_write(wrbuf, (const char *) xml_mem, xml_size);
-            xmlFree(xml_mem);
-            xmlFreeDoc(doc);
-            res = wrbuf_cstr(wrbuf);
-            *len = wrbuf_len(wrbuf);
-        } 
-    }
-#endif
-    return res;
-}
-
-
-static const char *npr_format(Z_NamePlusRecord *npr, const char *schema,
-                              WRBUF wrbuf,
-                              const char *type_spec, int *len)
-{
-    size_t i;
-    char type[40];
-    char charset[40];
-    char format[3];
-    const char *cp = type_spec;
-
-    for (i = 0; cp[i] && cp[i] != ';' && cp[i] != ' ' && i < sizeof(type)-1;
-         i++)
-        type[i] = cp[i];
-    type[i] = '\0';
-    charset[0] = '\0';
-    format[0] = '\0';
-    while (1)
-    {
-        while (cp[i] == ' ')
-            i++;
-        if (cp[i] != ';')
-            break;
-        i++;
-        while (cp[i] == ' ')
-            i++;
-        if (!strncmp(cp + i, "charset=", 8))
-        {
-            size_t j = 0;
-            i = i + 8; /* skip charset= */
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(charset)-1)
-                    charset[j++] = cp[i];
-            }
-            charset[j] = '\0';
-        }
-        else if (!strncmp(cp + i, "format=", 7))
-        {
-            size_t j = 0; 
-            i = i + 7;
-            for (j = 0; cp[i] && cp[i] != ';' && cp[i] != ' '; i++)
-            {
-                if (j < sizeof(format)-1)
-                    format[j++] = cp[i];
-            }
-            format[j] = '\0';
-        } 
-    }
-    if (!strcmp(type, "database"))
-    {
-        if (len)
-            *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
-        return npr->databaseName;
-    }
-    else if (!strcmp(type, "schema"))
-    {
-        if (len)
-            *len = schema ? strlen(schema) : 0;
-        return schema;
-    }
-    else if (!strcmp(type, "syntax"))
-    {
-        const char *desc = 0;   
-        if (npr->which == Z_NamePlusRecord_databaseRecord)
-        {
-            Z_External *r = (Z_External *) npr->u.databaseRecord;
-            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
-        }
-        if (!desc)
-            desc = "none";
-        if (len)
-            *len = strlen(desc);
-        return desc;
-    }
-    if (npr->which != Z_NamePlusRecord_databaseRecord)
-        return 0;
-
-    /* from now on - we have a database record .. */
-    if (!strcmp(type, "render"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_LINE, charset, format);
-    }
-    else if (!strcmp(type, "xml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "txml"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_TURBOMARC, charset,
-                                 format);
-    }
-    else if (!strcmp(type, "raw"))
-    {
-        return get_record_format(wrbuf, len, npr, YAZ_MARC_ISO2709, charset,
-            format);
-    }
-    else if (!strcmp(type, "ext"))
-    {
-        if (len) *len = -1;
-        return (const char *) npr->u.databaseRecord;
-    }
-    else if (!strcmp(type, "opac"))
-    {
-        if (npr->u.databaseRecord->which == Z_External_OPAC)
-            return get_record_format(wrbuf, len, npr, YAZ_MARC_MARCXML, charset,
-                                     format);
-    }
-    return 0;
-}
-
-ZOOM_API(const char *)
-    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
-{
-    WRBUF wrbuf;
-    
-    if (len)
-        *len = 0; /* default return */
-        
-    if (!rec || !rec->npr)
-        return 0;
-
-#if SHPTR
-    if (!rec->record_wrbuf)
-    {
-        WRBUF w = wrbuf_alloc();
-        YAZ_SHPTR_INIT(rec->record_wrbuf, w);
-    }
-    wrbuf = rec->record_wrbuf->ptr;
-#else
-    if (!rec->wrbuf)
-        rec->wrbuf = wrbuf_alloc();
-    wrbuf = rec->wrbuf;
-#endif
-    return npr_format(rec->npr, rec->schema, wrbuf, type_spec, len);
-}
-
-static int strcmp_null(const char *v1, const char *v2)
-{
-    if (!v1 && !v2)
-        return 0;
-    if (!v1 || !v2)
-        return -1;
-    return strcmp(v1, v2);
-}
-
-static size_t record_hash(int pos)
-{
-    if (pos < 0)
-        pos = 0;
-    return pos % RECORD_HASH_SIZE;
-}
-
-static void record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
-                             int pos,
-                             const char *syntax, const char *elementSetName,
-                             const char *schema,
-                             Z_SRW_diagnostic *diag)
-{
-    ZOOM_record_cache rc = 0;
-    
-    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-    ZOOM_connection_put_event(r->connection, event);
-
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos 
-            && strcmp_null(r->schema, rc->schema) == 0
-            && strcmp_null(elementSetName,rc->elementSetName) == 0
-            && strcmp_null(syntax, rc->syntax) == 0)
-            break;
-    }
-    if (!rc)
-    {
-        rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
-        rc->rec.odr = 0;
-#if SHPTR
-        YAZ_SHPTR_INC(r->record_wrbuf);
-        rc->rec.record_wrbuf = r->record_wrbuf;
-#else
-        rc->rec.wrbuf = 0;
-#endif
-        rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
-        
-        rc->syntax = odr_strdup_null(r->odr, syntax);
-        
-        rc->schema = odr_strdup_null(r->odr, r->schema);
-
-        rc->pos = pos;
-        rc->next = r->record_hash[record_hash(pos)];
-        r->record_hash[record_hash(pos)] = rc;
-    }
-    rc->rec.npr = npr;
-    rc->rec.schema = odr_strdup_null(r->odr, schema);
-    rc->rec.diag_set = 0;
-    rc->rec.diag_uri = 0;
-    rc->rec.diag_message = 0;
-    rc->rec.diag_details = 0;
-    if (diag)
-    {
-        if (diag->uri)
-        {
-            char *cp;
-            rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
-            if ((cp = strrchr(rc->rec.diag_set, '/')))
-                *cp = '\0';
-            rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
-        }
-        rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);            
-        rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
-    }
-}
-
-static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
-                                       const char *syntax,
-                                       const char *elementSetName)
-{
-    ZOOM_record_cache rc;
-    
-    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
-    {
-        if (pos == rc->pos)
-        {
-            if (strcmp_null(r->schema, rc->schema))
-                continue;
-            if (strcmp_null(elementSetName,rc->elementSetName))
-                continue;
-            if (strcmp_null(syntax, rc->syntax))
-                continue;
-            return &rc->rec;
-        }
-    }
-    return 0;
-}
-                                             
-static void handle_records(ZOOM_connection c, Z_Records *sr,
-                           int present_phase)
-{
-    ZOOM_resultset resultset;
-    int *start, *count;
-    const char *syntax = 0, *elementSetName = 0;
-
-    if (!c->tasks)
-        return ;
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;        
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return;
-    }
-    if (sr && sr->which == Z_Records_NSD)
-        response_default_diag(c, sr->u.nonSurrogateDiagnostic);
-    else if (sr && sr->which == Z_Records_multipleNSD)
-    {
-        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
-            response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
-        else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-    }
-    else 
-    {
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        if (sr && sr->which == Z_Records_DBOSD)
-        {
-            int i;
-            NMEM nmem = odr_extract_mem(c->odr_in);
-            Z_NamePlusRecordList *p =
-                sr->u.databaseOrSurDiagnostics;
-            for (i = 0; i<p->num_records; i++)
-            {
-                record_cache_add(resultset, p->records[i], i + *start,
-                                 syntax, elementSetName,
-                                 elementSetName, 0);
-            }
-            *count -= i;
-            if (*count < 0)
-                *count = 0;
-            *start += i;
-            yaz_log(log_details, 
-                    "handle_records resultset=%p start=%d count=%d",
-                    resultset, *start, *count);
-
-            /* transfer our response to search_nmem .. we need it later */
-            nmem_transfer(odr_getmem(resultset->odr), nmem);
-            nmem_destroy(nmem);
-            if (present_phase && p->num_records == 0)
-            {
-                /* present response and we didn't get any records! */
-                Z_NamePlusRecord *myrec = 
-                    zget_surrogateDiagRec(
-                        resultset->odr, 0, 
-                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                        "ZOOM C generated. Present phase and no records");
-                record_cache_add(resultset, myrec, *start,
-                                 syntax, elementSetName, 0, 0);
-            }
-        }
-        else if (present_phase)
-        {
-            /* present response and we didn't get any records! */
-            Z_NamePlusRecord *myrec = 
-                zget_surrogateDiagRec(
-                    resultset->odr, 0,
-                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
-                    "ZOOM C generated: Present response and no records");
-            record_cache_add(resultset, myrec, *start, syntax, elementSetName,
-                             0, 0);
-        }
-    }
-}
-
-static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
-{
-    handle_records(c, pr->records, 1);
-}
-
-static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
-                                       Z_Term *term)
-{
-    switch (term->which)
-    {
-    case Z_Term_general:
-        ZOOM_options_setl(opt, name,
-                          (const char *)(term->u.general->buf), 
-                          term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        ZOOM_options_set(opt, name, term->u.characterString);
-        break;
-    case Z_Term_numeric:
-        ZOOM_options_set_int(opt, name, *term->u.numeric);
-        break;
-    }
-}
-
-static void handle_queryExpression(ZOOM_options opt, const char *name,
-                                   Z_QueryExpression *exp)
-{
-    char opt_name[80];
-    
-    switch (exp->which)
-    {
-    case Z_QueryExpression_term:
-        if (exp->u.term && exp->u.term->queryTerm)
-        {
-            sprintf(opt_name, "%s.term", name);
-            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
-        }
-        break;
-    case Z_QueryExpression_query:
-        break;
-    }
-}
-
-static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            
-            if (ext->which == Z_External_searchResult1)
-            {
-                int j;
-                Z_SearchInfoReport *sr = ext->u.searchResult1;
-                
-                if (sr->num)
-                    ZOOM_options_set_int(
-                        resultset->options, "searchresult.size", sr->num);
-
-                for (j = 0; j < sr->num; j++)
-                {
-                    Z_SearchInfoReport_s *ent =
-                        ext->u.searchResult1->elements[j];
-                    char pref[80];
-                    
-                    sprintf(pref, "searchresult.%d", j);
-
-                    if (ent->subqueryId)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.id", pref);
-                        ZOOM_options_set(resultset->options, opt_name,
-                                         ent->subqueryId);
-                    }
-                    if (ent->subqueryExpression)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.subquery", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryExpression);
-                    }
-                    if (ent->subqueryInterpretation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.interpretation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryInterpretation);
-                    }
-                    if (ent->subqueryRecommendation)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.recommendation", pref);
-                        handle_queryExpression(resultset->options, opt_name,
-                                               ent->subqueryRecommendation);
-                    }
-                    if (ent->subqueryCount)
-                    {
-                        char opt_name[80];
-                        sprintf(opt_name, "%s.count", pref);
-                        ZOOM_options_set_int(resultset->options, opt_name,
-                                             *ent->subqueryCount);
-                    }                                             
-                }
-            }
-        }
-    }
-}
-
-static char *get_term_cstr(ODR odr, Z_Term *term) {
-
-    switch (term->which) {
-    case Z_Term_general:
-            return odr_strdupn(odr, (const char *) term->u.general->buf, (size_t) term->u.general->len);
-        break;
-    case Z_Term_characterString:
-        return odr_strdup(odr, term->u.characterString);
-    }
-    return 0;
-}
-
-static ZOOM_facet_field get_zoom_facet_field(ODR odr, Z_FacetField *facet) {
-    int term_index;
-    struct yaz_facet_attr attr_values;
-    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
-    yaz_facet_attr_init(&attr_values);
-    yaz_facet_attr_get_z_attributes(facet->attributes, &attr_values);
-    facet_field->facet_name = odr_strdup(odr, attr_values.useattr);
-    facet_field->num_terms = facet->num_terms;
-    yaz_log(YLOG_DEBUG, "ZOOM_facet_field %s %d terms %d", attr_values.useattr, attr_values.limit, facet->num_terms);
-    facet_field->facet_terms = odr_malloc(odr, facet_field->num_terms * sizeof(*facet_field->facet_terms));
-    for (term_index = 0 ; term_index < facet->num_terms; term_index++) {
-        Z_FacetTerm *facetTerm = facet->terms[term_index];
-        facet_field->facet_terms[term_index].frequency = *facetTerm->count;
-        facet_field->facet_terms[term_index].term = get_term_cstr(odr, facetTerm->term);
-        yaz_log(YLOG_DEBUG, "    term[%d] %s %d",
-                term_index, facet_field->facet_terms[term_index].term, facet_field->facet_terms[term_index].frequency);
-    }
-    return facet_field;
-}
-
-static void handle_facet_result(ZOOM_connection c, ZOOM_resultset r,
-                                Z_OtherInformation *o)
-{
-    int i;
-    for (i = 0; o && i < o->num_elements; i++)
-    {
-        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
-        {
-            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
-            if (ext->which == Z_External_userFacets)
-            {
-                int j;
-                Z_FacetList *fl = ext->u.facetList;
-                r->num_facets   = fl->num;
-                yaz_log(YLOG_DEBUG, "Facets found: %d", fl->num);
-                r->facets       =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets));
-                r->facets_names =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets_names));
-                for (j = 0; j < fl->num; j++)
-                {
-                    r->facets[j] = get_zoom_facet_field(r->odr, fl->elements[j]);
-                    if (!r->facets[j])
-                        yaz_log(YLOG_DEBUG, "Facet field missing on index %d !", j);
-                    r->facets_names[j] = (char *) ZOOM_facet_field_name(r->facets[j]);
-                }
-            }
-        }
-    }
-}
-
-
-static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
-{
-    ZOOM_resultset resultset;
-    ZOOM_Event event;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
-        return ;
-
-    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-    ZOOM_connection_put_event(c, event);
-
-    resultset = c->tasks->u.search.resultset;
-
-    if (sr->resultSetStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "resultSetStatus",
-                             *sr->resultSetStatus);
-    }
-    if (sr->presentStatus)
-    {
-        ZOOM_options_set_int(resultset->options, "presentStatus",
-                             *sr->presentStatus);
-    }
-    handle_search_result(c, resultset, sr->additionalSearchInfo);
-
-    handle_facet_result(c, resultset, sr->additionalSearchInfo);
-
-    resultset->size = *sr->resultCount;
-    handle_records(c, sr->records, 0);
-}
-
-static void sort_response(ZOOM_connection c, Z_SortResponse *res)
-{
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[0]);
-}
-
-static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
-{
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return 0;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->entries && res->entries->nonsurrogateDiagnostics)
-        response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
-    scan->scan_response = res;
-    scan->srw_scan_response = 0;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-    if (res->stepSize)
-        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
-    if (res->positionOfTerm)
-        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
-    if (res->scanStatus)
-        ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
-    if (res->numberOfEntriesReturned)
-        ZOOM_options_set_int(scan->options, "number",
-                             *res->numberOfEntriesReturned);
-    nmem_destroy(nmem);
-    return 1;
-}
-
-static zoom_ret send_sort(ZOOM_connection c,
-                          ZOOM_resultset resultset)
-{
-    if (c->error)
-        resultset->r_sort_spec = 0;
-    if (resultset->r_sort_spec)
-    {
-        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
-        Z_SortRequest *req = apdu->u.sortRequest;
-        
-        req->num_inputResultSetNames = 1;
-        req->inputResultSetNames = (Z_InternationalString **)
-            odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
-        req->inputResultSetNames[0] =
-            odr_strdup(c->odr_out, resultset->setname);
-        req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
-        req->sortSequence = resultset->r_sort_spec;
-        resultset->r_sort_spec = 0;
-        return send_APDU(c, apdu);
-    }
-    return zoom_complete;
-}
-
-static zoom_ret send_present(ZOOM_connection c)
-{
-    Z_APDU *apdu = 0;
-    Z_PresentRequest *req = 0;
-    int i = 0;
-    const char *syntax = 0;
-    const char *elementSetName = 0;
-    ZOOM_resultset  resultset;
-    int *start, *count;
-
-    if (!c->tasks)
-    {
-        yaz_log(log_details, "%p send_present no tasks", c);
-        return zoom_complete;
-    }
-    
-    switch (c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return zoom_complete;
-    }
-    yaz_log(log_details, "%p send_present start=%d count=%d",
-            c, *start, *count);
-
-    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
-    {
-        set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
-                       "", 0);
-    }
-    if (c->error)                  /* don't continue on error */
-        return zoom_complete;
-    yaz_log(log_details, "send_present resultset=%p start=%d count=%d",
-            resultset, *start, *count);
-
-    for (i = 0; i < *count; i++)
-    {
-        ZOOM_record rec =
-            record_cache_lookup(resultset, i + *start, syntax, elementSetName);
-        if (!rec)
-            break;
-        else
-        {
-            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
-            ZOOM_connection_put_event(c, event);
-        }
-    }
-    *start += i;
-    *count -= i;
-
-    if (*count == 0)
-    {
-        yaz_log(log_details, "%p send_present skip=%d no more to fetch", c, i);
-        return zoom_complete;
-    }
-
-    apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
-    req = apdu->u.presentRequest;
-
-    if (i)
-        yaz_log(log_details, "%p send_present skip=%d", c, i);
-
-    *req->resultSetStartPoint = *start + 1;
-
-    if (resultset->step > 0 && resultset->step < *count)
-        *req->numberOfRecordsRequested = resultset->step;
-    else
-        *req->numberOfRecordsRequested = *count;
-    
-    if (*req->numberOfRecordsRequested + *start > resultset->size)
-        *req->numberOfRecordsRequested = resultset->size - *start;
-    assert(*req->numberOfRecordsRequested > 0);
-
-    if (syntax && *syntax)
-        req->preferredRecordSyntax =
-            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
-
-    if (resultset->schema && *resultset->schema)
-    {
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-
-        req->recordComposition = compo;
-        compo->which = Z_RecordComp_complex;
-        compo->u.complex = (Z_CompSpec *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex));
-        compo->u.complex->selectAlternativeSyntax = (bool_t *) 
-            odr_malloc(c->odr_out, sizeof(bool_t));
-        *compo->u.complex->selectAlternativeSyntax = 0;
-
-        compo->u.complex->generic = (Z_Specification *)
-            odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
-
-        compo->u.complex->generic->which = Z_Schema_oid;
-        compo->u.complex->generic->schema.oid = (Odr_oid *)
-            zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
-
-        if (!compo->u.complex->generic->schema.oid)
-        {
-            /* OID wasn't a schema! Try record syntax instead. */
-
-            compo->u.complex->generic->schema.oid = (Odr_oid *)
-                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
-        }
-        if (elementSetName && *elementSetName)
-        {
-            compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
-                odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
-            compo->u.complex->generic->elementSpec->which =
-                Z_ElementSpec_elementSetName;
-            compo->u.complex->generic->elementSpec->u.elementSetName =
-                odr_strdup(c->odr_out, elementSetName);
-        }
-        else
-            compo->u.complex->generic->elementSpec = 0;
-        compo->u.complex->num_dbSpecific = 0;
-        compo->u.complex->dbSpecific = 0;
-        compo->u.complex->num_recordSyntax = 0;
-        compo->u.complex->recordSyntax = 0;
-    }
-    else if (elementSetName && *elementSetName)
-    {
-        Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc(c->odr_out, sizeof(*esn));
-        Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc(c->odr_out, sizeof(*compo));
-        
-        esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup(c->odr_out, elementSetName);
-        compo->which = Z_RecordComp_simple;
-        compo->u.simple = esn;
-        req->recordComposition = compo;
-    }
-    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
-    return send_APDU(c, apdu);
-}
-
-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 = 0;
-
-    if (!q->z_query)
-        return 0;
-    scan = (ZOOM_scanset) xmalloc(sizeof(*scan));
-    scan->connection = c;
-    scan->odr = odr_createmem(ODR_DECODE);
-    scan->options = ZOOM_options_create_with_parent(c->options);
-    scan->refcount = 1;
-    scan->scan_response = 0;
-    scan->srw_scan_response = 0;
-
-    scan->query = q;
-    (q->refcount)++;
-    scan->databaseNames = set_DatabaseNames(c, c->options,
-                                            &scan->num_databaseNames,
-                                            scan->odr);
-
-    if (1)
-    {
-        ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_SCAN);
-        task->u.scan.scan = scan;
-        
-        (scan->refcount)++;
-        if (!c->async)
-        {
-            while (ZOOM_event(1, &c))
-                ;
-        }
-    }
-    return scan;
-}
-
-ZOOM_API(void)
-    ZOOM_scanset_destroy(ZOOM_scanset scan)
-{
-    if (!scan)
-        return;
-    (scan->refcount)--;
-    if (scan->refcount == 0)
-    {
-        ZOOM_query_destroy(scan->query);
-
-        odr_destroy(scan->odr);
-        
-        ZOOM_options_destroy(scan->options);
-        xfree(scan);
-    }
-}
-
-static zoom_ret send_package(ZOOM_connection c)
-{
-    ZOOM_Event event;
-
-    yaz_log(log_details, "%p send_package", c);
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_PACKAGE);
-    
-    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event(c, event);
-    
-    c->buf_out = c->tasks->u.package->buf_out;
-    c->len_out = c->tasks->u.package->len_out;
-
-    return do_write(c);
-}
-
-static zoom_ret ZOOM_connection_send_scan(ZOOM_connection c)
-{
-    ZOOM_scanset scan;
-    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
-    Z_ScanRequest *req = apdu->u.scanRequest;
-
-    yaz_log(log_details, "%p send_scan", c);
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_SCAN);
-    scan = c->tasks->u.scan.scan;
-
-    /* Z39.50 scan can only carry RPN */
-    if (scan->query->z_query->which == Z_Query_type_1 ||
-        scan->query->z_query->which == Z_Query_type_101)
-    {
-        Z_RPNQuery *rpn = scan->query->z_query->u.type_1;
-        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
-        if (cp)
-        {
-            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
-            if (cd)
-            {
-                rpn = yaz_copy_z_RPNQuery(rpn, c->odr_out);
-
-                yaz_query_charset_convert_rpnquery(
-                    rpn, c->odr_out, cd);
-                yaz_iconv_close(cd);
-            }
-        }
-        req->attributeSet = rpn->attributeSetId;
-        if (!req->attributeSet)
-            req->attributeSet = odr_oiddup(c->odr_out, yaz_oid_attset_bib_1);
-        if (rpn->RPNStructure->which == Z_RPNStructure_simple &&
-            rpn->RPNStructure->u.simple->which == Z_Operand_APT)
-        {
-            req->termListAndStartPoint =
-                rpn->RPNStructure->u.simple->u.attributesPlusTerm;
-        }
-        else
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
-            return zoom_complete;
-        }
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    *req->numberOfTermsRequested =
-        ZOOM_options_get_int(scan->options, "number", 20);
-
-    req->preferredPositionInResponse =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "position", 1));
-
-    req->stepSize =
-        odr_intdup(c->odr_out,
-                   ZOOM_options_get_int(scan->options, "stepSize", 0));
-    
-    req->databaseNames = scan->databaseNames;
-    req->num_databaseNames = scan->num_databaseNames;
-
-    return send_APDU(c, apdu);
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    ZOOM_scanset scan;
-    Z_SRW_PDU *sr = 0;
-    const char *option_val = 0;
-
-    if (!c->tasks)
-        return zoom_complete;
-    assert (c->tasks->which == ZOOM_TASK_SCAN);
-    scan = c->tasks->u.scan.scan;
-        
-    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
-
-    /* SRU scan can only carry CQL and PQF */
-    if (scan->query->z_query->which == Z_Query_type_104)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
-        sr->u.scan_request->scanClause.cql = scan->query->query_string;
-    }
-    else if (scan->query->z_query->which == Z_Query_type_1
-             || scan->query->z_query->which == Z_Query_type_101)
-    {
-        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
-        sr->u.scan_request->scanClause.pqf = scan->query->query_string;
-    }
-    else
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
-        return zoom_complete;
-    }
-
-    sr->u.scan_request->maximumTerms = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
-    
-    sr->u.scan_request->responsePosition = odr_intdup(
-        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
-    
-    option_val = ZOOM_options_get(scan->options, "extraArgs");
-    yaz_encode_sru_extra(sr, c->odr_out, option_val);
-    return send_srw(c, sr);
-}
-#else
-static zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
-{
-    return zoom_complete;
-}
-#endif
-
-
-ZOOM_API(size_t)
-    ZOOM_scanset_size(ZOOM_scanset scan)
+ZOOM_API(size_t)
+    ZOOM_scanset_size(ZOOM_scanset scan)
 {
     if (!scan)
         return 0;
@@ -3240,548 +1345,65 @@ static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
         Z_SRW_scanTerm *t = res->terms + pos;
         if (t)
         {
-            *value_term = t->value;
-            *value_len = strlen(*value_term);
-
-            if (t->displayTerm)
-                *disp_term = t->displayTerm;
-            else
-                *disp_term = t->value;
-            *disp_len = strlen(*disp_term);
-            *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
-        }
-    }
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
-                      size_t *occ, size_t *len)
-{
-    const char *value_term = 0;
-    size_t value_len = 0;
-    const char *disp_term = 0;
-    size_t disp_len = 0;
-
-    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
-                        &disp_term, &disp_len);
-    
-    *len = value_len;
-    return value_term;
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
-                              size_t *occ, size_t *len)
-{
-    const char *value_term = 0;
-    size_t value_len = 0;
-    const char *disp_term = 0;
-    size_t disp_len = 0;
-
-    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
-                        &disp_term, &disp_len);
-    
-    *len = disp_len;
-    return disp_term;
-}
-
-ZOOM_API(const char *)
-    ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
-{
-    return ZOOM_options_get(scan->options, key);
-}
-
-ZOOM_API(void)
-    ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
-                            const char *val)
-{
-    ZOOM_options_set(scan->options, key, val);
-}
-
-static Z_APDU *create_es_package(ZOOM_package p, const Odr_oid *oid)
-{
-    const char *str;
-    Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    
-    str = ZOOM_options_get(p->options, "package-name");
-    if (str && *str)
-        req->packageName = odr_strdup(p->odr_out, str);
-    
-    str = ZOOM_options_get(p->options, "user-id");
-    if (str)
-        req->userId = odr_strdup_null(p->odr_out, str);
-    
-    req->packageType = odr_oiddup(p->odr_out, oid);
-
-    str = ZOOM_options_get(p->options, "function");
-    if (str)
-    {
-        if (!strcmp (str, "create"))
-            *req->function = Z_ExtendedServicesRequest_create;
-        if (!strcmp (str, "delete"))
-            *req->function = Z_ExtendedServicesRequest_delete;
-        if (!strcmp (str, "modify"))
-            *req->function = Z_ExtendedServicesRequest_modify;
-    }
-
-    str = ZOOM_options_get(p->options, "waitAction");
-    if (str)
-    {
-        if (!strcmp (str, "wait"))
-            *req->waitAction = Z_ExtendedServicesRequest_wait;
-        if (!strcmp (str, "waitIfPossible"))
-            *req->waitAction = Z_ExtendedServicesRequest_waitIfPossible;
-        if (!strcmp (str, "dontWait"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontWait;
-        if (!strcmp (str, "dontReturnPackage"))
-            *req->waitAction = Z_ExtendedServicesRequest_dontReturnPackage;
-    }
-    return apdu;
-}
-
-static const char *ill_array_lookup(void *clientData, const char *idx)
-{
-    ZOOM_package p = (ZOOM_package) clientData;
-    return ZOOM_options_get(p->options, idx+4);
-}
-
-static Z_External *encode_ill_request(ZOOM_package p)
-{
-    ODR out = p->odr_out;
-    ILL_Request *req;
-    Z_External *r = 0;
-    struct ill_get_ctl ctl;
-        
-    ctl.odr = p->odr_out;
-    ctl.clientData = p;
-    ctl.f = ill_array_lookup;
-        
-    req = ill_get_ILLRequest(&ctl, "ill", 0);
-        
-    if (!ill_Request(out, &req, 0, 0))
-    {
-        int ill_request_size;
-        char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
-        if (ill_request_buf)
-            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
-        return 0;
-    }
-    else
-    {
-        int illRequest_size = 0;
-        char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
-                
-        r = (Z_External *) odr_malloc(out, sizeof(*r));
-        r->direct_reference = odr_oiddup(out, yaz_oid_general_isoill_1);
-        r->indirect_reference = 0;
-        r->descriptor = 0;
-        r->which = Z_External_single;
-                
-        r->u.single_ASN1_type =
-            odr_create_Odr_oct(out,
-                               (unsigned char *)illRequest_buf,
-                               illRequest_size);
-    }
-    return r;
-}
-
-static Z_ItemOrder *encode_item_order(ZOOM_package p)
-{
-    Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
-    const char *str;
-    int len;
-    
-    req->which = Z_IOItemOrder_esRequest;
-    req->u.esRequest = (Z_IORequest *) 
-        odr_malloc(p->odr_out,sizeof(Z_IORequest));
-
-    /* to keep part ... */
-    req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
-    req->u.esRequest->toKeep->supplDescription = 0;
-    req->u.esRequest->toKeep->contact = (Z_IOContact *)
-        odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
-        
-    str = ZOOM_options_get(p->options, "contact-name");
-    req->u.esRequest->toKeep->contact->name =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-phone");
-    req->u.esRequest->toKeep->contact->phone =
-        odr_strdup_null(p->odr_out, str);
-        
-    str = ZOOM_options_get(p->options, "contact-email");
-    req->u.esRequest->toKeep->contact->email =
-        odr_strdup_null(p->odr_out, str);
-        
-    req->u.esRequest->toKeep->addlBilling = 0;
-        
-    /* not to keep part ... */
-    req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
-        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
-        
-    str = ZOOM_options_get(p->options, "itemorder-setname");
-    if (!str)
-        str = "default";
-
-    if (!*str) 
-        req->u.esRequest->notToKeep->resultSetItem = 0;
-    else
-    {
-        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
-            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
-
-        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
-            odr_strdup(p->odr_out, str);
-        req->u.esRequest->notToKeep->resultSetItem->item =
-            odr_intdup(p->odr_out, 0);
-        
-        str = ZOOM_options_get(p->options, "itemorder-item");
-        *req->u.esRequest->notToKeep->resultSetItem->item =
-            (str ? atoi(str) : 1);
-    }
-
-    str = ZOOM_options_getl(p->options, "doc", &len);
-    if (str)
-    {
-        req->u.esRequest->notToKeep->itemRequest =
-            z_ext_record_xml(p->odr_out, str, len);
-    }
-    else
-        req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
-    
-    return req;
-}
-
-Z_APDU *create_admin_package(ZOOM_package p, int type, 
-                             Z_ESAdminOriginPartToKeep **toKeepP,
-                             Z_ESAdminOriginPartNotToKeep **notToKeepP)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
-    if (apdu)
-    {
-        Z_ESAdminOriginPartToKeep  *toKeep;
-        Z_ESAdminOriginPartNotToKeep  *notToKeep;
-        Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-        const char *first_db = "Default";
-        int num_db;
-        char **db = set_DatabaseNames(p->connection, p->options, &num_db,
-                                      p->odr_out);
-        if (num_db > 0)
-            first_db = db[0];
-            
-        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
-        r->descriptor = 0;
-        r->indirect_reference = 0;
-        r->which = Z_External_ESAdmin;
-        
-        r->u.adminService = (Z_Admin *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService));
-        r->u.adminService->which = Z_Admin_esRequest;
-        r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
-        
-        toKeep = r->u.adminService->u.esRequest->toKeep =
-            (Z_ESAdminOriginPartToKeep *) 
-            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
-        toKeep->which = type;
-        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-        toKeep->u.create = odr_nullval();
-        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        
-        r->u.adminService->u.esRequest->notToKeep = notToKeep =
-            (Z_ESAdminOriginPartNotToKeep *)
-            odr_malloc(p->odr_out,
-                       sizeof(*r->u.adminService->u.esRequest->notToKeep));
-        notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
-        notToKeep->u.recordsWillFollow = odr_nullval();
-        if (toKeepP)
-            *toKeepP = toKeep;
-        if (notToKeepP)
-            *notToKeepP = notToKeep;
-    }
-    return apdu;
-}
-
-static Z_APDU *create_xmlupdate_package(ZOOM_package p)
-{
-    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
-    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
-    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
-    int len;
-    const char *doc = ZOOM_options_getl(p->options, "doc", &len);
-
-    if (!doc)
-    {
-        doc = "";
-        len = 0;
-    }
-
-    req->taskSpecificParameters = ext;
-    ext->direct_reference = req->packageType;
-    ext->descriptor = 0;
-    ext->indirect_reference = 0;
-    
-    ext->which = Z_External_octet;
-    ext->u.single_ASN1_type =
-        odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
-    return apdu;
-}
-
-static Z_APDU *create_update_package(ZOOM_package p)
-{
-    Z_APDU *apdu = 0;
-    const char *first_db = "Default";
-    int num_db;
-    char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
-    const char *action = ZOOM_options_get(p->options, "action");
-    int recordIdOpaque_len;
-    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
-        &recordIdOpaque_len);
-    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
-    int record_len;
-    const char *record_buf = ZOOM_options_getl(p->options, "record",
-        &record_len);
-    int recordOpaque_len;
-    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
-        &recordOpaque_len);
-    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
-    const char *version = ZOOM_options_get(p->options, "updateVersion");
-
-    const char *correlationInfo_note =
-        ZOOM_options_get(p->options, "correlationInfo.note");
-    const char *correlationInfo_id =
-        ZOOM_options_get(p->options, "correlationInfo.id");
-    int action_no = -1;
-    Odr_oid *syntax_oid = 0;
-    const Odr_oid *package_oid = yaz_oid_extserv_database_update;
-
-    if (!version)
-        version = "3";
-    if (!syntax_str)
-        syntax_str = "xml";
-    if (!record_buf && !recordOpaque_buf)
-    {
-        record_buf = "void";
-        record_len = 4;
-        syntax_str = "SUTRS";
-    }
-
-    if (syntax_str)
-    {
-        syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
-                                           CLASS_RECSYN, syntax_str,
-                                           p->odr_out);
-    }
-    if (!syntax_oid)
-        return 0;
-
-    if (num_db > 0)
-        first_db = db[0];
-    
-    switch(*version)
-    {
-    case '1':
-        package_oid = yaz_oid_extserv_database_update_first_version;
-        /* old update does not support specialUpdate */
-        if (!action)
-            action = "recordInsert";
-        break;
-    case '2':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update_second_version;
-        break;
-    case '3':
-        if (!action)
-            action = "specialUpdate";
-        package_oid = yaz_oid_extserv_database_update;
-        break;
-    default:
-        return 0;
-    }
-    
-    if (!strcmp(action, "recordInsert"))
-        action_no = Z_IUOriginPartToKeep_recordInsert;
-    else if (!strcmp(action, "recordReplace"))
-        action_no = Z_IUOriginPartToKeep_recordReplace;
-    else if (!strcmp(action, "recordDelete"))
-        action_no = Z_IUOriginPartToKeep_recordDelete;
-    else if (!strcmp(action, "elementUpdate"))
-        action_no = Z_IUOriginPartToKeep_elementUpdate;
-    else if (!strcmp(action, "specialUpdate"))
-        action_no = Z_IUOriginPartToKeep_specialUpdate;
-    else
-        return 0;
-
-    apdu = create_es_package(p, package_oid);
-    if (apdu)
-    {
-        Z_IUOriginPartToKeep *toKeep;
-        Z_IUSuppliedRecords *notToKeep;
-        Z_External *r = (Z_External *)
-            odr_malloc(p->odr_out, sizeof(*r));
-        const char *elementSetName =
-            ZOOM_options_get(p->options, "elementSetName");
-        
-        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        
-        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
-        r->descriptor = 0;
-        r->which = Z_External_update;
-        r->indirect_reference = 0;
-        r->u.update = (Z_IUUpdate *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update));
-        
-        r->u.update->which = Z_IUUpdate_esRequest;
-        r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
-            odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
-        toKeep = r->u.update->u.esRequest->toKeep = 
-            (Z_IUOriginPartToKeep *)
-            odr_malloc(p->odr_out, sizeof(*toKeep));
-        
-        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
-        toKeep->schema = 0;
-        
-        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
-            
-        toKeep->actionQualifier = 0;
-        toKeep->action = odr_intdup(p->odr_out, action_no);
-        
-        notToKeep = r->u.update->u.esRequest->notToKeep = 
-            (Z_IUSuppliedRecords *)
-            odr_malloc(p->odr_out, sizeof(*notToKeep));
-        notToKeep->num = 1;
-        notToKeep->elements = (Z_IUSuppliedRecords_elem **)
-            odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
-        notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
-            odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
-        notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
-        if (recordIdOpaque)
-        {
-            notToKeep->elements[0]->u.opaque = 
-                odr_create_Odr_oct(p->odr_out,
-                                   (const unsigned char *) recordIdOpaque,
-                                   recordIdOpaque_len);
-        }
-        else if (recordIdNumber)
-        {
-            notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
-            
-            notToKeep->elements[0]->u.number =
-                odr_intdup(p->odr_out, atoi(recordIdNumber));
-        }
-        else
-            notToKeep->elements[0]->u.opaque = 0;
-        notToKeep->elements[0]->supplementalId = 0;
-        if (correlationInfo_note || correlationInfo_id)
-        {
-            Z_IUCorrelationInfo *ci;
-            ci = notToKeep->elements[0]->correlationInfo =
-                (Z_IUCorrelationInfo *) odr_malloc(p->odr_out, sizeof(*ci));
-            ci->note = odr_strdup_null(p->odr_out, correlationInfo_note);
-            ci->id = correlationInfo_id ?
-                odr_intdup(p->odr_out, atoi(correlationInfo_id)) : 0;
-        }
-        else
-            notToKeep->elements[0]->correlationInfo = 0;
-        if (recordOpaque_buf)
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid_any(p->odr_out, syntax_oid,
-                                 recordOpaque_buf, recordOpaque_len);
-        }
-        else
-        {
-            notToKeep->elements[0]->record =
-                z_ext_record_oid(p->odr_out, syntax_oid,
-                                 record_buf, record_len);
+            *value_term = t->value;
+            *value_len = strlen(*value_term);
+
+            if (t->displayTerm)
+                *disp_term = t->displayTerm;
+            else
+                *disp_term = t->value;
+            *disp_len = strlen(*disp_term);
+            *occ = t->numberOfRecords ? *t->numberOfRecords : 0;
         }
     }
-    if (0 && apdu)
-    {
-        ODR print = odr_createmem(ODR_PRINT);
+}
 
-        z_APDU(print, &apdu, 0, 0);
-        odr_destroy(print);
-    }
-    return apdu;
+ZOOM_API(const char *)
+    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
+                      size_t *occ, size_t *len)
+{
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
+
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = value_len;
+    return value_term;
 }
 
-ZOOM_API(void)
-    ZOOM_package_send(ZOOM_package p, const char *type)
+ZOOM_API(const char *)
+    ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
+                              size_t *occ, size_t *len)
 {
-    Z_APDU *apdu = 0;
-    ZOOM_connection c;
-    if (!p)
-        return;
-    c = p->connection;
-    odr_reset(p->odr_out);
-    xfree(p->buf_out);
-    p->buf_out = 0;
-    if (!strcmp(type, "itemorder"))
-    {
-        apdu = create_es_package(p, yaz_oid_extserv_item_order);
-        if (apdu)
-        {
-            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
-            
-            r->direct_reference = 
-                odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
-            r->descriptor = 0;
-            r->which = Z_External_itemOrder;
-            r->indirect_reference = 0;
-            r->u.itemOrder = encode_item_order(p);
-
-            apdu->u.extendedServicesRequest->taskSpecificParameters = r;
-        }
-    }
-    else if (!strcmp(type, "create"))  /* create database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
-                                    0, 0);
-    }   
-    else if (!strcmp(type, "drop"))  /* drop database */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
-                                    0, 0);
-    }
-    else if (!strcmp(type, "commit"))  /* commit changes */
-    {
-        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
-                                    0, 0);
-    }
-    else if (!strcmp(type, "update")) /* update record(s) */
-    {
-        apdu = create_update_package(p);
-    }
-    else if (!strcmp(type, "xmlupdate"))
-    {
-        apdu = create_xmlupdate_package(p);
-    }
-    if (apdu)
-    {
-        if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
-        {
-            char *buf;
+    const char *value_term = 0;
+    size_t value_len = 0;
+    const char *disp_term = 0;
+    size_t disp_len = 0;
 
-            ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
-            task->u.package = p;
-            buf = odr_getbuf(p->odr_out, &p->len_out, 0);
-            p->buf_out = (char *) xmalloc(p->len_out);
-            memcpy(p->buf_out, buf, p->len_out);
-            
-            (p->refcount)++;
-            if (!c->async)
-            {
-                while (ZOOM_event(1, &c))
-                    ;
-            }
-        }
-    }
+    ZOOM_scanset_term_x(scan, pos, occ, &value_term, &value_len,
+                        &disp_term, &disp_len);
+    
+    *len = disp_len;
+    return disp_term;
 }
 
+ZOOM_API(const char *)
+    ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
+{
+    return ZOOM_options_get(scan->options, key);
+}
+
+ZOOM_API(void)
+    ZOOM_scanset_option_set(ZOOM_scanset scan, const char *key,
+                            const char *val)
+{
+    ZOOM_options_set(scan->options, key, val);
+}
+
+
 ZOOM_API(ZOOM_package)
     ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
 {
@@ -3846,18 +1468,18 @@ ZOOM_API(int)
 
     if (!task)
         return 0;
-    yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
+    yaz_log(c->log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
             c, task->which, task->running);
     if (c->error != ZOOM_ERROR_NONE)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "removing tasks because of error = %d", c, c->error);
         ZOOM_connection_remove_tasks(c);
         return 0;
     }
     if (task->running)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task already running", c);
         return 0;
     }
@@ -3871,13 +1493,13 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = ZOOM_connection_send_search(c);
+                ret = ZOOM_connection_Z3950_send_search(c);
             break;
         case ZOOM_TASK_RETRIEVE:
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = send_present(c);
+                ret = send_Z3950_present(c);
             break;
         case ZOOM_TASK_CONNECT:
             ret = do_connect(c);
@@ -3886,460 +1508,36 @@ ZOOM_API(int)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_scan(c);
             else
-                ret = ZOOM_connection_send_scan(c);
+                ret = ZOOM_connection_Z3950_send_scan(c);
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
             break;
         case ZOOM_TASK_SORT:
             c->tasks->u.sort.resultset->r_sort_spec = 
-                c->tasks->u.sort.q->sort_spec;
-            ret = send_sort(c, c->tasks->u.sort.resultset);
+                ZOOM_query_get_sortspec(c->tasks->u.sort.q);
+            ret = send_Z3950_sort(c, c->tasks->u.sort.resultset);
             break;
         }
     }
     else
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "remove tasks because no connection exist", c);
         ZOOM_connection_remove_tasks(c);
     }
     if (ret == zoom_complete)
     {
-        yaz_log(log_details, "%p ZOOM_connection_exec_task "
+        yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
                 "task removed (complete)", c);
         ZOOM_connection_remove_task(c);
         return 0;
     }
-    yaz_log(log_details, "%p ZOOM_connection_exec_task "
+    yaz_log(c->log_details, "%p ZOOM_connection_exec_task "
             "task pending", c);
     return 1;
 }
 
-static zoom_ret send_sort_present(ZOOM_connection c)
-{
-    zoom_ret r = zoom_complete;
-
-    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
-        r = send_sort(c, c->tasks->u.search.resultset);
-    if (r == zoom_complete)
-        r = send_present(c);
-    return r;
-}
-
-static int es_response_taskpackage_update(ZOOM_connection c,
-               Z_IUUpdateTaskPackage *utp)
-{
-       if (utp && utp->targetPart)
-       {
-               Z_IUTargetPart *targetPart = utp->targetPart;
-               switch ( *targetPart->updateStatus ) {
-                       case Z_IUTargetPart_success:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
-                               break;
-                       case Z_IUTargetPart_partial:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
-                               break;
-                       case Z_IUTargetPart_failure:
-                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
-                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
-                                       response_diag(c, targetPart->globalDiagnostics[0]);
-                               break;
-               }
-               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
-       }
-    return 1;
-}
-
-static int es_response_taskpackage(ZOOM_connection c,
-                                   Z_TaskPackage *taskPackage)
-{
-       // targetReference
-       Odr_oct *id = taskPackage->targetReference;
-       if (id)
-               ZOOM_options_setl(c->tasks->u.package->options,
-                                                       "targetReference", (char*) id->buf, id->len);
-       
-       // taskStatus
-       switch ( *taskPackage->taskStatus ) {
-               case Z_TaskPackage_pending:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
-                       break;
-               case Z_TaskPackage_active:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
-                       break;
-               case Z_TaskPackage_complete:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
-                       break;
-               case Z_TaskPackage_aborted:
-                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
-                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
-                               response_diag(c, taskPackage->packageDiagnostics[0]);
-                       break;
-       }
-       
-       // taskSpecificParameters
-       // NOTE: Only Update implemented, no others.
-       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
-                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
-                       es_response_taskpackage_update(c, utp);
-       }
-       return 1;
-}
-
-
-static int es_response(ZOOM_connection c,
-                       Z_ExtendedServicesResponse *res)
-{
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
-        return 0;
-    switch (*res->operationStatus) {
-        case Z_ExtendedServicesResponse_done:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
-            break;
-        case Z_ExtendedServicesResponse_accepted:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
-            break;
-        case Z_ExtendedServicesResponse_failure:
-            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
-            if (res->diagnostics && res->num_diagnostics > 0)
-                response_diag(c, res->diagnostics[0]);
-            break;
-    }
-    if (res->taskPackage &&
-        res->taskPackage->which == Z_External_extendedService)
-    {
-        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
-        es_response_taskpackage(c, taskPackage);
-    }
-    if (res->taskPackage && 
-        res->taskPackage->which == Z_External_octet)
-    {
-        Odr_oct *doc = res->taskPackage->u.octet_aligned;
-        ZOOM_options_setl(c->tasks->u.package->options,
-                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
-    }
-    return 1;
-}
-
-static void interpret_init_diag(ZOOM_connection c,
-                                Z_DiagnosticFormat *diag)
-{
-    if (diag->num > 0)
-    {
-        Z_DiagnosticFormat_s *ds = diag->elements[0];
-        if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
-            response_default_diag(c, ds->u.defaultDiagRec);
-    }
-}
-
-
-static void interpret_otherinformation_field(ZOOM_connection c,
-                                             Z_OtherInformation *ui)
-{
-    int i;
-    for (i = 0; i < ui->num_elements; i++)
-    {
-        Z_OtherInformationUnit *unit = ui->list[i];
-        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo &&
-            unit->information.externallyDefinedInfo->which ==
-            Z_External_diag1) 
-        {
-            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
-        } 
-    }
-}
-
-
-static void set_init_option(const char *name, void *clientData) {
-    ZOOM_connection c = (ZOOM_connection) clientData;
-    char buf[80];
-
-    sprintf(buf, "init_opt_%.70s", name);
-    ZOOM_connection_option_set(c, buf, "1");
-}
-
-
-static void recv_apdu(ZOOM_connection c, Z_APDU *apdu)
-{
-    Z_InitResponse *initrs;
-    
-    ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p recv_apdu apdu->which=%d", c, apdu->which);
-    switch(apdu->which)
-    {
-    case Z_APDU_initResponse:
-        yaz_log(log_api, "%p recv_apdu: Received Init response", c);
-        initrs = apdu->u.initResponse;
-        ZOOM_connection_option_set(c, "serverImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "serverImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "serverImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-        /* Set the three old options too, for old applications */
-        ZOOM_connection_option_set(c, "targetImplementationId",
-                                   initrs->implementationId ?
-                                   initrs->implementationId : "");
-        ZOOM_connection_option_set(c, "targetImplementationName",
-                                   initrs->implementationName ?
-                                   initrs->implementationName : "");
-        ZOOM_connection_option_set(c, "targetImplementationVersion",
-                                   initrs->implementationVersion ?
-                                   initrs->implementationVersion : "");
-
-        /* Make initrs->options available as ZOOM-level options */
-        yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
-
-        if (!*initrs->result)
-        {
-            Z_External *uif = initrs->userInformationField;
-
-            set_ZOOM_error(c, ZOOM_ERROR_INIT, 0); /* default error */
-
-            if (uif && uif->which == Z_External_userInfo1)
-                interpret_otherinformation_field(c, uif->u.userInfo1);
-        }
-        else
-        {
-            char *cookie =
-                yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
-                                      yaz_oid_userinfo_cookie, 1, 0);
-            xfree(c->cookie_in);
-            c->cookie_in = 0;
-            if (cookie)
-                c->cookie_in = xstrdup(cookie);
-            if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
-                ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
-                c->support_named_resultsets = 1;
-            if (c->tasks)
-            {
-                assert(c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task(c);
-            }
-            ZOOM_connection_exec_task(c);
-        }
-        if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
-        {
-            NMEM tmpmem = nmem_create();
-            Z_CharSetandLanguageNegotiation *p =
-                yaz_get_charneg_record(initrs->otherInfo);
-            
-            if (p)
-            {
-                char *charset = NULL, *lang = NULL;
-                int sel;
-                
-                yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
-                yaz_log(log_details, "%p recv_apdu target accepted: "
-                        "charset %s, language %s, select %d",
-                        c,
-                        charset ? charset : "none", lang ? lang : "none", sel);
-                if (charset)
-                    ZOOM_connection_option_set(c, "negotiation-charset",
-                                               charset);
-                if (lang)
-                    ZOOM_connection_option_set(c, "negotiation-lang",
-                                               lang);
-
-                ZOOM_connection_option_set(
-                    c,  "negotiation-charset-in-effect-for-records",
-                    (sel != 0) ? "1" : "0");
-                nmem_destroy(tmpmem);
-            }
-        }       
-        break;
-    case Z_APDU_searchResponse:
-        yaz_log(log_api, "%p recv_apdu Search response", c);
-        handle_search_response(c, apdu->u.searchResponse);
-        if (send_sort_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_presentResponse:
-        yaz_log(log_api, "%p recv_apdu Present response", c);
-        handle_present_response(c, apdu->u.presentResponse);
-        if (send_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_sortResponse:
-        yaz_log(log_api, "%p recv_apdu Sort response", c);
-        sort_response(c, apdu->u.sortResponse);
-        if (send_present(c) == zoom_complete)
-            ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_scanResponse:
-        yaz_log(log_api, "%p recv_apdu Scan response", c);
-        scan_response(c, apdu->u.scanResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_extendedServicesResponse:
-        yaz_log(log_api, "%p recv_apdu Extended Services response", c);
-        es_response(c, apdu->u.extendedServicesResponse);
-        ZOOM_connection_remove_task(c);
-        break;
-    case Z_APDU_close:
-        yaz_log(log_api, "%p recv_apdu Close PDU", c);
-        if (!ZOOM_test_reconnect(c))
-        {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
-        }
-        break;
-    default:
-        yaz_log(log_api, "%p Received unknown PDU", c);
-        set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-        do_close(c);
-    }
-}
-
-#if YAZ_HAVE_XML2
-static zoom_ret handle_srw_response(ZOOM_connection c,
-                                    Z_SRW_searchRetrieveResponse *res)
-{
-    ZOOM_resultset resultset = 0;
-    int i;
-    NMEM nmem;
-    ZOOM_Event event;
-    int *start, *count;
-    const char *syntax, *elementSetName;
-
-    if (!c->tasks)
-        return zoom_complete;
-
-    switch(c->tasks->which)
-    {
-    case ZOOM_TASK_SEARCH:
-        resultset = c->tasks->u.search.resultset;
-        start = &c->tasks->u.search.start;
-        count = &c->tasks->u.search.count;
-        syntax = c->tasks->u.search.syntax;
-        elementSetName = c->tasks->u.search.elementSetName;        
-
-        if (!c->tasks->u.search.recv_search_fired)
-        {
-            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
-            ZOOM_connection_put_event(c, event);
-            c->tasks->u.search.recv_search_fired = 1;
-        }
-        break;
-    case ZOOM_TASK_RETRIEVE:
-        resultset = c->tasks->u.retrieve.resultset;
-        start = &c->tasks->u.retrieve.start;
-        count = &c->tasks->u.retrieve.count;
-        syntax = c->tasks->u.retrieve.syntax;
-        elementSetName = c->tasks->u.retrieve.elementSetName;
-        break;
-    default:
-        return zoom_complete;
-    }
-
-    resultset->size = 0;
-
-    if (res->resultSetId)
-        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
-
-    yaz_log(log_details, "%p handle_srw_response got SRW response OK", c);
-
-    if (res->num_diagnostics > 0)
-    {
-        set_SRU_error(c, &res->diagnostics[0]);
-    }
-    else
-    {
-        if (res->numberOfRecords)
-            resultset->size = *res->numberOfRecords;
-        for (i = 0; i<res->num_records; i++)
-        {
-            int pos;
-            Z_SRW_record *sru_rec;
-            Z_SRW_diagnostic *diag = 0;
-            int num_diag;
-            
-            Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
-                odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
-            
-            if (res->records[i].recordPosition && 
-                *res->records[i].recordPosition > 0)
-                pos = *res->records[i].recordPosition - 1;
-            else
-                pos = *start + i;
-            
-            sru_rec = &res->records[i];
-            
-            npr->databaseName = 0;
-            npr->which = Z_NamePlusRecord_databaseRecord;
-            npr->u.databaseRecord = (Z_External *)
-                odr_malloc(c->odr_in, sizeof(Z_External));
-            npr->u.databaseRecord->descriptor = 0;
-            npr->u.databaseRecord->direct_reference =
-                odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
-            npr->u.databaseRecord->which = Z_External_octet;
-            
-            npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
-                odr_malloc(c->odr_in, sizeof(Odr_oct));
-            npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
-                sru_rec->recordData_buf;
-            npr->u.databaseRecord->u.octet_aligned->len = 
-                npr->u.databaseRecord->u.octet_aligned->size = 
-                sru_rec->recordData_len;
-            
-            if (sru_rec->recordSchema 
-                && !strcmp(sru_rec->recordSchema,
-                           "info:srw/schema/1/diagnostics-v1.1"))
-            {
-                sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
-                                                 sru_rec->recordData_len,
-                                                 &diag, &num_diag,
-                                                 resultset->odr);
-            }
-            record_cache_add(resultset, npr, pos, syntax, elementSetName,
-                             sru_rec->recordSchema, diag);
-        }
-        *count -= i;
-        *start += i;
-        if (*count + *start > resultset->size)
-            *count = resultset->size - *start;
-        if (*count < 0)
-            *count = 0;
-        
-        nmem = odr_extract_mem(c->odr_in);
-        nmem_transfer(odr_getmem(resultset->odr), nmem);
-        nmem_destroy(nmem);
-
-        if (*count > 0)
-            return ZOOM_connection_srw_send_search(c);
-    }
-    return zoom_complete;
-}
-#endif
-
-#if YAZ_HAVE_XML2
-static void handle_srw_scan_response(ZOOM_connection c,
-                                     Z_SRW_scanResponse *res)
-{
-    NMEM nmem = odr_extract_mem(c->odr_in);
-    ZOOM_scanset scan;
-
-    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
-        return;
-    scan = c->tasks->u.scan.scan;
-
-    if (res->num_diagnostics > 0)
-        set_SRU_error(c, &res->diagnostics[0]);
-
-    scan->scan_response = 0;
-    scan->srw_scan_response = res;
-    nmem_transfer(odr_getmem(scan->odr), nmem);
-
-    ZOOM_options_set_int(scan->options, "number", res->num_terms);
-    nmem_destroy(nmem);
-}
-#endif
-
 #if YAZ_HAVE_XML2
 static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
 {
@@ -4367,7 +1565,7 @@ static Z_GDU *get_HTTP_Request_url(ODR odr, const char *url)
     return p;
 }
 
-static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
+static zoom_ret send_HTTP_redirect(ZOOM_connection c, const char *uri,
                                   Z_HTTP_Response *cookie_hres)
 {
     struct Z_HTTP_Header *h;
@@ -4416,9 +1614,18 @@ static zoom_ret send_SRW_redirect(ZOOM_connection c, const char *uri,
     c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
 
     odr_reset(c->odr_out);
-    return do_write(c);
+    return ZOOM_send_buf(c);
 }
 
+#if YAZ_HAVE_XML2
+void ZOOM_set_HTTP_error(ZOOM_connection c, int error,
+                         const char *addinfo, const char *addinfo2)
+{
+    ZOOM_set_dset_error(c, error, "HTTP", addinfo, addinfo2);
+}
+#endif
+
+
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     zoom_ret cret = zoom_complete;
@@ -4429,7 +1636,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
     const char *location;
 
     ZOOM_connection_set_mask(c, 0);
-    yaz_log(log_details, "%p handle_http", c);
+    yaz_log(c->log_details, "%p handle_http", c);
     
     if ((hres->code == 301 || hres->code == 302) && c->sru_mode == zoom_sru_get
         && (location = z_HTTP_header_lookup(hres->headers, "Location")))
@@ -4437,75 +1644,38 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         c->no_redirects++;
         if (c->no_redirects > 10)
         {
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
             c->no_redirects = 0;
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             /* since redirect may change host we just reconnect. A smarter
                implementation might check whether it's the same server */
             do_connect_host(c, location, 0);
-            send_SRW_redirect(c, location, hres);
+            send_HTTP_redirect(c, location, hres);
             /* we're OK for now. Operation is not really complete */
             ret = 0;
             cret = zoom_pending;
         }
     }
     else 
-    {   /* not redirect (normal response) */
-        if (!yaz_srw_check_content_type(hres))
-            addinfo = "content-type";
-        else
-        {
-            Z_SOAP *soap_package = 0;
-            ODR o = c->odr_in;
-            Z_SOAP_Handler soap_handlers[2] = {
-                {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
-                {0, 0, 0}
-            };
-            ret = z_soap_codec(o, &soap_package,
-                               &hres->content_buf, &hres->content_len,
-                               soap_handlers);
-            if (!ret && soap_package->which == Z_SOAP_generic &&
-                soap_package->u.generic->no == 0)
-            {
-                Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
-                
-                ZOOM_options_set(c->options, "sru_version", sr->srw_version);
-                ZOOM_options_setl(c->options, "sru_extra_response_data",
-                                  sr->extraResponseData_buf, sr->extraResponseData_len);
-                if (sr->which == Z_SRW_searchRetrieve_response)
-                    cret = handle_srw_response(c, sr->u.response);
-                else if (sr->which == Z_SRW_scan_response)
-                    handle_srw_scan_response(c, sr->u.scan_response);
-                else
-                    ret = -1;
-            }
-            else if (!ret && (soap_package->which == Z_SOAP_fault
-                              || soap_package->which == Z_SOAP_error))
-            {
-                set_HTTP_error(c, hres->code,
-                               soap_package->u.fault->fault_code,
-                               soap_package->u.fault->fault_string);
-            }
-            else
-                ret = -1;
-        }   
+    {  
+        ret = ZOOM_handle_sru(c, hres, &cret);
         if (ret == 0)
         {
             if (c->no_redirects) /* end of redirect. change hosts again */
-                do_close(c);
+                ZOOM_connection_close(c);
         }
         c->no_redirects = 0;
     }
     if (ret)
     {
         if (hres->code != 200)
-            set_HTTP_error(c, hres->code, 0, 0);
+            ZOOM_set_HTTP_error(c, hres->code, 0, 0);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, addinfo);
+        ZOOM_connection_close(c);
     }
     if (cret == zoom_complete)
     {
@@ -4528,7 +1698,7 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         }
         if (must_close)
         {
-            do_close(c);
+            ZOOM_connection_close(c);
             if (c->tasks)
             {
                 c->tasks->running = 0;
@@ -4550,15 +1720,15 @@ static int do_read(ZOOM_connection c)
     
     r = cs_get(c->cs, &c->buf_in, &c->len_in);
     more = cs_more(c->cs);
-    yaz_log(log_details, "%p do_read len=%d more=%d", c, r, more);
+    yaz_log(c->log_details, "%p do_read len=%d more=%d", c, r, more);
     if (r == 1)
         return 0;
     if (r <= 0)
     {
         if (!ZOOM_test_reconnect(c))
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4581,28 +1751,28 @@ static int do_read(ZOOM_connection c)
                     "ODR code %d:%d element=%s offset=%d",
                     err, x, element ? element : "<unknown>",
                     odr_offset(c->odr_in));
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, msg);
-            if (log_api)
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, msg);
+            if (c->log_api)
             {
                 FILE *ber_file = yaz_log_file();
                 if (ber_file)
                     odr_dumpBER(ber_file, c->buf_in, r);
             }
-            do_close(c);
+            ZOOM_connection_close(c);
         }
         else
         {
             if (c->odr_print)
                 z_GDU(c->odr_print, &gdu, 0, 0);
             if (gdu->which == Z_GDU_Z3950)
-                recv_apdu(c, gdu->u.z3950);
+                ZOOM_handle_Z3950_apdu(c, gdu->u.z3950);
             else if (gdu->which == Z_GDU_HTTP_Response)
             {
 #if YAZ_HAVE_XML2
                 handle_http(c, gdu->u.HTTP_Response);
 #else
-                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+                ZOOM_connection_close(c);
 #endif
             }
         }
@@ -4619,19 +1789,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
     ZOOM_connection_put_event(c, event);
 
-    yaz_log(log_details, "%p do_write_ex len=%d", c, len_out);
+    yaz_log(c->log_details, "%p do_write_ex len=%d", c, len_out);
     if ((r = cs_put(c->cs, buf_out, len_out)) < 0)
     {
-        yaz_log(log_details, "%p do_write_ex write failed", c);
+        yaz_log(c->log_details, "%p do_write_ex write failed", c);
         if (ZOOM_test_reconnect(c))
         {
             return zoom_pending;
         }
         if (c->state == STATE_CONNECTING)
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-        do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+        ZOOM_connection_close(c);
         return zoom_complete;
     }
     else if (r == 1)
@@ -4642,19 +1812,19 @@ static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
         if (c->cs->io_pending & CS_WANT_READ)
             mask += ZOOM_SELECT_READ;
         ZOOM_connection_set_mask(c, mask);
-        yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write incomplete mask=%d",
                 c, c->mask);
     }
     else
     {
         ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
-        yaz_log(log_details, "%p do_write_ex write complete mask=%d",
+        yaz_log(c->log_details, "%p do_write_ex write complete mask=%d",
                 c, c->mask);
     }
     return zoom_pending;
 }
 
-static zoom_ret do_write(ZOOM_connection c)
+zoom_ret ZOOM_send_buf(ZOOM_connection c)
 {
     return do_write_ex(c, c->buf_out, c->len_out);
 }
@@ -4808,20 +1978,20 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
 {
     ZOOM_Event event = 0;
     int r = cs_look(c->cs);
-    yaz_log(log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
+    yaz_log(c->log_details, "%p ZOOM_connection_do_io mask=%d cs_look=%d",
             c, mask, r);
     
     if (r == CS_NONE)
     {
         event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
-        set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     else if (r == CS_CONNECT)
     {
         int ret = ret = cs_rcvconnect(c->cs);
-        yaz_log(log_details, "%p ZOOM_connection_do_io "
+        yaz_log(c->log_details, "%p ZOOM_connection_do_io "
                 "cs_rcvconnect returned %d", c, ret);
         if (ret == 1)
         {
@@ -4840,7 +2010,7 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
             ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
-                ZOOM_connection_send_init(c);
+                ZOOM_connection_Z3950_send_init(c);
             else
             {
                 /* no init request for SRW .. */
@@ -4853,8 +2023,8 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         }
         else
         {
-            set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-            do_close(c);
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECT, c->host_port);
+            ZOOM_connection_close(c);
         }
     }
     else
@@ -4863,15 +2033,15 @@ static void ZOOM_connection_do_io(ZOOM_connection c, int mask)
         {
             if (!ZOOM_test_reconnect(c))
             {
-                set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-                do_close(c);
+                ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+                ZOOM_connection_close(c);
             }
             return;
         }
         if (mask & ZOOM_SELECT_READ)
             do_read(c);
         if (c->cs && (mask & ZOOM_SELECT_WRITE))
-            do_write(c);
+            ZOOM_send_buf(c);
     }
 }
 
@@ -4884,77 +2054,14 @@ ZOOM_API(int)
 }
 
 
-static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
-{
-    WRBUF wrbuf = (WRBUF) client_data;
-    wrbuf_puts(wrbuf, buf);
-}
-
-/*
- * 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;
-    const char *cqlfile;
-    cql_transform_t trans;
-    char *result = 0;
-
-    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;
-    }
-
-    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
-    if (cqlfile == 0) 
-    {
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
-    }
-    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
-    {
-        char buf[512];        
-        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
-                cqlfile, strerror(errno));
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-    }
-    else 
-    {
-        WRBUF wrbuf_result = wrbuf_alloc();
-        error = cql_transform(trans, cql_parser_result(parser),
-                              cql2pqf_wrbuf_puts, wrbuf_result);
-        if (error != 0) {
-            char buf[512];
-            const char *addinfo;
-            error = cql_transform_error(trans, &addinfo);
-            sprintf(buf, "%.200s (addinfo=%.200s)", 
-                    cql_strerror(error), addinfo);
-            set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-        }
-        else
-        {
-            result = xstrdup(wrbuf_cstr(wrbuf_result));
-        }
-        cql_transform_close(trans);
-        wrbuf_destroy(wrbuf_result);
-    }
-    cql_parser_destroy(parser);
-    return result;
-}
-
 ZOOM_API(int) ZOOM_connection_fire_event_timeout(ZOOM_connection c)
 {
     if (c->mask)
     {
         ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_TIMEOUT);
         /* timeout and this connection was waiting */
-        set_ZOOM_error(c, ZOOM_ERROR_TIMEOUT, 0);
-        do_close(c);
+        ZOOM_set_error(c, ZOOM_ERROR_TIMEOUT, 0);
+        ZOOM_connection_close(c);
         ZOOM_connection_put_event(c, event);
     }
     return 0;
@@ -4988,7 +2095,7 @@ ZOOM_API(int)
 {
     int i;
 
-    yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
+    yaz_log(log_details0, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
     
     for (i = 0; i<no; i++)
     {
@@ -5036,7 +2143,11 @@ ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
 
 ZOOM_API(void) ZOOM_connection_close(ZOOM_connection c)
 {
-    do_close(c);
+    if (c->cs)
+        cs_close(c->cs);
+    c->cs = 0;
+    ZOOM_connection_set_mask(c, 0);
+    c->state = STATE_IDLE;
 }
 
 /*
index 51125b5..bb3b132 100644 (file)
@@ -33,7 +33,6 @@
 #include <yaz/comstack.h>
 #include <yaz/wrbuf.h>
 #include <yaz/zoom.h>
-#include <yaz/sortspec.h>
 #include <yaz/srw.h>
 #include <yaz/mutex.h>
 
 
 typedef struct ZOOM_Event_p *ZOOM_Event;
 
-struct ZOOM_query_p {
-    Z_Query *z_query;
-    Z_SortKeySpecList *sort_spec;
-    int refcount;
-    ODR odr;
-    char *query_string;
-};
-
 typedef enum {
     zoom_sru_error,
     zoom_sru_soap,
@@ -116,6 +107,9 @@ struct ZOOM_connection_p {
     ZOOM_Event m_queue_back;
     zoom_sru_mode sru_mode;
     int no_redirects; /* 0 for no redirects. >0 for number of redirects */
+
+    int log_details;
+    int log_api;
 };
 
 #if ZOOM_RESULT_LISTS
@@ -174,23 +168,6 @@ struct ZOOM_resultset_p {
     char **facets_names;
 };
 
-struct ZOOM_record_p {
-    ODR odr;
-#if SHPTR
-    struct WRBUF_shptr *record_wrbuf;
-#else
-    WRBUF wrbuf;
-#endif
-
-    Z_NamePlusRecord *npr;
-    const char *schema;
-
-    const char *diag_uri;
-    const char *diag_message;
-    const char *diag_details;
-    const char *diag_set;
-};
-
 struct facet_term_p {
     char *term;
     int frequency;
@@ -202,16 +179,6 @@ struct ZOOM_facet_field_p {
     struct facet_term_p *facet_terms;
 };
 
-
-struct ZOOM_record_cache_p {
-    struct ZOOM_record_p rec;
-    char *elementSetName;
-    char *syntax;
-    char *schema;
-    int pos;
-    ZOOM_record_cache next;
-};
-
 struct ZOOM_scanset_p {
     int refcount;
     ODR odr;
@@ -277,8 +244,61 @@ struct ZOOM_Event_p {
     ZOOM_Event prev;
 };
 
+typedef enum {
+    zoom_pending,
+    zoom_complete
+} zoom_ret;
+
 void ZOOM_options_addref (ZOOM_options opt);
 
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu);
+
+void ZOOM_set_dset_error(ZOOM_connection c, int error,
+                         const char *dset,
+                         const char *addinfo, const char *addinfo2);
+
+void ZOOM_set_error(ZOOM_connection c, int error, const char *addinfo);
+
+ZOOM_Event ZOOM_Event_create(int kind);
+void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event);
+
+zoom_ret ZOOM_connection_Z3950_send_search(ZOOM_connection c);
+zoom_ret send_Z3950_present(ZOOM_connection c);
+zoom_ret ZOOM_connection_Z3950_send_scan(ZOOM_connection c);
+zoom_ret ZOOM_send_buf(ZOOM_connection c);
+zoom_ret send_Z3950_sort(ZOOM_connection c, ZOOM_resultset resultset);
+char **ZOOM_connection_get_databases(ZOOM_connection con, ZOOM_options options,
+                                     int *num, ODR odr);
+zoom_ret ZOOM_connection_Z3950_send_init(ZOOM_connection c);
+
+ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which);
+void ZOOM_connection_remove_task(ZOOM_connection c);
+int ZOOM_test_reconnect(ZOOM_connection c);
+
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName);
+void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
+                           int pos,
+                           const char *syntax, const char *elementSetName,
+                           const char *schema,
+                           Z_SRW_diagnostic *diag);
+
+Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s);
+Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s);
+char *ZOOM_query_get_query_string(ZOOM_query s);
+
+int ZOOM_uri_to_code(const char *uri);
+
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c);
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c);
+
+int ZOOM_handle_sru(ZOOM_connection c, Z_HTTP_Response *hres,
+                    zoom_ret *cret);
+
+void ZOOM_set_HTTP_error(ZOOM_connection c, int error,
+                         const char *addinfo, const char *addinfo2);
+
 /*
  * Local variables:
  * c-basic-offset: 4
diff --git a/src/zoom-query.c b/src/zoom-query.c
new file mode 100644 (file)
index 0000000..1049873
--- /dev/null
@@ -0,0 +1,262 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-query.c
+ * \brief Implements ZOOM C query interface.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/yaz-util.h>
+#include <yaz/xmalloc.h>
+#include <yaz/log.h>
+#include <yaz/pquery.h>
+#include <yaz/cql.h>
+#include <yaz/ccl.h>
+#include <yaz/sortspec.h>
+
+struct ZOOM_query_p {
+    Z_Query *z_query;
+    Z_SortKeySpecList *sort_spec;
+    int refcount;
+    ODR odr;
+    char *query_string;
+};
+
+Z_Query *ZOOM_query_get_Z_Query(ZOOM_query s)
+{
+    return s->z_query;
+}
+
+
+Z_SortKeySpecList *ZOOM_query_get_sortspec(ZOOM_query s)
+{
+    return s->sort_spec;
+}
+
+static void cql2pqf_wrbuf_puts(const char *buf, void *client_data)
+{
+    WRBUF wrbuf = (WRBUF) client_data;
+    wrbuf_puts(wrbuf, buf);
+}
+
+char *ZOOM_query_get_query_string(ZOOM_query s)
+{
+    return s->query_string;
+}
+
+/*
+ * 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;
+    const char *cqlfile;
+    cql_transform_t trans;
+    char *result = 0;
+
+    parser = cql_parser_create();
+    if ((error = cql_parser_string(parser, cql)) != 0) {
+        cql_parser_destroy(parser);
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_PARSE, cql);
+        return 0;
+    }
+
+    cqlfile = ZOOM_connection_option_get(c, "cqlfile");
+    if (cqlfile == 0) 
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
+    }
+    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
+    {
+        char buf[512];        
+        sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
+                cqlfile, strerror(errno));
+        ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+    }
+    else 
+    {
+        WRBUF wrbuf_result = wrbuf_alloc();
+        error = cql_transform(trans, cql_parser_result(parser),
+                              cql2pqf_wrbuf_puts, wrbuf_result);
+        if (error != 0) {
+            char buf[512];
+            const char *addinfo;
+            error = cql_transform_error(trans, &addinfo);
+            sprintf(buf, "%.200s (addinfo=%.200s)", 
+                    cql_strerror(error), addinfo);
+            ZOOM_set_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+        }
+        else
+        {
+            result = xstrdup(wrbuf_cstr(wrbuf_result));
+        }
+        cql_transform_close(trans);
+        wrbuf_destroy(wrbuf_result);
+    }
+    cql_parser_destroy(parser);
+    return result;
+}
+
+
+ZOOM_API(ZOOM_query)
+    ZOOM_query_create(void)
+{
+    ZOOM_query s = (ZOOM_query) xmalloc(sizeof(*s));
+
+    s->refcount = 1;
+    s->z_query = 0;
+    s->sort_spec = 0;
+    s->odr = odr_createmem(ODR_ENCODE);
+    s->query_string = 0;
+
+    return s;
+}
+
+ZOOM_API(void)
+    ZOOM_query_destroy(ZOOM_query s)
+{
+    if (!s)
+        return;
+
+    (s->refcount)--;
+    if (s->refcount == 0)
+    {
+        odr_destroy(s->odr);
+        xfree(s);
+    }
+}
+
+ZOOM_API(void)
+    ZOOM_query_addref(ZOOM_query s)
+{
+    s->refcount++;
+}
+
+ZOOM_API(int)
+    ZOOM_query_prefix(ZOOM_query s, const char *str)
+{
+    s->query_string = odr_strdup(s->odr, str);
+    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
+    s->z_query->which = Z_Query_type_1;
+    s->z_query->u.type_1 =  p_query_rpn(s->odr, str);
+    if (!s->z_query->u.type_1)
+    {
+        s->z_query = 0;
+        return -1;
+    }
+    return 0;
+}
+
+ZOOM_API(int)
+    ZOOM_query_cql(ZOOM_query s, const char *str)
+{
+    Z_External *ext;
+
+    s->query_string = odr_strdup(s->odr, str);
+
+    ext = (Z_External *) odr_malloc(s->odr, sizeof(*ext));
+    ext->direct_reference = odr_oiddup(s->odr, yaz_oid_userinfo_cql);
+    ext->indirect_reference = 0;
+    ext->descriptor = 0;
+    ext->which = Z_External_CQL;
+    ext->u.cql = s->query_string;
+    
+    s->z_query = (Z_Query *) odr_malloc(s->odr, sizeof(*s->z_query));
+    s->z_query->which = Z_Query_type_104;
+    s->z_query->u.type_104 =  ext;
+
+    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;
+    ZOOM_connection freeme = 0;
+
+    if (conn == 0)
+        conn = freeme = ZOOM_connection_create(0);
+
+    rpn = cql2pqf(conn, str);
+    if (freeme != 0)
+        ZOOM_connection_destroy(freeme);
+    if (rpn == 0)
+        return -1;
+
+    ret = ZOOM_query_prefix(s, rpn);
+    xfree(rpn);
+    return ret;
+}
+
+/*
+ * Analogous in every way to ZOOM_query_cql2rpn(), except that there
+ * is no analogous ZOOM_query_ccl() that just sends uninterpreted CCL
+ * to the server, as the YAZ GFS doesn't know how to handle this.
+ */
+ZOOM_API(int)
+    ZOOM_query_ccl2rpn(ZOOM_query s, const char *str, const char *config,
+                       int *ccl_error, const char **error_string,
+                       int *error_pos)
+{
+    int ret;
+    struct ccl_rpn_node *rpn;
+    CCL_bibset bibset = ccl_qual_mk();
+
+    if (config)
+        ccl_qual_buf(bibset, config);
+
+    rpn = ccl_find_str(bibset, str, ccl_error, error_pos);
+    if (!rpn)
+    {
+        *error_string = ccl_err_msg(*ccl_error);
+        ret = -1;
+    }
+    else
+    {
+        WRBUF wr = wrbuf_alloc();
+        ccl_pquery(wr, rpn);
+        ccl_rpn_delete(rpn);
+        ret = ZOOM_query_prefix(s, wrbuf_cstr(wr));
+        wrbuf_destroy(wr);
+    }
+    ccl_qual_rm(&bibset);
+    return ret;
+}
+
+ZOOM_API(int)
+    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
+{
+    s->sort_spec = yaz_sort_spec(s->odr, criteria);
+    if (!s->sort_spec)
+        return -1;
+    return 0;
+}
+
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-record-cache.c b/src/zoom-record-cache.c
new file mode 100644 (file)
index 0000000..9bc4e15
--- /dev/null
@@ -0,0 +1,314 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-record-cache.c
+ * \brief Implements ZOOM record caching
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/diagbib1.h>
+#include <yaz/record_render.h>
+#include <yaz/shptr.h>
+
+#if SHPTR
+YAZ_SHPTR_TYPE(WRBUF)
+#endif
+
+struct ZOOM_record_p {
+    ODR odr;
+#if SHPTR
+    struct WRBUF_shptr *record_wrbuf;
+#else
+    WRBUF wrbuf;
+#endif
+
+    Z_NamePlusRecord *npr;
+    const char *schema;
+
+    const char *diag_uri;
+    const char *diag_message;
+    const char *diag_details;
+    const char *diag_set;
+};
+
+struct ZOOM_record_cache_p {
+    struct ZOOM_record_p rec;
+    char *elementSetName;
+    char *syntax;
+    char *schema;
+    int pos;
+    ZOOM_record_cache next;
+};
+
+
+static int strcmp_null(const char *v1, const char *v2)
+{
+    if (!v1 && !v2)
+        return 0;
+    if (!v1 || !v2)
+        return -1;
+    return strcmp(v1, v2);
+}
+
+static size_t record_hash(int pos)
+{
+    if (pos < 0)
+        pos = 0;
+    return pos % RECORD_HASH_SIZE;
+}
+
+void ZOOM_record_cache_add(ZOOM_resultset r, Z_NamePlusRecord *npr, 
+                           int pos,
+                           const char *syntax, const char *elementSetName,
+                           const char *schema,
+                           Z_SRW_diagnostic *diag)
+{
+    ZOOM_record_cache rc = 0;
+    
+    ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+    ZOOM_connection_put_event(r->connection, event);
+
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
+    {
+        if (pos == rc->pos 
+            && strcmp_null(r->schema, rc->schema) == 0
+            && strcmp_null(elementSetName,rc->elementSetName) == 0
+            && strcmp_null(syntax, rc->syntax) == 0)
+            break;
+    }
+    if (!rc)
+    {
+        rc = (ZOOM_record_cache) odr_malloc(r->odr, sizeof(*rc));
+        rc->rec.odr = 0;
+#if SHPTR
+        YAZ_SHPTR_INC(r->record_wrbuf);
+        rc->rec.record_wrbuf = r->record_wrbuf;
+#else
+        rc->rec.wrbuf = 0;
+#endif
+        rc->elementSetName = odr_strdup_null(r->odr, elementSetName);
+        
+        rc->syntax = odr_strdup_null(r->odr, syntax);
+        
+        rc->schema = odr_strdup_null(r->odr, r->schema);
+
+        rc->pos = pos;
+        rc->next = r->record_hash[record_hash(pos)];
+        r->record_hash[record_hash(pos)] = rc;
+    }
+    rc->rec.npr = npr;
+    rc->rec.schema = odr_strdup_null(r->odr, schema);
+    rc->rec.diag_set = 0;
+    rc->rec.diag_uri = 0;
+    rc->rec.diag_message = 0;
+    rc->rec.diag_details = 0;
+    if (diag)
+    {
+        if (diag->uri)
+        {
+            char *cp;
+            rc->rec.diag_set = odr_strdup(r->odr, diag->uri);
+            if ((cp = strrchr(rc->rec.diag_set, '/')))
+                *cp = '\0';
+            rc->rec.diag_uri = odr_strdup(r->odr, diag->uri);
+        }
+        rc->rec.diag_message = odr_strdup_null(r->odr, diag->message);            
+        rc->rec.diag_details = odr_strdup_null(r->odr, diag->details);
+    }
+}
+
+ZOOM_record ZOOM_record_cache_lookup(ZOOM_resultset r, int pos,
+                                     const char *syntax,
+                                     const char *elementSetName)
+{
+    ZOOM_record_cache rc;
+    
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
+    {
+        if (pos == rc->pos)
+        {
+            if (strcmp_null(r->schema, rc->schema))
+                continue;
+            if (strcmp_null(elementSetName,rc->elementSetName))
+                continue;
+            if (strcmp_null(syntax, rc->syntax))
+                continue;
+            return &rc->rec;
+        }
+    }
+    return 0;
+}
+
+ZOOM_API(ZOOM_record)
+    ZOOM_record_clone(ZOOM_record srec)
+{
+    char *buf;
+    int size;
+    ODR odr_enc;
+    ZOOM_record nrec;
+
+    odr_enc = odr_createmem(ODR_ENCODE);
+    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
+        return 0;
+    buf = odr_getbuf(odr_enc, &size, 0);
+    
+    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
+    nrec->odr = odr_createmem(ODR_DECODE);
+#if SHPTR
+    nrec->record_wrbuf = 0;
+#else
+    nrec->wrbuf = 0;
+#endif
+    odr_setbuf(nrec->odr, buf, size, 0);
+    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
+    
+    nrec->schema = odr_strdup_null(nrec->odr, srec->schema);
+    nrec->diag_uri = odr_strdup_null(nrec->odr, srec->diag_uri);
+    nrec->diag_message = odr_strdup_null(nrec->odr, srec->diag_message);
+    nrec->diag_details = odr_strdup_null(nrec->odr, srec->diag_details);
+    nrec->diag_set = odr_strdup_null(nrec->odr, srec->diag_set);
+    odr_destroy(odr_enc);
+    return nrec;
+}
+
+static void ZOOM_record_release(ZOOM_record rec)
+{
+    if (!rec)
+        return;
+
+#if SHPTR
+    if (rec->record_wrbuf)
+        YAZ_SHPTR_DEC(rec->record_wrbuf, wrbuf_destroy);
+#else
+    if (rec->wrbuf)
+        wrbuf_destroy(rec->wrbuf);
+#endif
+
+    if (rec->odr)
+        odr_destroy(rec->odr);
+}
+
+ZOOM_API(void)
+    ZOOM_resultset_cache_reset(ZOOM_resultset r)
+{
+    int i;
+    for (i = 0; i<RECORD_HASH_SIZE; i++)
+    {
+        ZOOM_record_cache rc;
+        for (rc = r->record_hash[i]; rc; rc = rc->next)
+        {
+            ZOOM_record_release(&rc->rec);
+        }
+        r->record_hash[i] = 0;
+    }
+}
+
+
+ZOOM_API(const char *)
+    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
+{
+    WRBUF wrbuf;
+    
+    if (len)
+        *len = 0; /* default return */
+        
+    if (!rec || !rec->npr)
+        return 0;
+
+#if SHPTR
+    if (!rec->record_wrbuf)
+    {
+        WRBUF w = wrbuf_alloc();
+        YAZ_SHPTR_INIT(rec->record_wrbuf, w);
+    }
+    wrbuf = rec->record_wrbuf->ptr;
+#else
+    if (!rec->wrbuf)
+        rec->wrbuf = wrbuf_alloc();
+    wrbuf = rec->wrbuf;
+#endif
+    return yaz_record_render(rec->npr, rec->schema, wrbuf, type_spec, len);
+}
+
+ZOOM_API(int)
+    ZOOM_record_error(ZOOM_record rec, const char **cp,
+                      const char **addinfo, const char **diagset)
+{
+    Z_NamePlusRecord *npr;
+    
+    if (!rec)
+        return 0;
+
+    npr = rec->npr;
+    if (rec->diag_uri)
+    {
+        if (cp)
+            *cp = rec->diag_message;
+        if (addinfo)
+            *addinfo = rec->diag_details;
+        if (diagset)
+            *diagset = rec->diag_set;
+        return ZOOM_uri_to_code(rec->diag_uri);
+    }
+    if (npr && npr->which == Z_NamePlusRecord_surrogateDiagnostic)
+    {
+        Z_DiagRec *diag_rec = npr->u.surrogateDiagnostic;
+        int error = YAZ_BIB1_UNSPECIFIED_ERROR;
+        const char *add = 0;
+
+        if (diag_rec->which == Z_DiagRec_defaultFormat)
+        {
+            Z_DefaultDiagFormat *ddf = diag_rec->u.defaultFormat;
+            oid_class oclass;
+    
+            error = *ddf->condition;
+            switch (ddf->which)
+            {
+            case Z_DefaultDiagFormat_v2Addinfo:
+                add = ddf->u.v2Addinfo;
+                break;
+            case Z_DefaultDiagFormat_v3Addinfo:
+                add = ddf->u.v3Addinfo;
+                break;
+            }
+            if (diagset)
+                *diagset =
+                    yaz_oid_to_string(yaz_oid_std(),
+                                      ddf->diagnosticSetId, &oclass);
+        }
+        else
+        {
+            if (diagset)
+                *diagset = "Bib-1";
+        }
+        if (addinfo)
+            *addinfo = add ? add : "";
+        if (cp)
+            *cp = diagbib1_str(error);
+        return error;
+    }
+    return 0;
+}
+
+ZOOM_API(void)
+    ZOOM_record_destroy(ZOOM_record rec)
+{
+    ZOOM_record_release(rec);
+    xfree(rec);
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-sru.c b/src/zoom-sru.c
new file mode 100644 (file)
index 0000000..1932f09
--- /dev/null
@@ -0,0 +1,429 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-sru.c
+ * \brief Implements ZOOM SRU
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/log.h>
+
+#if YAZ_HAVE_XML2
+static void set_SRU_error(ZOOM_connection c, Z_SRW_diagnostic *d)
+{
+    const char *uri = d->uri;
+    if (uri)
+        ZOOM_set_dset_error(c, ZOOM_uri_to_code(uri), uri, d->details, 0);
+}
+#endif
+
+
+
+#if YAZ_HAVE_XML2
+static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
+{
+    Z_GDU *gdu;
+    ZOOM_Event event;
+    const char *database =  ZOOM_options_get(c->options, "databaseName");
+    char *fdatabase = 0;
+    
+    if (database)
+        fdatabase = yaz_encode_sru_dbpath_odr(c->odr_out, database);
+    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port,
+                                       fdatabase ? fdatabase : c->path);
+
+    if (c->sru_mode == zoom_sru_get)
+    {
+        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    else if (c->sru_mode == zoom_sru_post)
+    {
+        yaz_sru_post_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    else if (c->sru_mode == zoom_sru_soap)
+    {
+        yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
+    }
+    if (!z_GDU(c->odr_out, &gdu, 0, 0))
+        return zoom_complete;
+    if (c->odr_print)
+        z_GDU(c->odr_print, &gdu, 0, 0);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+        
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
+    odr_reset(c->odr_out);
+    return ZOOM_send_buf(c);
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static Z_SRW_PDU *ZOOM_srw_get_pdu(ZOOM_connection c, int type)
+{
+    Z_SRW_PDU *sr = yaz_srw_get_pdu(c->odr_out, type, c->sru_version);
+    sr->username = c->user;
+    sr->password = c->password;
+    return sr;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    ZOOM_scanset scan;
+    Z_SRW_PDU *sr = 0;
+    const char *option_val = 0;
+    Z_Query *z_query;
+
+    if (!c->tasks)
+        return zoom_complete;
+    assert (c->tasks->which == ZOOM_TASK_SCAN);
+    scan = c->tasks->u.scan.scan;
+        
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_scan_request);
+
+    z_query = ZOOM_query_get_Z_Query(scan->query);
+    /* SRU scan can only carry CQL and PQF */
+    if (z_query->which == Z_Query_type_104)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_cql;
+        sr->u.scan_request->scanClause.cql =
+            ZOOM_query_get_query_string(scan->query);
+    }
+    else if (z_query->which == Z_Query_type_1
+             || z_query->which == Z_Query_type_101)
+    {
+        sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
+        sr->u.scan_request->scanClause.pqf =
+            ZOOM_query_get_query_string(scan->query);
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+
+    sr->u.scan_request->maximumTerms = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "number", 10));
+    
+    sr->u.scan_request->responsePosition = odr_intdup(
+        c->odr_out, ZOOM_options_get_int(scan->options, "position", 1));
+    
+    option_val = ZOOM_options_get(scan->options, "extraArgs");
+    yaz_encode_sru_extra(sr, c->odr_out, option_val);
+    return send_srw(c, sr);
+}
+#else
+zoom_ret ZOOM_connection_srw_send_scan(ZOOM_connection c)
+{
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
+{
+    int i;
+    int *start, *count;
+    ZOOM_resultset resultset = 0;
+    Z_SRW_PDU *sr = 0;
+    const char *option_val = 0;
+    Z_Query *z_query;
+
+    if (c->error)                  /* don't continue on error */
+        return zoom_complete;
+    assert(c->tasks);
+    switch(c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        if (!resultset->setname)
+            resultset->setname = xstrdup("default");
+        ZOOM_options_set(resultset->options, "setname", resultset->setname);
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+
+        if (*start >= resultset->size)
+            return zoom_complete;
+        if (*start + *count > resultset->size)
+            *count = resultset->size - *start;
+
+        for (i = 0; i < *count; i++)
+        {
+            ZOOM_record rec =
+                ZOOM_record_cache_lookup(resultset, i + *start,
+                                         c->tasks->u.retrieve.syntax,
+                                         c->tasks->u.retrieve.elementSetName);
+            if (!rec)
+                break;
+            else
+            {
+                ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+                ZOOM_connection_put_event(c, event);
+            }
+        }
+        *start += i;
+        *count -= i;
+
+        if (*count == 0)
+            return zoom_complete;
+        break;
+    default:
+        return zoom_complete;
+    }
+    assert(resultset->query);
+        
+    sr = ZOOM_srw_get_pdu(c, Z_SRW_searchRetrieve_request);
+    z_query = ZOOM_query_get_Z_Query(resultset->query);
+
+    if (z_query->which == Z_Query_type_104
+        && z_query->u.type_104->which == Z_External_CQL)
+    {
+        sr->u.request->query_type = Z_SRW_query_type_cql;
+        sr->u.request->query.cql = z_query->u.type_104->u.cql;
+    }
+    else if (z_query->which == Z_Query_type_1 && z_query->u.type_1)
+    {
+        sr->u.request->query_type = Z_SRW_query_type_pqf;
+        sr->u.request->query.pqf =
+            ZOOM_query_get_query_string(resultset->query);
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+    sr->u.request->startRecord = odr_intdup(c->odr_out, *start + 1);
+    sr->u.request->maximumRecords = odr_intdup(
+        c->odr_out, (resultset->step > 0 && resultset->step < *count) ? 
+        resultset->step : *count);
+    sr->u.request->recordSchema = resultset->schema;
+    
+    option_val = ZOOM_resultset_option_get(resultset, "recordPacking");
+    if (option_val)
+        sr->u.request->recordPacking = odr_strdup(c->odr_out, option_val);
+
+    option_val = ZOOM_resultset_option_get(resultset, "extraArgs");
+    yaz_encode_sru_extra(sr, c->odr_out, option_val);
+    return send_srw(c, sr);
+}
+#else
+zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
+{
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static zoom_ret handle_srw_response(ZOOM_connection c,
+                                    Z_SRW_searchRetrieveResponse *res)
+{
+    ZOOM_resultset resultset = 0;
+    int i;
+    NMEM nmem;
+    ZOOM_Event event;
+    int *start, *count;
+    const char *syntax, *elementSetName;
+
+    if (!c->tasks)
+        return zoom_complete;
+
+    switch(c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;        
+
+        if (!c->tasks->u.search.recv_search_fired)
+        {
+            event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+            ZOOM_connection_put_event(c, event);
+            c->tasks->u.search.recv_search_fired = 1;
+        }
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return zoom_complete;
+    }
+
+    resultset->size = 0;
+
+    if (res->resultSetId)
+        ZOOM_resultset_option_set(resultset, "resultSetId", res->resultSetId);
+
+    yaz_log(c->log_details, "%p handle_srw_response got SRW response OK", c);
+
+    if (res->num_diagnostics > 0)
+    {
+        set_SRU_error(c, &res->diagnostics[0]);
+    }
+    else
+    {
+        if (res->numberOfRecords)
+            resultset->size = *res->numberOfRecords;
+        for (i = 0; i<res->num_records; i++)
+        {
+            int pos;
+            Z_SRW_record *sru_rec;
+            Z_SRW_diagnostic *diag = 0;
+            int num_diag;
+            
+            Z_NamePlusRecord *npr = (Z_NamePlusRecord *)
+                odr_malloc(c->odr_in, sizeof(Z_NamePlusRecord));
+            
+            if (res->records[i].recordPosition && 
+                *res->records[i].recordPosition > 0)
+                pos = *res->records[i].recordPosition - 1;
+            else
+                pos = *start + i;
+            
+            sru_rec = &res->records[i];
+            
+            npr->databaseName = 0;
+            npr->which = Z_NamePlusRecord_databaseRecord;
+            npr->u.databaseRecord = (Z_External *)
+                odr_malloc(c->odr_in, sizeof(Z_External));
+            npr->u.databaseRecord->descriptor = 0;
+            npr->u.databaseRecord->direct_reference =
+                odr_oiddup(c->odr_in, yaz_oid_recsyn_xml);
+            npr->u.databaseRecord->which = Z_External_octet;
+            
+            npr->u.databaseRecord->u.octet_aligned = (Odr_oct *)
+                odr_malloc(c->odr_in, sizeof(Odr_oct));
+            npr->u.databaseRecord->u.octet_aligned->buf = (unsigned char*)
+                sru_rec->recordData_buf;
+            npr->u.databaseRecord->u.octet_aligned->len = 
+                npr->u.databaseRecord->u.octet_aligned->size = 
+                sru_rec->recordData_len;
+            
+            if (sru_rec->recordSchema 
+                && !strcmp(sru_rec->recordSchema,
+                           "info:srw/schema/1/diagnostics-v1.1"))
+            {
+                sru_decode_surrogate_diagnostics(sru_rec->recordData_buf,
+                                                 sru_rec->recordData_len,
+                                                 &diag, &num_diag,
+                                                 resultset->odr);
+            }
+            ZOOM_record_cache_add(resultset, npr, pos, syntax, elementSetName,
+                                  sru_rec->recordSchema, diag);
+        }
+        *count -= i;
+        *start += i;
+        if (*count + *start > resultset->size)
+            *count = resultset->size - *start;
+        if (*count < 0)
+            *count = 0;
+        
+        nmem = odr_extract_mem(c->odr_in);
+        nmem_transfer(odr_getmem(resultset->odr), nmem);
+        nmem_destroy(nmem);
+
+        if (*count > 0)
+            return ZOOM_connection_srw_send_search(c);
+    }
+    return zoom_complete;
+}
+#endif
+
+#if YAZ_HAVE_XML2
+static void handle_srw_scan_response(ZOOM_connection c,
+                                     Z_SRW_scanResponse *res)
+{
+    NMEM nmem = odr_extract_mem(c->odr_in);
+    ZOOM_scanset scan;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
+        return;
+    scan = c->tasks->u.scan.scan;
+
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
+
+    scan->scan_response = 0;
+    scan->srw_scan_response = res;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
+
+    ZOOM_options_set_int(scan->options, "number", res->num_terms);
+    nmem_destroy(nmem);
+}
+#endif
+
+int ZOOM_handle_sru(ZOOM_connection c, Z_HTTP_Response *hres,
+                    zoom_ret *cret)
+{
+    int ret = 0;
+    const char *addinfo = 0;
+
+    /* not redirect (normal response) */
+    if (!yaz_srw_check_content_type(hres))
+        addinfo = "content-type";
+    else
+    {
+        Z_SOAP *soap_package = 0;
+        ODR o = c->odr_in;
+        Z_SOAP_Handler soap_handlers[2] = {
+            {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
+            {0, 0, 0}
+        };
+        ret = z_soap_codec(o, &soap_package,
+                           &hres->content_buf, &hres->content_len,
+                           soap_handlers);
+        if (!ret && soap_package->which == Z_SOAP_generic &&
+            soap_package->u.generic->no == 0)
+        {
+            Z_SRW_PDU *sr = (Z_SRW_PDU*) soap_package->u.generic->p;
+            
+            ZOOM_options_set(c->options, "sru_version", sr->srw_version);
+            ZOOM_options_setl(c->options, "sru_extra_response_data",
+                              sr->extraResponseData_buf, sr->extraResponseData_len);
+            if (sr->which == Z_SRW_searchRetrieve_response)
+                *cret = handle_srw_response(c, sr->u.response);
+            else if (sr->which == Z_SRW_scan_response)
+                handle_srw_scan_response(c, sr->u.scan_response);
+            else
+                ret = -1;
+        }
+        else if (!ret && (soap_package->which == Z_SOAP_fault
+                          || soap_package->which == Z_SOAP_error))
+        {
+            ZOOM_set_HTTP_error(c, hres->code,
+                                soap_package->u.fault->fault_code,
+                                soap_package->u.fault->fault_string);
+        }
+        else
+            ret = -1;
+    }   
+    return ret;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
diff --git a/src/zoom-z3950.c b/src/zoom-z3950.c
new file mode 100644 (file)
index 0000000..bbd2e2a
--- /dev/null
@@ -0,0 +1,1776 @@
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2010 Index Data
+ * See the file LICENSE for details.
+ */
+/**
+ * \file zoom-z3950.c
+ * \brief Implements ZOOM Z39.50 handling
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include "zoom-p.h"
+
+#include <yaz/yaz-util.h>
+#include <yaz/xmalloc.h>
+#include <yaz/otherinfo.h>
+#include <yaz/log.h>
+#include <yaz/pquery.h>
+#include <yaz/marcdisp.h>
+#include <yaz/diagbib1.h>
+#include <yaz/charneg.h>
+#include <yaz/ill.h>
+#include <yaz/query-charset.h>
+#include <yaz/copy_types.h>
+#include <yaz/snprintf.h>
+#include <yaz/facet.h>
+
+#include <yaz/shptr.h>
+
+/*
+ * This wrapper is just for logging failed lookups.  It would be nicer
+ * if it could cause failure when a lookup fails, but that's hard.
+ */
+static Odr_oid *zoom_yaz_str_to_z3950oid(ZOOM_connection c,
+                                     oid_class oid_class, const char *str) {
+    Odr_oid *res = yaz_string_to_oid_odr(yaz_oid_std(), oid_class, str,
+                                     c->odr_out);
+    if (res == 0)
+        yaz_log(YLOG_WARN, "%p OID lookup (%d, '%s') failed",
+                c, (int) oid_class, str);
+    return res;
+}
+
+static Z_APDU *create_es_package(ZOOM_package p, const Odr_oid *oid)
+{
+    const char *str;
+    Z_APDU *apdu = zget_APDU(p->odr_out, Z_APDU_extendedServicesRequest);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    
+    str = ZOOM_options_get(p->options, "package-name");
+    if (str && *str)
+        req->packageName = odr_strdup(p->odr_out, str);
+    
+    str = ZOOM_options_get(p->options, "user-id");
+    if (str)
+        req->userId = odr_strdup_null(p->odr_out, str);
+    
+    req->packageType = odr_oiddup(p->odr_out, oid);
+
+    str = ZOOM_options_get(p->options, "function");
+    if (str)
+    {
+        if (!strcmp (str, "create"))
+            *req->function = Z_ExtendedServicesRequest_create;
+        if (!strcmp (str, "delete"))
+            *req->function = Z_ExtendedServicesRequest_delete;
+        if (!strcmp (str, "modify"))
+            *req->function = Z_ExtendedServicesRequest_modify;
+    }
+
+    str = ZOOM_options_get(p->options, "waitAction");
+    if (str)
+    {
+        if (!strcmp (str, "wait"))
+            *req->waitAction = Z_ExtendedServicesRequest_wait;
+        if (!strcmp (str, "waitIfPossible"))
+            *req->waitAction = Z_ExtendedServicesRequest_waitIfPossible;
+        if (!strcmp (str, "dontWait"))
+            *req->waitAction = Z_ExtendedServicesRequest_dontWait;
+        if (!strcmp (str, "dontReturnPackage"))
+            *req->waitAction = Z_ExtendedServicesRequest_dontReturnPackage;
+    }
+    return apdu;
+}
+
+static const char *ill_array_lookup(void *clientData, const char *idx)
+{
+    ZOOM_package p = (ZOOM_package) clientData;
+    return ZOOM_options_get(p->options, idx+4);
+}
+
+static Z_External *encode_ill_request(ZOOM_package p)
+{
+    ODR out = p->odr_out;
+    ILL_Request *req;
+    Z_External *r = 0;
+    struct ill_get_ctl ctl;
+        
+    ctl.odr = p->odr_out;
+    ctl.clientData = p;
+    ctl.f = ill_array_lookup;
+        
+    req = ill_get_ILLRequest(&ctl, "ill", 0);
+        
+    if (!ill_Request(out, &req, 0, 0))
+    {
+        int ill_request_size;
+        char *ill_request_buf = odr_getbuf(out, &ill_request_size, 0);
+        if (ill_request_buf)
+            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
+        return 0;
+    }
+    else
+    {
+        int illRequest_size = 0;
+        char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
+                
+        r = (Z_External *) odr_malloc(out, sizeof(*r));
+        r->direct_reference = odr_oiddup(out, yaz_oid_general_isoill_1);
+        r->indirect_reference = 0;
+        r->descriptor = 0;
+        r->which = Z_External_single;
+                
+        r->u.single_ASN1_type =
+            odr_create_Odr_oct(out,
+                               (unsigned char *)illRequest_buf,
+                               illRequest_size);
+    }
+    return r;
+}
+
+static Z_ItemOrder *encode_item_order(ZOOM_package p)
+{
+    Z_ItemOrder *req = (Z_ItemOrder *) odr_malloc(p->odr_out, sizeof(*req));
+    const char *str;
+    int len;
+    
+    req->which = Z_IOItemOrder_esRequest;
+    req->u.esRequest = (Z_IORequest *) 
+        odr_malloc(p->odr_out,sizeof(Z_IORequest));
+
+    /* to keep part ... */
+    req->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
+        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartToKeep));
+    req->u.esRequest->toKeep->supplDescription = 0;
+    req->u.esRequest->toKeep->contact = (Z_IOContact *)
+        odr_malloc(p->odr_out, sizeof(*req->u.esRequest->toKeep->contact));
+        
+    str = ZOOM_options_get(p->options, "contact-name");
+    req->u.esRequest->toKeep->contact->name =
+        odr_strdup_null(p->odr_out, str);
+        
+    str = ZOOM_options_get(p->options, "contact-phone");
+    req->u.esRequest->toKeep->contact->phone =
+        odr_strdup_null(p->odr_out, str);
+        
+    str = ZOOM_options_get(p->options, "contact-email");
+    req->u.esRequest->toKeep->contact->email =
+        odr_strdup_null(p->odr_out, str);
+        
+    req->u.esRequest->toKeep->addlBilling = 0;
+        
+    /* not to keep part ... */
+    req->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
+        odr_malloc(p->odr_out,sizeof(Z_IOOriginPartNotToKeep));
+        
+    str = ZOOM_options_get(p->options, "itemorder-setname");
+    if (!str)
+        str = "default";
+
+    if (!*str) 
+        req->u.esRequest->notToKeep->resultSetItem = 0;
+    else
+    {
+        req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
+            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
+
+        req->u.esRequest->notToKeep->resultSetItem->resultSetId =
+            odr_strdup(p->odr_out, str);
+        req->u.esRequest->notToKeep->resultSetItem->item =
+            odr_intdup(p->odr_out, 0);
+        
+        str = ZOOM_options_get(p->options, "itemorder-item");
+        *req->u.esRequest->notToKeep->resultSetItem->item =
+            (str ? atoi(str) : 1);
+    }
+
+    str = ZOOM_options_getl(p->options, "doc", &len);
+    if (str)
+    {
+        req->u.esRequest->notToKeep->itemRequest =
+            z_ext_record_xml(p->odr_out, str, len);
+    }
+    else
+        req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
+    
+    return req;
+}
+
+Z_APDU *create_admin_package(ZOOM_package p, int type, 
+                             Z_ESAdminOriginPartToKeep **toKeepP,
+                             Z_ESAdminOriginPartNotToKeep **notToKeepP)
+{
+    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_admin);
+    if (apdu)
+    {
+        Z_ESAdminOriginPartToKeep  *toKeep;
+        Z_ESAdminOriginPartNotToKeep  *notToKeep;
+        Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
+        const char *first_db = "Default";
+        int num_db;
+        char **db = ZOOM_connection_get_databases(p->connection,
+                                                  p->options, &num_db,
+                                                  p->odr_out);
+        if (num_db > 0)
+            first_db = db[0];
+            
+        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
+        r->descriptor = 0;
+        r->indirect_reference = 0;
+        r->which = Z_External_ESAdmin;
+        
+        r->u.adminService = (Z_Admin *)
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService));
+        r->u.adminService->which = Z_Admin_esRequest;
+        r->u.adminService->u.esRequest = (Z_AdminEsRequest *)
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest));
+        
+        toKeep = r->u.adminService->u.esRequest->toKeep =
+            (Z_ESAdminOriginPartToKeep *) 
+            odr_malloc(p->odr_out, sizeof(*r->u.adminService->u.esRequest->toKeep));
+        toKeep->which = type;
+        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
+        toKeep->u.create = odr_nullval();
+        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        
+        r->u.adminService->u.esRequest->notToKeep = notToKeep =
+            (Z_ESAdminOriginPartNotToKeep *)
+            odr_malloc(p->odr_out,
+                       sizeof(*r->u.adminService->u.esRequest->notToKeep));
+        notToKeep->which = Z_ESAdminOriginPartNotToKeep_recordsWillFollow;
+        notToKeep->u.recordsWillFollow = odr_nullval();
+        if (toKeepP)
+            *toKeepP = toKeep;
+        if (notToKeepP)
+            *notToKeepP = notToKeep;
+    }
+    return apdu;
+}
+
+static Z_APDU *create_xmlupdate_package(ZOOM_package p)
+{
+    Z_APDU *apdu = create_es_package(p, yaz_oid_extserv_xml_es);
+    Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
+    Z_External *ext = (Z_External *) odr_malloc(p->odr_out, sizeof(*ext));
+    int len;
+    const char *doc = ZOOM_options_getl(p->options, "doc", &len);
+
+    if (!doc)
+    {
+        doc = "";
+        len = 0;
+    }
+
+    req->taskSpecificParameters = ext;
+    ext->direct_reference = req->packageType;
+    ext->descriptor = 0;
+    ext->indirect_reference = 0;
+    
+    ext->which = Z_External_octet;
+    ext->u.single_ASN1_type =
+        odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
+    return apdu;
+}
+
+static Z_APDU *create_update_package(ZOOM_package p)
+{
+    Z_APDU *apdu = 0;
+    const char *first_db = "Default";
+    int num_db;
+    char **db = ZOOM_connection_get_databases(p->connection, p->options,
+                                              &num_db, p->odr_out);
+    const char *action = ZOOM_options_get(p->options, "action");
+    int recordIdOpaque_len;
+    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
+        &recordIdOpaque_len);
+    const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
+    int record_len;
+    const char *record_buf = ZOOM_options_getl(p->options, "record",
+        &record_len);
+    int recordOpaque_len;
+    const char *recordOpaque_buf = ZOOM_options_getl(p->options, "recordOpaque",
+        &recordOpaque_len);
+    const char *syntax_str = ZOOM_options_get(p->options, "syntax");
+    const char *version = ZOOM_options_get(p->options, "updateVersion");
+
+    const char *correlationInfo_note =
+        ZOOM_options_get(p->options, "correlationInfo.note");
+    const char *correlationInfo_id =
+        ZOOM_options_get(p->options, "correlationInfo.id");
+    int action_no = -1;
+    Odr_oid *syntax_oid = 0;
+    const Odr_oid *package_oid = yaz_oid_extserv_database_update;
+
+    if (!version)
+        version = "3";
+    if (!syntax_str)
+        syntax_str = "xml";
+    if (!record_buf && !recordOpaque_buf)
+    {
+        record_buf = "void";
+        record_len = 4;
+        syntax_str = "SUTRS";
+    }
+
+    if (syntax_str)
+    {
+        syntax_oid = yaz_string_to_oid_odr(yaz_oid_std(),
+                                           CLASS_RECSYN, syntax_str,
+                                           p->odr_out);
+    }
+    if (!syntax_oid)
+        return 0;
+
+    if (num_db > 0)
+        first_db = db[0];
+    
+    switch(*version)
+    {
+    case '1':
+        package_oid = yaz_oid_extserv_database_update_first_version;
+        /* old update does not support specialUpdate */
+        if (!action)
+            action = "recordInsert";
+        break;
+    case '2':
+        if (!action)
+            action = "specialUpdate";
+        package_oid = yaz_oid_extserv_database_update_second_version;
+        break;
+    case '3':
+        if (!action)
+            action = "specialUpdate";
+        package_oid = yaz_oid_extserv_database_update;
+        break;
+    default:
+        return 0;
+    }
+    
+    if (!strcmp(action, "recordInsert"))
+        action_no = Z_IUOriginPartToKeep_recordInsert;
+    else if (!strcmp(action, "recordReplace"))
+        action_no = Z_IUOriginPartToKeep_recordReplace;
+    else if (!strcmp(action, "recordDelete"))
+        action_no = Z_IUOriginPartToKeep_recordDelete;
+    else if (!strcmp(action, "elementUpdate"))
+        action_no = Z_IUOriginPartToKeep_elementUpdate;
+    else if (!strcmp(action, "specialUpdate"))
+        action_no = Z_IUOriginPartToKeep_specialUpdate;
+    else
+        return 0;
+
+    apdu = create_es_package(p, package_oid);
+    if (apdu)
+    {
+        Z_IUOriginPartToKeep *toKeep;
+        Z_IUSuppliedRecords *notToKeep;
+        Z_External *r = (Z_External *)
+            odr_malloc(p->odr_out, sizeof(*r));
+        const char *elementSetName =
+            ZOOM_options_get(p->options, "elementSetName");
+        
+        apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        
+        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
+        r->descriptor = 0;
+        r->which = Z_External_update;
+        r->indirect_reference = 0;
+        r->u.update = (Z_IUUpdate *)
+            odr_malloc(p->odr_out, sizeof(*r->u.update));
+        
+        r->u.update->which = Z_IUUpdate_esRequest;
+        r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
+            odr_malloc(p->odr_out, sizeof(*r->u.update->u.esRequest));
+        toKeep = r->u.update->u.esRequest->toKeep = 
+            (Z_IUOriginPartToKeep *)
+            odr_malloc(p->odr_out, sizeof(*toKeep));
+        
+        toKeep->databaseName = odr_strdup(p->odr_out, first_db);
+        toKeep->schema = 0;
+        
+        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
+            
+        toKeep->actionQualifier = 0;
+        toKeep->action = odr_intdup(p->odr_out, action_no);
+        
+        notToKeep = r->u.update->u.esRequest->notToKeep = 
+            (Z_IUSuppliedRecords *)
+            odr_malloc(p->odr_out, sizeof(*notToKeep));
+        notToKeep->num = 1;
+        notToKeep->elements = (Z_IUSuppliedRecords_elem **)
+            odr_malloc(p->odr_out, sizeof(*notToKeep->elements));
+        notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
+            odr_malloc(p->odr_out, sizeof(**notToKeep->elements));
+        notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
+        if (recordIdOpaque)
+        {
+            notToKeep->elements[0]->u.opaque = 
+                odr_create_Odr_oct(p->odr_out,
+                                   (const unsigned char *) recordIdOpaque,
+                                   recordIdOpaque_len);
+        }
+        else if (recordIdNumber)
+        {
+            notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_number;
+            
+            notToKeep->elements[0]->u.number =
+                odr_intdup(p->odr_out, atoi(recordIdNumber));
+        }
+        else
+            notToKeep->elements[0]->u.opaque = 0;
+        notToKeep->elements[0]->supplementalId = 0;
+        if (correlationInfo_note || correlationInfo_id)
+        {
+            Z_IUCorrelationInfo *ci;
+            ci = notToKeep->elements[0]->correlationInfo =
+                (Z_IUCorrelationInfo *) odr_malloc(p->odr_out, sizeof(*ci));
+            ci->note = odr_strdup_null(p->odr_out, correlationInfo_note);
+            ci->id = correlationInfo_id ?
+                odr_intdup(p->odr_out, atoi(correlationInfo_id)) : 0;
+        }
+        else
+            notToKeep->elements[0]->correlationInfo = 0;
+        if (recordOpaque_buf)
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid_any(p->odr_out, syntax_oid,
+                                 recordOpaque_buf, recordOpaque_len);
+        }
+        else
+        {
+            notToKeep->elements[0]->record =
+                z_ext_record_oid(p->odr_out, syntax_oid,
+                                 record_buf, record_len);
+        }
+    }
+    if (0 && apdu)
+    {
+        ODR print = odr_createmem(ODR_PRINT);
+
+        z_APDU(print, &apdu, 0, 0);
+        odr_destroy(print);
+    }
+    return apdu;
+}
+
+
+static void otherInfo_attach(ZOOM_connection c, Z_APDU *a, ODR out)
+{
+    int i;
+    for (i = 0; i<200; i++)
+    {
+        size_t len;
+        Odr_oid *oid;
+        Z_OtherInformation **oi;
+        char buf[80];
+        const char *val;
+        const char *cp;
+
+        sprintf(buf, "otherInfo%d", i);
+        val = ZOOM_options_get(c->options, buf);
+        if (!val)
+            break;
+        cp = strchr(val, ':');
+        if (!cp)
+            continue;
+        len = cp - val;
+        if (len >= sizeof(buf))
+            len = sizeof(buf)-1;
+        memcpy(buf, val, len);
+        buf[len] = '\0';
+        
+        oid = yaz_string_to_oid_odr(yaz_oid_std(), CLASS_USERINFO,
+                                    buf, out);
+        if (!oid)
+            continue;
+        
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, oid, 1, cp+1);
+    }
+}
+
+
+
+static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
+{
+    assert(a);
+    if (c->cookie_out)
+    {
+        Z_OtherInformation **oi;
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_cookie, 
+                              1, c->cookie_out);
+    }
+    if (c->client_IP)
+    {
+        Z_OtherInformation **oi;
+        yaz_oi_APDU(a, &oi);
+        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
+                              1, c->client_IP);
+    }
+    otherInfo_attach(c, a, out);
+    if (!z_APDU(out, &a, 0, 0))
+    {
+        FILE *outf = fopen("/tmp/apdu.txt", "a");
+        if (a && outf)
+        {
+            ODR odr_pr = odr_createmem(ODR_PRINT);
+            fprintf(outf, "a=%p\n", a);
+            odr_setprint(odr_pr, outf);
+            z_APDU(odr_pr, &a, 0, 0);
+            odr_destroy(odr_pr);
+        }
+        yaz_log(c->log_api, "%p encoding_APDU: encoding failed", c);
+        ZOOM_set_error(c, ZOOM_ERROR_ENCODE, 0);
+        odr_reset(out);
+        return -1;
+    }
+    if (c->odr_print)
+        z_APDU(c->odr_print, &a, 0, 0);
+    yaz_log(c->log_details, "%p encoding_APDU encoding OK", c);
+    return 0;
+}
+
+static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
+{
+    ZOOM_Event event;
+    assert(a);
+    if (encode_APDU(c, a, c->odr_out))
+        return zoom_complete;
+    yaz_log(c->log_details, "%p send APDU type=%d", c, a->which);
+    c->buf_out = odr_getbuf(c->odr_out, &c->len_out, 0);
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
+    odr_reset(c->odr_out);
+    return ZOOM_send_buf(c);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_init(ZOOM_connection c)
+{
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_initRequest);
+    Z_InitRequest *ireq = apdu->u.initRequest;
+    Z_IdAuthentication *auth = (Z_IdAuthentication *)
+        odr_malloc(c->odr_out, sizeof(*auth));
+
+    ODR_MASK_SET(ireq->options, Z_Options_search);
+    ODR_MASK_SET(ireq->options, Z_Options_present);
+    ODR_MASK_SET(ireq->options, Z_Options_scan);
+    ODR_MASK_SET(ireq->options, Z_Options_sort);
+    ODR_MASK_SET(ireq->options, Z_Options_extendedServices);
+    ODR_MASK_SET(ireq->options, Z_Options_namedResultSets);
+    
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
+    ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
+    
+    ireq->implementationId =
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationId"),
+                    ireq->implementationId);
+    
+    ireq->implementationName = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationName"),
+                    odr_prepend(c->odr_out, "ZOOM-C",
+                                ireq->implementationName));
+    
+    ireq->implementationVersion = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationVersion"),
+                                ireq->implementationVersion);
+    
+    *ireq->maximumRecordSize = c->maximum_record_size;
+    *ireq->preferredMessageSize = c->preferred_message_size;
+    
+    if (c->group || c->password)
+    {
+        Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
+        pass->groupId = odr_strdup_null(c->odr_out, c->group);
+        pass->userId = odr_strdup_null(c->odr_out, c->user);
+        pass->password = odr_strdup_null(c->odr_out, c->password);
+        auth->which = Z_IdAuthentication_idPass;
+        auth->u.idPass = pass;
+        ireq->idAuthentication = auth;
+    }
+    else if (c->user)
+    {
+        auth->which = Z_IdAuthentication_open;
+        auth->u.open = odr_strdup(c->odr_out, c->user);
+        ireq->idAuthentication = auth;
+    }
+    if (c->proxy)
+    {
+        yaz_oi_set_string_oid(&ireq->otherInfo, c->odr_out,
+                              yaz_oid_userinfo_proxy, 1, c->host_port);
+    }
+    if (c->charset || c->lang)
+    {
+        Z_OtherInformation **oi;
+        Z_OtherInformationUnit *oi_unit;
+        
+        yaz_oi_APDU(apdu, &oi);
+        
+        if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
+        {
+            ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
+            oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
+            oi_unit->information.externallyDefinedInfo =
+                yaz_set_proposal_charneg_list(c->odr_out, " ",
+                                              c->charset, c->lang, 1);
+        }
+    }
+    assert(apdu);
+    return send_APDU(c, apdu);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_search(ZOOM_connection c)
+{
+    ZOOM_resultset r;
+    int lslb, ssub, mspn;
+    const char *syntax;
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_searchRequest);
+    Z_SearchRequest *search_req = apdu->u.searchRequest;
+    const char *elementSetName;
+    const char *smallSetElementSetName;
+    const char *mediumSetElementSetName;
+    const char *facets;
+
+    assert(c->tasks);
+    assert(c->tasks->which == ZOOM_TASK_SEARCH);
+
+    r = c->tasks->u.search.resultset;
+
+    yaz_log(c->log_details, "%p ZOOM_connection_send_search set=%p", c, r);
+
+    elementSetName =
+        ZOOM_options_get(r->options, "elementSetName");
+    smallSetElementSetName  =
+        ZOOM_options_get(r->options, "smallSetElementSetName");
+    mediumSetElementSetName =
+        ZOOM_options_get(r->options, "mediumSetElementSetName");
+
+    if (!smallSetElementSetName)
+        smallSetElementSetName = elementSetName;
+
+    if (!mediumSetElementSetName)
+        mediumSetElementSetName = elementSetName;
+
+    facets = ZOOM_options_get(r->options, "facets");
+    if (facets) {
+        Z_FacetList *facet_list = yaz_pqf_parse_facet_list(c->odr_out, facets);
+        if (facet_list) {
+            Z_OtherInformation **oi;
+            yaz_oi_APDU(apdu, &oi);
+            if (facet_list) {
+                yaz_oi_set_facetlist(oi, c->odr_out, facet_list);
+            }
+        }
+    }
+
+    assert(r);
+    assert(r->query);
+
+    /* prepare query for the search request */
+    search_req->query = ZOOM_query_get_Z_Query(r->query);
+    if (!search_req->query)
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+        return zoom_complete;
+    }
+    if (search_req->query->which == Z_Query_type_1 || 
+        search_req->query->which == Z_Query_type_101)
+    {
+        const char *cp = ZOOM_options_get(r->options, "rpnCharset");
+        if (cp)
+        {
+            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
+            if (cd)
+            {
+                int r;
+                search_req->query = yaz_copy_Z_Query(search_req->query,
+                                                     c->odr_out);
+                
+                r = yaz_query_charset_convert_rpnquery_check(
+                    search_req->query->u.type_1,
+                    c->odr_out, cd);
+                yaz_iconv_close(cd);
+                if (r)
+                {  /* query could not be char converted */
+                    ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+                    return zoom_complete;
+                }
+            }
+        }
+    }
+    search_req->databaseNames = r->databaseNames;
+    search_req->num_databaseNames = r->num_databaseNames;
+
+    /* get syntax (no need to provide unless piggyback is in effect) */
+    syntax = c->tasks->u.search.syntax;
+
+    lslb = ZOOM_options_get_int(r->options, "largeSetLowerBound", -1);
+    ssub = ZOOM_options_get_int(r->options, "smallSetUpperBound", -1);
+    mspn = ZOOM_options_get_int(r->options, "mediumSetPresentNumber", -1);
+    if (lslb != -1 && ssub != -1 && mspn != -1)
+    {
+        /* So're a Z39.50 expert? Let's hope you don't do sort */
+        *search_req->largeSetLowerBound = lslb;
+        *search_req->smallSetUpperBound = ssub;
+        *search_req->mediumSetPresentNumber = mspn;
+    }
+    else if (c->tasks->u.search.start == 0 && c->tasks->u.search.count > 0
+             && r->piggyback && !r->r_sort_spec && !r->schema)
+    {
+        /* Regular piggyback - do it unless we're going to do sort */
+        *search_req->largeSetLowerBound = 2000000000;
+        *search_req->smallSetUpperBound = 1;
+        *search_req->mediumSetPresentNumber = 
+            r->step>0 ? r->step : c->tasks->u.search.count;
+    }
+    else
+    {
+        /* non-piggyback. Need not provide elementsets or syntaxes .. */
+        smallSetElementSetName = 0;
+        mediumSetElementSetName = 0;
+        syntax = 0;
+    }
+    if (smallSetElementSetName && *smallSetElementSetName)
+    {
+        Z_ElementSetNames *esn = (Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, smallSetElementSetName);
+        search_req->smallSetElementSetNames = esn;
+    }
+    if (mediumSetElementSetName && *mediumSetElementSetName)
+    {
+        Z_ElementSetNames *esn =(Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
+        search_req->mediumSetElementSetNames = esn;
+    }
+    if (syntax)
+        search_req->preferredRecordSyntax =
+            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
+    
+    if (!r->setname)
+    {
+        if (c->support_named_resultsets)
+        {
+            char setname[14];
+            int ord;
+            /* find the lowest unused ordinal so that we re-use
+               result sets on the server. */
+            for (ord = 1; ; ord++)
+            {
+#if ZOOM_RESULT_LISTS
+                ZOOM_resultsets rsp;
+                sprintf(setname, "%d", ord);
+                for (rsp = c->resultsets; rsp; rsp = rsp->next)
+                    if (rsp->resultset->setname && !strcmp(rsp->resultset->setname, setname))
+                        break;
+                if (!rsp)
+                    break;
+#else
+                ZOOM_resultset rp;
+                sprintf(setname, "%d", ord);
+                for (rp = c->resultsets; rp; rp = rp->next)
+                    if (rp->setname && !strcmp(rp->setname, setname))
+                        break;
+                if (!rp)
+                    break;
+#endif
+
+            }
+            r->setname = xstrdup(setname);
+            yaz_log(c->log_details, "%p ZOOM_connection_send_search: "
+                    "allocating set %s", c, r->setname);
+        }
+        else
+        {
+            yaz_log(c->log_details, "%p ZOOM_connection_send_search: using "
+                    "default set", c);
+            r->setname = xstrdup("default");
+        }
+        ZOOM_options_set(r->options, "setname", r->setname);
+    }
+    search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
+    return send_APDU(c, apdu);
+}
+
+zoom_ret ZOOM_connection_Z3950_send_scan(ZOOM_connection c)
+{
+    ZOOM_scanset scan;
+    Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_scanRequest);
+    Z_ScanRequest *req = apdu->u.scanRequest;
+    Z_Query *z_query;
+
+    yaz_log(c->log_details, "%p send_scan", c);
+    if (!c->tasks)
+        return zoom_complete;
+    assert (c->tasks->which == ZOOM_TASK_SCAN);
+    scan = c->tasks->u.scan.scan;
+
+    z_query = ZOOM_query_get_Z_Query(scan->query);
+
+    /* Z39.50 scan can only carry RPN */
+    if (z_query->which == Z_Query_type_1 ||
+        z_query->which == Z_Query_type_101)
+    {
+        Z_RPNQuery *rpn = z_query->u.type_1;
+        const char *cp = ZOOM_options_get(scan->options, "rpnCharset");
+        if (cp)
+        {
+            yaz_iconv_t cd = yaz_iconv_open(cp, "UTF-8");
+            if (cd)
+            {
+                rpn = yaz_copy_z_RPNQuery(rpn, c->odr_out);
+
+                yaz_query_charset_convert_rpnquery(
+                    rpn, c->odr_out, cd);
+                yaz_iconv_close(cd);
+            }
+        }
+        req->attributeSet = rpn->attributeSetId;
+        if (!req->attributeSet)
+            req->attributeSet = odr_oiddup(c->odr_out, yaz_oid_attset_bib_1);
+        if (rpn->RPNStructure->which == Z_RPNStructure_simple &&
+            rpn->RPNStructure->u.simple->which == Z_Operand_APT)
+        {
+            req->termListAndStartPoint =
+                rpn->RPNStructure->u.simple->u.attributesPlusTerm;
+        }
+        else
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
+            return zoom_complete;
+        }
+    }
+    else
+    {
+        ZOOM_set_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
+        return zoom_complete;
+    }
+
+    *req->numberOfTermsRequested =
+        ZOOM_options_get_int(scan->options, "number", 20);
+
+    req->preferredPositionInResponse =
+        odr_intdup(c->odr_out,
+                   ZOOM_options_get_int(scan->options, "position", 1));
+
+    req->stepSize =
+        odr_intdup(c->odr_out,
+                   ZOOM_options_get_int(scan->options, "stepSize", 0));
+    
+    req->databaseNames = scan->databaseNames;
+    req->num_databaseNames = scan->num_databaseNames;
+
+    return send_APDU(c, apdu);
+}
+
+ZOOM_API(void)
+    ZOOM_package_send(ZOOM_package p, const char *type)
+{
+    Z_APDU *apdu = 0;
+    ZOOM_connection c;
+    if (!p)
+        return;
+    c = p->connection;
+    odr_reset(p->odr_out);
+    xfree(p->buf_out);
+    p->buf_out = 0;
+    if (!strcmp(type, "itemorder"))
+    {
+        apdu = create_es_package(p, yaz_oid_extserv_item_order);
+        if (apdu)
+        {
+            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
+            
+            r->direct_reference = 
+                odr_oiddup(p->odr_out, yaz_oid_extserv_item_order);
+            r->descriptor = 0;
+            r->which = Z_External_itemOrder;
+            r->indirect_reference = 0;
+            r->u.itemOrder = encode_item_order(p);
+
+            apdu->u.extendedServicesRequest->taskSpecificParameters = r;
+        }
+    }
+    else if (!strcmp(type, "create"))  /* create database */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_create,
+                                    0, 0);
+    }   
+    else if (!strcmp(type, "drop"))  /* drop database */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_drop,
+                                    0, 0);
+    }
+    else if (!strcmp(type, "commit"))  /* commit changes */
+    {
+        apdu = create_admin_package(p, Z_ESAdminOriginPartToKeep_commit,
+                                    0, 0);
+    }
+    else if (!strcmp(type, "update")) /* update record(s) */
+    {
+        apdu = create_update_package(p);
+    }
+    else if (!strcmp(type, "xmlupdate"))
+    {
+        apdu = create_xmlupdate_package(p);
+    }
+    if (apdu)
+    {
+        if (encode_APDU(p->connection, apdu, p->odr_out) == 0)
+        {
+            char *buf;
+
+            ZOOM_task task = ZOOM_connection_add_task(c, ZOOM_TASK_PACKAGE);
+            task->u.package = p;
+            buf = odr_getbuf(p->odr_out, &p->len_out, 0);
+            p->buf_out = (char *) xmalloc(p->len_out);
+            memcpy(p->buf_out, buf, p->len_out);
+            
+            (p->refcount)++;
+            if (!c->async)
+            {
+                while (ZOOM_event(1, &c))
+                    ;
+            }
+        }
+    }
+}
+
+
+static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
+                                 int present_phase);
+
+static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
+{
+    char oid_name_buf[OID_STR_MAX];
+    const char *oid_name;
+    char *addinfo = 0;
+
+    oid_name = yaz_oid_to_string_buf(r->diagnosticSetId, 0, oid_name_buf);
+    switch (r->which)
+    {
+    case Z_DefaultDiagFormat_v2Addinfo:
+        addinfo = r->u.v2Addinfo;
+        break;
+    case Z_DefaultDiagFormat_v3Addinfo:
+        addinfo = r->u.v3Addinfo;
+        break;
+    }
+    xfree(c->addinfo);
+    c->addinfo = 0;
+    ZOOM_set_dset_error(c, *r->condition, oid_name, addinfo, 0);
+}
+
+static void response_diag(ZOOM_connection c, Z_DiagRec *p)
+{
+    if (p->which != Z_DiagRec_defaultFormat)
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+    else
+        response_default_diag(c, p->u.defaultFormat);
+}
+
+static int es_response_taskpackage_update(ZOOM_connection c,
+               Z_IUUpdateTaskPackage *utp)
+{
+       if (utp && utp->targetPart)
+       {
+               Z_IUTargetPart *targetPart = utp->targetPart;
+               switch ( *targetPart->updateStatus ) {
+                       case Z_IUTargetPart_success:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "success");
+                               break;
+                       case Z_IUTargetPart_partial:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "partial");
+                               break;
+                       case Z_IUTargetPart_failure:
+                               ZOOM_options_set(c->tasks->u.package->options,"updateStatus", "failure");
+                               if (targetPart->globalDiagnostics && targetPart->num_globalDiagnostics > 0)
+                                       response_diag(c, targetPart->globalDiagnostics[0]);
+                               break;
+               }
+               // NOTE: Individual record status, surrogate diagnostics, and supplemental diagnostics ARE NOT REPORTED.
+       }
+    return 1;
+}
+
+static int es_response_taskpackage(ZOOM_connection c,
+                                   Z_TaskPackage *taskPackage)
+{
+       // targetReference
+       Odr_oct *id = taskPackage->targetReference;
+       if (id)
+               ZOOM_options_setl(c->tasks->u.package->options,
+                                                       "targetReference", (char*) id->buf, id->len);
+       
+       // taskStatus
+       switch ( *taskPackage->taskStatus ) {
+               case Z_TaskPackage_pending:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "pending");
+                       break;
+               case Z_TaskPackage_active:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "active");
+                       break;
+               case Z_TaskPackage_complete:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "complete");
+                       break;
+               case Z_TaskPackage_aborted:
+                       ZOOM_options_set(c->tasks->u.package->options,"taskStatus", "aborted");
+                       if ( taskPackage->num_packageDiagnostics && taskPackage->packageDiagnostics )
+                               response_diag(c, taskPackage->packageDiagnostics[0]);
+                       break;
+       }
+       
+       // taskSpecificParameters
+       // NOTE: Only Update implemented, no others.
+       if ( taskPackage->taskSpecificParameters->which == Z_External_update ) {
+                       Z_IUUpdateTaskPackage *utp = taskPackage->taskSpecificParameters->u.update->u.taskPackage;
+                       es_response_taskpackage_update(c, utp);
+       }
+       return 1;
+}
+
+
+static int handle_Z3950_es_response(ZOOM_connection c,
+                                    Z_ExtendedServicesResponse *res)
+{
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_PACKAGE)
+        return 0;
+    switch (*res->operationStatus) {
+        case Z_ExtendedServicesResponse_done:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "done");
+            break;
+        case Z_ExtendedServicesResponse_accepted:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "accepted");
+            break;
+        case Z_ExtendedServicesResponse_failure:
+            ZOOM_options_set(c->tasks->u.package->options,"operationStatus", "failure");
+            if (res->diagnostics && res->num_diagnostics > 0)
+                response_diag(c, res->diagnostics[0]);
+            break;
+    }
+    if (res->taskPackage &&
+        res->taskPackage->which == Z_External_extendedService)
+    {
+        Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
+        es_response_taskpackage(c, taskPackage);
+    }
+    if (res->taskPackage && 
+        res->taskPackage->which == Z_External_octet)
+    {
+        Odr_oct *doc = res->taskPackage->u.octet_aligned;
+        ZOOM_options_setl(c->tasks->u.package->options,
+                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
+    }
+    return 1;
+}
+
+static void interpret_init_diag(ZOOM_connection c,
+                                Z_DiagnosticFormat *diag)
+{
+    if (diag->num > 0)
+    {
+        Z_DiagnosticFormat_s *ds = diag->elements[0];
+        if (ds->which == Z_DiagnosticFormat_s_defaultDiagRec)
+            response_default_diag(c, ds->u.defaultDiagRec);
+    }
+}
+
+
+static void interpret_otherinformation_field(ZOOM_connection c,
+                                             Z_OtherInformation *ui)
+{
+    int i;
+    for (i = 0; i < ui->num_elements; i++)
+    {
+        Z_OtherInformationUnit *unit = ui->list[i];
+        if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo &&
+            unit->information.externallyDefinedInfo->which ==
+            Z_External_diag1) 
+        {
+            interpret_init_diag(c, unit->information.externallyDefinedInfo->u.diag1);
+        } 
+    }
+}
+
+static char *get_term_cstr(ODR odr, Z_Term *term) {
+
+    switch (term->which) {
+    case Z_Term_general:
+            return odr_strdupn(odr, (const char *) term->u.general->buf, (size_t) term->u.general->len);
+        break;
+    case Z_Term_characterString:
+        return odr_strdup(odr, term->u.characterString);
+    }
+    return 0;
+}
+
+static ZOOM_facet_field get_zoom_facet_field(ODR odr, Z_FacetField *facet) {
+    int term_index;
+    struct yaz_facet_attr attr_values;
+    ZOOM_facet_field facet_field = odr_malloc(odr, sizeof(*facet_field));
+    yaz_facet_attr_init(&attr_values);
+    yaz_facet_attr_get_z_attributes(facet->attributes, &attr_values);
+    facet_field->facet_name = odr_strdup(odr, attr_values.useattr);
+    facet_field->num_terms = facet->num_terms;
+    yaz_log(YLOG_DEBUG, "ZOOM_facet_field %s %d terms %d", attr_values.useattr, attr_values.limit, facet->num_terms);
+    facet_field->facet_terms = odr_malloc(odr, facet_field->num_terms * sizeof(*facet_field->facet_terms));
+    for (term_index = 0 ; term_index < facet->num_terms; term_index++) {
+        Z_FacetTerm *facetTerm = facet->terms[term_index];
+        facet_field->facet_terms[term_index].frequency = *facetTerm->count;
+        facet_field->facet_terms[term_index].term = get_term_cstr(odr, facetTerm->term);
+        yaz_log(YLOG_DEBUG, "    term[%d] %s %d",
+                term_index, facet_field->facet_terms[term_index].term, facet_field->facet_terms[term_index].frequency);
+    }
+    return facet_field;
+}
+
+static void handle_facet_result(ZOOM_connection c, ZOOM_resultset r,
+                                Z_OtherInformation *o)
+{
+    int i;
+    for (i = 0; o && i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            if (ext->which == Z_External_userFacets)
+            {
+                int j;
+                Z_FacetList *fl = ext->u.facetList;
+                r->num_facets   = fl->num;
+                yaz_log(YLOG_DEBUG, "Facets found: %d", fl->num);
+                r->facets       =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets));
+                r->facets_names =  odr_malloc(r->odr, r->num_facets * sizeof(*r->facets_names));
+                for (j = 0; j < fl->num; j++)
+                {
+                    r->facets[j] = get_zoom_facet_field(r->odr, fl->elements[j]);
+                    if (!r->facets[j])
+                        yaz_log(YLOG_DEBUG, "Facet field missing on index %d !", j);
+                    r->facets_names[j] = (char *) ZOOM_facet_field_name(r->facets[j]);
+                }
+            }
+        }
+    }
+}
+
+static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
+                                       Z_Term *term)
+{
+    switch (term->which)
+    {
+    case Z_Term_general:
+        ZOOM_options_setl(opt, name,
+                          (const char *)(term->u.general->buf), 
+                          term->u.general->len);
+        break;
+    case Z_Term_characterString:
+        ZOOM_options_set(opt, name, term->u.characterString);
+        break;
+    case Z_Term_numeric:
+        ZOOM_options_set_int(opt, name, *term->u.numeric);
+        break;
+    }
+}
+
+static void handle_queryExpression(ZOOM_options opt, const char *name,
+                                   Z_QueryExpression *exp)
+{
+    char opt_name[80];
+    
+    switch (exp->which)
+    {
+    case Z_QueryExpression_term:
+        if (exp->u.term && exp->u.term->queryTerm)
+        {
+            sprintf(opt_name, "%s.term", name);
+            handle_queryExpressionTerm(opt, opt_name, exp->u.term->queryTerm);
+        }
+        break;
+    case Z_QueryExpression_query:
+        break;
+    }
+}
+
+
+static void handle_search_result(ZOOM_connection c, ZOOM_resultset resultset,
+                                Z_OtherInformation *o)
+{
+    int i;
+    for (i = 0; o && i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            
+            if (ext->which == Z_External_searchResult1)
+            {
+                int j;
+                Z_SearchInfoReport *sr = ext->u.searchResult1;
+                
+                if (sr->num)
+                    ZOOM_options_set_int(
+                        resultset->options, "searchresult.size", sr->num);
+
+                for (j = 0; j < sr->num; j++)
+                {
+                    Z_SearchInfoReport_s *ent =
+                        ext->u.searchResult1->elements[j];
+                    char pref[80];
+                    
+                    sprintf(pref, "searchresult.%d", j);
+
+                    if (ent->subqueryId)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.id", pref);
+                        ZOOM_options_set(resultset->options, opt_name,
+                                         ent->subqueryId);
+                    }
+                    if (ent->subqueryExpression)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.subquery", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryExpression);
+                    }
+                    if (ent->subqueryInterpretation)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.interpretation", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryInterpretation);
+                    }
+                    if (ent->subqueryRecommendation)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.recommendation", pref);
+                        handle_queryExpression(resultset->options, opt_name,
+                                               ent->subqueryRecommendation);
+                    }
+                    if (ent->subqueryCount)
+                    {
+                        char opt_name[80];
+                        sprintf(opt_name, "%s.count", pref);
+                        ZOOM_options_set_int(resultset->options, opt_name,
+                                             *ent->subqueryCount);
+                    }                                             
+                }
+            }
+        }
+    }
+}
+
+static void handle_Z3950_search_response(ZOOM_connection c,
+                                         Z_SearchResponse *sr)
+{
+    ZOOM_resultset resultset;
+    ZOOM_Event event;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SEARCH)
+        return ;
+
+    event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
+    ZOOM_connection_put_event(c, event);
+
+    resultset = c->tasks->u.search.resultset;
+
+    if (sr->resultSetStatus)
+    {
+        ZOOM_options_set_int(resultset->options, "resultSetStatus",
+                             *sr->resultSetStatus);
+    }
+    if (sr->presentStatus)
+    {
+        ZOOM_options_set_int(resultset->options, "presentStatus",
+                             *sr->presentStatus);
+    }
+    handle_search_result(c, resultset, sr->additionalSearchInfo);
+
+    handle_facet_result(c, resultset, sr->additionalSearchInfo);
+
+    resultset->size = *sr->resultCount;
+    handle_Z3950_records(c, sr->records, 0);
+}
+
+static void handle_Z3950_sort_response(ZOOM_connection c, Z_SortResponse *res)
+{
+    if (res->diagnostics && res->num_diagnostics > 0)
+        response_diag(c, res->diagnostics[0]);
+}
+
+static int handle_Z3950_scan_response(ZOOM_connection c, Z_ScanResponse *res)
+{
+    NMEM nmem = odr_extract_mem(c->odr_in);
+    ZOOM_scanset scan;
+
+    if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
+        return 0;
+    scan = c->tasks->u.scan.scan;
+
+    if (res->entries && res->entries->nonsurrogateDiagnostics)
+        response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
+    scan->scan_response = res;
+    scan->srw_scan_response = 0;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
+    if (res->stepSize)
+        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
+    if (res->positionOfTerm)
+        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
+    if (res->scanStatus)
+        ZOOM_options_set_int(scan->options, "scanStatus", *res->scanStatus);
+    if (res->numberOfEntriesReturned)
+        ZOOM_options_set_int(scan->options, "number",
+                             *res->numberOfEntriesReturned);
+    nmem_destroy(nmem);
+    return 1;
+}
+
+static void handle_Z3950_records(ZOOM_connection c, Z_Records *sr,
+                                 int present_phase)
+{
+    ZOOM_resultset resultset;
+    int *start, *count;
+    const char *syntax = 0, *elementSetName = 0;
+
+    if (!c->tasks)
+        return ;
+    switch (c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;        
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return;
+    }
+    if (sr && sr->which == Z_Records_NSD)
+        response_default_diag(c, sr->u.nonSurrogateDiagnostic);
+    else if (sr && sr->which == Z_Records_multipleNSD)
+    {
+        if (sr->u.multipleNonSurDiagnostics->num_diagRecs >= 1)
+            response_diag(c, sr->u.multipleNonSurDiagnostics->diagRecs[0]);
+        else
+            ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+    }
+    else 
+    {
+        if (*count + *start > resultset->size)
+            *count = resultset->size - *start;
+        if (*count < 0)
+            *count = 0;
+        if (sr && sr->which == Z_Records_DBOSD)
+        {
+            int i;
+            NMEM nmem = odr_extract_mem(c->odr_in);
+            Z_NamePlusRecordList *p =
+                sr->u.databaseOrSurDiagnostics;
+            for (i = 0; i<p->num_records; i++)
+            {
+                ZOOM_record_cache_add(resultset, p->records[i], i + *start,
+                                      syntax, elementSetName,
+                                      elementSetName, 0);
+            }
+            *count -= i;
+            if (*count < 0)
+                *count = 0;
+            *start += i;
+            yaz_log(c->log_details, 
+                    "handle_records resultset=%p start=%d count=%d",
+                    resultset, *start, *count);
+
+            /* transfer our response to search_nmem .. we need it later */
+            nmem_transfer(odr_getmem(resultset->odr), nmem);
+            nmem_destroy(nmem);
+            if (present_phase && p->num_records == 0)
+            {
+                /* present response and we didn't get any records! */
+                Z_NamePlusRecord *myrec = 
+                    zget_surrogateDiagRec(
+                        resultset->odr, 0, 
+                        YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                        "ZOOM C generated. Present phase and no records");
+                ZOOM_record_cache_add(resultset, myrec, *start,
+                                      syntax, elementSetName, 0, 0);
+            }
+        }
+        else if (present_phase)
+        {
+            /* present response and we didn't get any records! */
+            Z_NamePlusRecord *myrec = 
+                zget_surrogateDiagRec(
+                    resultset->odr, 0,
+                    YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS,
+                    "ZOOM C generated: Present response and no records");
+            ZOOM_record_cache_add(resultset, myrec, *start,
+                                  syntax, elementSetName, 0, 0);
+        }
+    }
+}
+
+static void handle_Z3950_present_response(ZOOM_connection c,
+                                          Z_PresentResponse *pr)
+{
+    handle_Z3950_records(c, pr->records, 1);
+}
+
+static void set_init_option(const char *name, void *clientData)
+{
+    ZOOM_connection c = (ZOOM_connection) clientData;
+    char buf[80];
+
+    sprintf(buf, "init_opt_%.70s", name);
+    ZOOM_connection_option_set(c, buf, "1");
+}
+
+
+zoom_ret send_Z3950_sort(ZOOM_connection c, ZOOM_resultset resultset)
+{
+    if (c->error)
+        resultset->r_sort_spec = 0;
+    if (resultset->r_sort_spec)
+    {
+        Z_APDU *apdu = zget_APDU(c->odr_out, Z_APDU_sortRequest);
+        Z_SortRequest *req = apdu->u.sortRequest;
+        
+        req->num_inputResultSetNames = 1;
+        req->inputResultSetNames = (Z_InternationalString **)
+            odr_malloc(c->odr_out, sizeof(*req->inputResultSetNames));
+        req->inputResultSetNames[0] =
+            odr_strdup(c->odr_out, resultset->setname);
+        req->sortedResultSetName = odr_strdup(c->odr_out, resultset->setname);
+        req->sortSequence = resultset->r_sort_spec;
+        resultset->r_sort_spec = 0;
+        return send_APDU(c, apdu);
+    }
+    return zoom_complete;
+}
+
+zoom_ret send_Z3950_present(ZOOM_connection c)
+{
+    Z_APDU *apdu = 0;
+    Z_PresentRequest *req = 0;
+    int i = 0;
+    const char *syntax = 0;
+    const char *elementSetName = 0;
+    ZOOM_resultset  resultset;
+    int *start, *count;
+
+    if (!c->tasks)
+    {
+        yaz_log(c->log_details, "%p send_present no tasks", c);
+        return zoom_complete;
+    }
+    
+    switch (c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        resultset = c->tasks->u.search.resultset;
+        start = &c->tasks->u.search.start;
+        count = &c->tasks->u.search.count;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        resultset = c->tasks->u.retrieve.resultset;
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return zoom_complete;
+    }
+    yaz_log(c->log_details, "%p send_present start=%d count=%d",
+            c, *start, *count);
+
+    if (*start < 0 || *count < 0 || *start + *count > resultset->size)
+    {
+        ZOOM_set_dset_error(c, YAZ_BIB1_PRESENT_REQUEST_OUT_OF_RANGE, "Bib-1",
+                       "", 0);
+    }
+    if (c->error)                  /* don't continue on error */
+        return zoom_complete;
+    yaz_log(c->log_details, "send_present resultset=%p start=%d count=%d",
+            resultset, *start, *count);
+
+    for (i = 0; i < *count; i++)
+    {
+        ZOOM_record rec =
+            ZOOM_record_cache_lookup(resultset, i + *start,
+                                     syntax, elementSetName);
+        if (!rec)
+            break;
+        else
+        {
+            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_RECV_RECORD);
+            ZOOM_connection_put_event(c, event);
+        }
+    }
+    *start += i;
+    *count -= i;
+
+    if (*count == 0)
+    {
+        yaz_log(c->log_details, "%p send_present skip=%d no more to fetch", c, i);
+        return zoom_complete;
+    }
+
+    apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
+    req = apdu->u.presentRequest;
+
+    if (i)
+        yaz_log(c->log_details, "%p send_present skip=%d", c, i);
+
+    *req->resultSetStartPoint = *start + 1;
+
+    if (resultset->step > 0 && resultset->step < *count)
+        *req->numberOfRecordsRequested = resultset->step;
+    else
+        *req->numberOfRecordsRequested = *count;
+    
+    if (*req->numberOfRecordsRequested + *start > resultset->size)
+        *req->numberOfRecordsRequested = resultset->size - *start;
+    assert(*req->numberOfRecordsRequested > 0);
+
+    if (syntax && *syntax)
+        req->preferredRecordSyntax =
+            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
+
+    if (resultset->schema && *resultset->schema)
+    {
+        Z_RecordComposition *compo = (Z_RecordComposition *)
+            odr_malloc(c->odr_out, sizeof(*compo));
+
+        req->recordComposition = compo;
+        compo->which = Z_RecordComp_complex;
+        compo->u.complex = (Z_CompSpec *)
+            odr_malloc(c->odr_out, sizeof(*compo->u.complex));
+        compo->u.complex->selectAlternativeSyntax = (bool_t *) 
+            odr_malloc(c->odr_out, sizeof(bool_t));
+        *compo->u.complex->selectAlternativeSyntax = 0;
+
+        compo->u.complex->generic = (Z_Specification *)
+            odr_malloc(c->odr_out, sizeof(*compo->u.complex->generic));
+
+        compo->u.complex->generic->which = Z_Schema_oid;
+        compo->u.complex->generic->schema.oid = (Odr_oid *)
+            zoom_yaz_str_to_z3950oid (c, CLASS_SCHEMA, resultset->schema);
+
+        if (!compo->u.complex->generic->schema.oid)
+        {
+            /* OID wasn't a schema! Try record syntax instead. */
+
+            compo->u.complex->generic->schema.oid = (Odr_oid *)
+                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
+        }
+        if (elementSetName && *elementSetName)
+        {
+            compo->u.complex->generic->elementSpec = (Z_ElementSpec *)
+                odr_malloc(c->odr_out, sizeof(Z_ElementSpec));
+            compo->u.complex->generic->elementSpec->which =
+                Z_ElementSpec_elementSetName;
+            compo->u.complex->generic->elementSpec->u.elementSetName =
+                odr_strdup(c->odr_out, elementSetName);
+        }
+        else
+            compo->u.complex->generic->elementSpec = 0;
+        compo->u.complex->num_dbSpecific = 0;
+        compo->u.complex->dbSpecific = 0;
+        compo->u.complex->num_recordSyntax = 0;
+        compo->u.complex->recordSyntax = 0;
+    }
+    else if (elementSetName && *elementSetName)
+    {
+        Z_ElementSetNames *esn = (Z_ElementSetNames *)
+            odr_malloc(c->odr_out, sizeof(*esn));
+        Z_RecordComposition *compo = (Z_RecordComposition *)
+            odr_malloc(c->odr_out, sizeof(*compo));
+        
+        esn->which = Z_ElementSetNames_generic;
+        esn->u.generic = odr_strdup(c->odr_out, elementSetName);
+        compo->which = Z_RecordComp_simple;
+        compo->u.simple = esn;
+        req->recordComposition = compo;
+    }
+    req->resultSetId = odr_strdup(c->odr_out, resultset->setname);
+    return send_APDU(c, apdu);
+}
+
+static zoom_ret send_Z3950_sort_present(ZOOM_connection c)
+{
+    zoom_ret r = zoom_complete;
+
+    if (c->tasks && c->tasks->which == ZOOM_TASK_SEARCH)
+        r = send_Z3950_sort(c, c->tasks->u.search.resultset);
+    if (r == zoom_complete)
+        r = send_Z3950_present(c);
+    return r;
+}
+
+void ZOOM_handle_Z3950_apdu(ZOOM_connection c, Z_APDU *apdu)
+{
+    Z_InitResponse *initrs;
+    
+    ZOOM_connection_set_mask(c, 0);
+    yaz_log(c->log_details, "%p handle_Z3950_apdu apdu->which=%d",
+            c, apdu->which);
+    switch (apdu->which)
+    {
+    case Z_APDU_initResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu: Received Init response", c);
+        initrs = apdu->u.initResponse;
+        ZOOM_connection_option_set(c, "serverImplementationId",
+                                   initrs->implementationId ?
+                                   initrs->implementationId : "");
+        ZOOM_connection_option_set(c, "serverImplementationName",
+                                   initrs->implementationName ?
+                                   initrs->implementationName : "");
+        ZOOM_connection_option_set(c, "serverImplementationVersion",
+                                   initrs->implementationVersion ?
+                                   initrs->implementationVersion : "");
+        /* Set the three old options too, for old applications */
+        ZOOM_connection_option_set(c, "targetImplementationId",
+                                   initrs->implementationId ?
+                                   initrs->implementationId : "");
+        ZOOM_connection_option_set(c, "targetImplementationName",
+                                   initrs->implementationName ?
+                                   initrs->implementationName : "");
+        ZOOM_connection_option_set(c, "targetImplementationVersion",
+                                   initrs->implementationVersion ?
+                                   initrs->implementationVersion : "");
+
+        /* Make initrs->options available as ZOOM-level options */
+        yaz_init_opt_decode(initrs->options, set_init_option, (void*) c);
+
+        if (!*initrs->result)
+        {
+            Z_External *uif = initrs->userInformationField;
+
+            ZOOM_set_error(c, ZOOM_ERROR_INIT, 0); /* default error */
+
+            if (uif && uif->which == Z_External_userInfo1)
+                interpret_otherinformation_field(c, uif->u.userInfo1);
+        }
+        else
+        {
+            char *cookie =
+                yaz_oi_get_string_oid(&apdu->u.initResponse->otherInfo,
+                                      yaz_oid_userinfo_cookie, 1, 0);
+            xfree(c->cookie_in);
+            c->cookie_in = 0;
+            if (cookie)
+                c->cookie_in = xstrdup(cookie);
+            if (ODR_MASK_GET(initrs->options, Z_Options_namedResultSets) &&
+                ODR_MASK_GET(initrs->protocolVersion, Z_ProtocolVersion_3))
+                c->support_named_resultsets = 1;
+            if (c->tasks)
+            {
+                assert(c->tasks->which == ZOOM_TASK_CONNECT);
+                ZOOM_connection_remove_task(c);
+            }
+            ZOOM_connection_exec_task(c);
+        }
+        if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
+        {
+            NMEM tmpmem = nmem_create();
+            Z_CharSetandLanguageNegotiation *p =
+                yaz_get_charneg_record(initrs->otherInfo);
+            
+            if (p)
+            {
+                char *charset = NULL, *lang = NULL;
+                int sel;
+                
+                yaz_get_response_charneg(tmpmem, p, &charset, &lang, &sel);
+                yaz_log(c->log_details, "%p handle_Z3950_apdu target accepted: "
+                        "charset %s, language %s, select %d",
+                        c,
+                        charset ? charset : "none", lang ? lang : "none", sel);
+                if (charset)
+                    ZOOM_connection_option_set(c, "negotiation-charset",
+                                               charset);
+                if (lang)
+                    ZOOM_connection_option_set(c, "negotiation-lang",
+                                               lang);
+
+                ZOOM_connection_option_set(
+                    c,  "negotiation-charset-in-effect-for-records",
+                    (sel != 0) ? "1" : "0");
+                nmem_destroy(tmpmem);
+            }
+        }       
+        break;
+    case Z_APDU_searchResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Search response", c);
+        handle_Z3950_search_response(c, apdu->u.searchResponse);
+        if (send_Z3950_sort_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_presentResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Present response", c);
+        handle_Z3950_present_response(c, apdu->u.presentResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_sortResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Sort response", c);
+        handle_Z3950_sort_response(c, apdu->u.sortResponse);
+        if (send_Z3950_present(c) == zoom_complete)
+            ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_scanResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Scan response", c);
+        handle_Z3950_scan_response(c, apdu->u.scanResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_extendedServicesResponse:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Extended Services response", c);
+        handle_Z3950_es_response(c, apdu->u.extendedServicesResponse);
+        ZOOM_connection_remove_task(c);
+        break;
+    case Z_APDU_close:
+        yaz_log(c->log_api, "%p handle_Z3950_apdu Close PDU", c);
+        if (!ZOOM_test_reconnect(c))
+        {
+            ZOOM_set_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+            ZOOM_connection_close(c);
+        }
+        break;
+    default:
+        yaz_log(c->log_api, "%p Received unknown PDU", c);
+        ZOOM_set_error(c, ZOOM_ERROR_DECODE, 0);
+        ZOOM_connection_close(c);
+    }
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 4
+ * c-file-style: "Stroustrup"
+ * indent-tabs-mode: nil
+ * End:
+ * vim: shiftwidth=4 tabstop=8 expandtab
+ */
+
index 5405807..8df6c5e 100644 (file)
@@ -473,6 +473,11 @@ MISC_OBJS= \
    $(OBJDIR)\srw.obj \
    $(OBJDIR)\srwutil.obj \
    $(OBJDIR)\zoom-c.obj \
+   $(OBJDIR)\zoom-record-cache.obj \
+   $(OBJDIR)\zoom-z3950.obj \
+   $(OBJDIR)\zoom-sru.obj \
+   $(OBJDIR)\zoom-query.obj \
+   $(OBJDIR)\record-render.obj \
    $(OBJDIR)\facet.obj \
    $(OBJDIR)\zoom-opt.obj \
    $(OBJDIR)\zoom-socket.obj \