Option logapdu renamed apdulog as it harmonizes more with pazpar2
[yaz-moved-to-github.git] / src / zoom-c.c
index cd13a01..bea723f 100644 (file)
@@ -1,8 +1,6 @@
-/*
- * Copyright (C) 1995-2005, Index Data ApS
+/* This file is part of the YAZ toolkit.
+ * Copyright (C) 1995-2008 Index Data
  * See the file LICENSE for details.
- *
- * $Id: zoom-c.c,v 1.62 2005-12-21 16:41:36 mike Exp $
  */
 /**
  * \file zoom-c.c
 #include <yaz/ill.h>
 #include <yaz/srw.h>
 #include <yaz/cql.h>
-
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#if HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-#if HAVE_SYS_POLL_H
-#include <sys/poll.h>
-#endif
-#if HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#ifdef WIN32
-#include <winsock.h>
-#endif
+#include <yaz/ccl.h>
+#include <yaz/query-charset.h>
+#include <yaz/copy_types.h>
+#include <yaz/snprintf.h>
 
 static int log_api = 0;
 static int log_details = 0;
@@ -50,12 +36,28 @@ typedef enum {
     zoom_complete
 } zoom_ret;
 
-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 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);
 
 
-static void initlog()
+/*
+ * 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)
@@ -66,30 +68,31 @@ static void initlog()
     }
 }
 
-static ZOOM_Event ZOOM_Event_create (int kind)
+static ZOOM_Event ZOOM_Event_create(int kind)
 {
-    ZOOM_Event event = (ZOOM_Event) xmalloc (sizeof(*event));
+    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;
 }
 
-static void ZOOM_Event_destroy (ZOOM_Event event)
+static void ZOOM_Event_destroy(ZOOM_Event event)
 {
-    xfree (event);
+    xfree(event);
 }
 
-static void ZOOM_connection_put_event (ZOOM_connection c, ZOOM_Event event)
+static void ZOOM_connection_put_event(ZOOM_connection c, ZOOM_Event event)
 {
     if (c->m_queue_back)
     {
         c->m_queue_back->prev = event;
-        assert (c->m_queue_front);
+        assert(c->m_queue_front);
     }
     else
     {
-        assert (!c->m_queue_front);
+        assert(!c->m_queue_front);
         c->m_queue_front = event;
     }
     event->next = c->m_queue_back;
@@ -105,11 +108,11 @@ static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
         c->last_event = ZOOM_EVENT_NONE;
         return 0;
     }
-    assert (c->m_queue_back);
+    assert(c->m_queue_back);
     c->m_queue_front = event->prev;
     if (c->m_queue_front)
     {
-        assert (c->m_queue_back);
+        assert(c->m_queue_back);
         c->m_queue_front->next = 0;
     }
     else
@@ -118,14 +121,29 @@ static ZOOM_Event ZOOM_connection_get_event(ZOOM_connection c)
     return event;
 }
 
+static void ZOOM_connection_remove_events(ZOOM_connection c)
+{
+    ZOOM_Event event;
+    while ((event = ZOOM_connection_get_event(c)))
+        ZOOM_Event_destroy(event);
+}
+
+ZOOM_API(int) ZOOM_connection_peek_event(ZOOM_connection c)
+{
+    ZOOM_Event event = c->m_queue_front;
+
+    return event ? event->kind : ZOOM_EVENT_NONE;
+}
 
-static void set_dset_error (ZOOM_connection c, int error,
-                            const char *dset,
-                            const char *addinfo, const char *addinfo2)
+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)
 {
     char *cp;
 
-    xfree (c->addinfo);
+    xfree(c->addinfo);
     c->addinfo = 0;
     c->error = error;
     if (!c->diagset || strcmp(dset, c->diagset))
@@ -134,7 +152,7 @@ static void set_dset_error (ZOOM_connection c, int error,
         c->diagset = xstrdup(dset);
         /* remove integer part from SRW diagset .. */
         if ((cp = strrchr(c->diagset, '/')))
-           *cp = '\0';
+            *cp = '\0';
     }
     if (addinfo && addinfo2)
     {
@@ -144,28 +162,49 @@ static void set_dset_error (ZOOM_connection c, int error,
     }
     else if (addinfo)
         c->addinfo = xstrdup(addinfo);
-    if (error)
+    if (error != ZOOM_ERROR_NONE)
+    {
         yaz_log(log_api, "%p set_dset_error %s %s:%d %s %s",
                 c, c->host_port ? c->host_port : "<>", dset, error,
                 addinfo ? addinfo : "",
                 addinfo2 ? addinfo2 : "");
+        ZOOM_connection_remove_tasks(c);
+    }
+}
+
+static int uri_to_code(const char *uri)
+{
+    int code = 0;       
+    const char *cp;
+    if ((cp = strrchr(uri, '/')))
+        code = atoi(cp+1);
+    return code;
 }
 
-#if HAVE_XML2
-static void set_HTTP_error (ZOOM_connection c, int error,
-                            const char *addinfo, const char *addinfo2)
+#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)
+
+static void set_ZOOM_error(ZOOM_connection c, int error,
+                           const char *addinfo)
 {
     set_dset_error(c, error, "ZOOM", addinfo, 0);
 }
 
-static void clear_error (ZOOM_connection c)
+static void clear_error(ZOOM_connection c)
 {
     /*
      * If an error is tied to an operation then it's ok to clear: for
@@ -174,6 +213,8 @@ static void clear_error (ZOOM_connection c)
      * or Init Refused are not cleared, because they are not
      * recoverable: doing another search doesn't help.
      */
+
+    ZOOM_connection_remove_events(c);
     switch (c->error)
     {
     case ZOOM_ERROR_CONNECT:
@@ -189,33 +230,65 @@ static void clear_error (ZOOM_connection c)
     }
 }
 
-ZOOM_task ZOOM_connection_add_task (ZOOM_connection c, int which)
+void ZOOM_connection_show_task(ZOOM_task task)
+{
+    switch(task->which)
+    {
+    case ZOOM_TASK_SEARCH:
+        yaz_log(YLOG_LOG, "search p=%p", task);
+        break;
+    case ZOOM_TASK_RETRIEVE:
+        yaz_log(YLOG_LOG, "retrieve p=%p", task);
+        break;
+    case ZOOM_TASK_CONNECT:
+        yaz_log(YLOG_LOG, "connect p=%p", task);
+        break;
+    case ZOOM_TASK_SCAN:
+        yaz_log(YLOG_LOG, "scant p=%p", task);
+        break;
+    }
+}
+
+void ZOOM_connection_show_tasks(ZOOM_connection c)
+{
+    ZOOM_task task;
+    yaz_log(YLOG_LOG, "connection p=%p tasks", c);
+    for (task = c->tasks; task; task = task->next)
+        ZOOM_connection_show_task(task);
+}
+
+ZOOM_task ZOOM_connection_add_task(ZOOM_connection c, int which)
 {
     ZOOM_task *taskp = &c->tasks;
     while (*taskp)
         taskp = &(*taskp)->next;
-    *taskp = (ZOOM_task) xmalloc (sizeof(**taskp));
+    *taskp = (ZOOM_task) xmalloc(sizeof(**taskp));
     (*taskp)->running = 0;
     (*taskp)->which = which;
     (*taskp)->next = 0;
-    clear_error (c);
+    clear_error(c);
     return *taskp;
 }
 
-ZOOM_task ZOOM_connection_insert_task (ZOOM_connection c, int which)
+ZOOM_API(int) ZOOM_connection_is_idle(ZOOM_connection c)
 {
-    ZOOM_task task = (ZOOM_task) xmalloc (sizeof(*task));
+    return c->tasks ? 0 : 1;
+}
+
+ZOOM_task ZOOM_connection_insert_task(ZOOM_connection c, int which)
+{
+    ZOOM_task task = (ZOOM_task) xmalloc(sizeof(*task));
 
     task->next = c->tasks;
     c->tasks = task;
 
     task->running = 0;
     task->which = which;
-    clear_error (c);
+    clear_error(c);
     return task;
 }
 
-void ZOOM_connection_remove_task (ZOOM_connection c)
+void ZOOM_connection_remove_task(ZOOM_connection c)
 {
     ZOOM_task task = c->tasks;
 
@@ -225,44 +298,56 @@ void ZOOM_connection_remove_task (ZOOM_connection c)
         switch (task->which)
         {
         case ZOOM_TASK_SEARCH:
-            ZOOM_resultset_destroy (task->u.search.resultset);
+            resultset_destroy(task->u.search.resultset);
+            xfree(task->u.search.syntax);
+            xfree(task->u.search.elementSetName);
             break;
         case ZOOM_TASK_RETRIEVE:
-            ZOOM_resultset_destroy (task->u.retrieve.resultset);
+            resultset_destroy(task->u.retrieve.resultset);
+            xfree(task->u.retrieve.syntax);
+            xfree(task->u.retrieve.elementSetName);
             break;
         case ZOOM_TASK_CONNECT:
             break;
         case ZOOM_TASK_SCAN:
-            ZOOM_scanset_destroy (task->u.scan.scan);
+            ZOOM_scanset_destroy(task->u.scan.scan);
             break;
         case ZOOM_TASK_PACKAGE:
-            ZOOM_package_destroy (task->u.package);
+            ZOOM_package_destroy(task->u.package);
             break;
         case ZOOM_TASK_SORT:
-            ZOOM_resultset_destroy (task->u.sort.resultset);
+            resultset_destroy(task->u.sort.resultset);
             ZOOM_query_destroy(task->u.sort.q);
             break;
         default:
-            assert (0);
+            assert(0);
+        }
+        xfree(task);
+
+        if (!c->tasks)
+        {
+            ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_END);
+            ZOOM_connection_put_event(c, event);
         }
-        xfree (task);
     }
 }
 
-static int ZOOM_connection_exec_task (ZOOM_connection c);
+static int ZOOM_connection_exec_task(ZOOM_connection c);
 
-void ZOOM_connection_remove_tasks (ZOOM_connection c)
+void ZOOM_connection_remove_tasks(ZOOM_connection c)
 {
     while (c->tasks)
         ZOOM_connection_remove_task(c);
 }
 
-static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos);
+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)
+    ZOOM_connection_create(ZOOM_options options)
 {
-    ZOOM_connection c = (ZOOM_connection) xmalloc (sizeof(*c));
+    ZOOM_connection c = (ZOOM_connection) xmalloc(sizeof(*c));
 
     initlog();
 
@@ -270,7 +355,7 @@ ZOOM_connection_create (ZOOM_options options)
 
     c->proto = PROTO_Z3950;
     c->cs = 0;
-    c->mask = 0;
+    ZOOM_connection_set_mask(c, 0);
     c->reconnect_ok = 0;
     c->state = STATE_IDLE;
     c->addinfo = 0;
@@ -295,8 +380,15 @@ ZOOM_connection_create (ZOOM_options options)
     c->client_IP = 0;
     c->tasks = 0;
 
-    c->odr_in = odr_createmem (ODR_DECODE);
-    c->odr_out = odr_createmem (ODR_ENCODE);
+    c->user = 0;
+    c->group = 0;
+    c->password = 0;
+
+    c->maximum_record_size = 0;
+    c->preferred_message_size = 0;
+
+    c->odr_in = odr_createmem(ODR_DECODE);
+    c->odr_out = odr_createmem(ODR_ENCODE);
 
     c->async = 0;
     c->support_named_resultsets = 0;
@@ -304,20 +396,23 @@ ZOOM_connection_create (ZOOM_options options)
 
     c->m_queue_front = 0;
     c->m_queue_back = 0;
+
+    c->sru_version = 0;
     return c;
 }
 
+
 /* 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)
+static char **set_DatabaseNames(ZOOM_connection con, ZOOM_options options,
+                                int *num, ODR odr)
 {
     char **databaseNames;
-    const char *cp = ZOOM_options_get (options, "databaseName");
+    const char *cp = ZOOM_options_get(options, "databaseName");
     
-    if (!cp || !*cp)
+    if ((!cp || !*cp) && con->host_port)
     {
-        if (strncmp (con->host_port, "unix:", 5) == 0)
+        if (strncmp(con->host_port, "unix:", 5) == 0)
             cp = strchr(con->host_port+5, ':');
         else
             cp = strchr(con->host_port, '/');
@@ -326,22 +421,35 @@ static char **set_DatabaseNames (ZOOM_connection con, ZOOM_options options,
     }
     if (!cp)
         cp = "Default";
-    nmem_strsplit(con->odr_out->mem, "+", cp,  &databaseNames, num);
+    nmem_strsplit(odr_getmem(odr), "+", cp,  &databaseNames, num);
     return databaseNames;
 }
 
 ZOOM_API(ZOOM_connection)
-ZOOM_connection_new (const char *host, int portnum)
+    ZOOM_connection_new(const char *host, int portnum)
 {
-    ZOOM_connection c = ZOOM_connection_create (0);
+    ZOOM_connection c = ZOOM_connection_create(0);
 
-    ZOOM_connection_connect (c, host, portnum);
+    ZOOM_connection_connect(c, host, portnum);
     return c;
 }
 
+static zoom_sru_mode get_sru_mode_from_string(const char *s)
+{
+    if (!s || !*s)
+        return zoom_sru_soap;
+    if (!yaz_matchstr(s, "soap"))
+        return zoom_sru_soap;
+    else if (!yaz_matchstr(s, "get"))
+        return zoom_sru_get;
+    else if (!yaz_matchstr(s, "post"))
+        return zoom_sru_post;
+    return zoom_sru_error;
+}
+
 ZOOM_API(void)
-ZOOM_connection_connect(ZOOM_connection c,
-                        const char *host, int portnum)
+    ZOOM_connection_connect(ZOOM_connection c,
+                            const char *host, int portnum)
 {
     const char *val;
     ZOOM_task task;
@@ -349,7 +457,10 @@ ZOOM_connection_connect(ZOOM_connection c,
     initlog();
 
     yaz_log(log_api, "%p ZOOM_connection_connect host=%s portnum=%d",
-            c, host, portnum);
+            c, host ? host : "null", portnum);
+
+    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
+    ZOOM_connection_remove_tasks(c);
 
     if (c->cs)
     {
@@ -358,45 +469,46 @@ ZOOM_connection_connect(ZOOM_connection c,
         return;
     }
     yaz_log(log_details, "%p ZOOM_connection_connect connect", c);
-    xfree (c->proxy);
-    val = ZOOM_options_get (c->options, "proxy");
+    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);
-        c->proxy = xstrdup (val);
+        c->proxy = xstrdup(val);
     }
-    else
-        c->proxy = 0;
 
-    xfree (c->charset);
-    val = ZOOM_options_get (c->options, "charset");
+    xfree(c->charset);
+    c->charset = 0;
+    val = ZOOM_options_get(c->options, "charset");
     if (val && *val)
     {
         yaz_log(log_details, "%p ZOOM_connection_connect charset=%s", c, val);
-        c->charset = xstrdup (val);
+        c->charset = xstrdup(val);
     }
-    else
-        c->charset = 0;
 
-    xfree (c->lang);
-    val = ZOOM_options_get (c->options, "lang");
+    xfree(c->lang);
+    val = ZOOM_options_get(c->options, "lang");
     if (val && *val)
     {
         yaz_log(log_details, "%p ZOOM_connection_connect lang=%s", c, val);
-        c->lang = xstrdup (val);
+        c->lang = xstrdup(val);
     }
     else
         c->lang = 0;
 
-    xfree (c->host_port);
-    if (portnum)
+    if (host)
     {
-        char hostn[128];
-        sprintf (hostn, "%.80s:%d", host, portnum);
-        c->host_port = xstrdup(hostn);
-    }
-    else
-        c->host_port = xstrdup(host);
+        xfree(c->host_port);
+        if (portnum)
+        {
+            char hostn[128];
+            sprintf(hostn, "%.80s:%d", host, portnum);
+            c->host_port = xstrdup(hostn);
+        }
+        else
+            c->host_port = xstrdup(host);
+    }        
 
     {
         /*
@@ -429,54 +541,96 @@ ZOOM_connection_connect(ZOOM_connection c,
         }
     }
 
+    val = ZOOM_options_get(c->options, "sru");
+    c->sru_mode = get_sru_mode_from_string(val);
+
+    xfree(c->sru_version);
+    val = ZOOM_options_get(c->options, "sru_version");
+    c->sru_version = xstrdup(val ? val : "1.2");
+
     ZOOM_options_set(c->options, "host", c->host_port);
 
-    val = ZOOM_options_get (c->options, "cookie");
+    xfree(c->cookie_out);
+    c->cookie_out = 0;
+    val = ZOOM_options_get(c->options, "cookie");
     if (val && *val)
     { 
         yaz_log(log_details, "%p ZOOM_connection_connect cookie=%s", c, val);
-        c->cookie_out = xstrdup (val);
+        c->cookie_out = xstrdup(val);
     }
 
-    val = ZOOM_options_get (c->options, "clientIP");
+    xfree(c->client_IP);
+    c->client_IP = 0;
+    val = ZOOM_options_get(c->options, "clientIP");
     if (val && *val)
     {
         yaz_log(log_details, "%p ZOOM_connection_connect clientIP=%s",
                 c, val);
-        c->client_IP = xstrdup (val);
+        c->client_IP = xstrdup(val);
     }
 
-    c->async = ZOOM_options_get_bool (c->options, "async", 0);
+    xfree(c->group);
+    c->group = 0;
+    val = ZOOM_options_get(c->options, "group");
+    if (val && *val)
+        c->group = xstrdup(val);
+
+    xfree(c->user);
+    c->user = 0;
+    val = ZOOM_options_get(c->options, "user");
+    if (val && *val)
+        c->user = xstrdup(val);
+
+    xfree(c->password);
+    c->password = 0;
+    val = ZOOM_options_get(c->options, "password");
+    if (!val)
+        val = ZOOM_options_get(c->options, "pass");
+
+    if (val && *val)
+        c->password = xstrdup(val);
+    
+    c->maximum_record_size =
+        ZOOM_options_get_int(c->options, "maximumRecordSize", 1024*1024);
+    c->preferred_message_size =
+        ZOOM_options_get_int(c->options, "preferredMessageSize", 1024*1024);
+
+    c->async = ZOOM_options_get_bool(c->options, "async", 0);
+    if (ZOOM_options_get_bool(c->options, "apdulog", 0))
+    {
+        c->odr_print = odr_createmem(ODR_PRINT);
+        odr_setprint(c->odr_print, yaz_log_file());
+    }
+    else
+        c->odr_print = 0;
     yaz_log(log_details, "%p ZOOM_connection_connect async=%d", c, c->async);
  
-    set_ZOOM_error(c, ZOOM_ERROR_NONE, 0);
-
-    task = ZOOM_connection_add_task (c, ZOOM_TASK_CONNECT);
+    task = ZOOM_connection_add_task(c, ZOOM_TASK_CONNECT);
 
     if (!c->async)
     {
-        while (ZOOM_event (1, &c))
+        while (ZOOM_event(1, &c))
             ;
     }
 }
 
 ZOOM_API(ZOOM_query)
-ZOOM_query_create(void)
+    ZOOM_query_create(void)
 {
-    ZOOM_query s = (ZOOM_query) xmalloc (sizeof(*s));
+    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->odr = odr_createmem(ODR_ENCODE);
     s->query_string = 0;
 
     return s;
 }
 
 ZOOM_API(void)
-ZOOM_query_destroy(ZOOM_query s)
+    ZOOM_query_destroy(ZOOM_query s)
 {
     if (!s)
         return;
@@ -485,18 +639,18 @@ ZOOM_query_destroy(ZOOM_query s)
     yaz_log(log_details, "%p ZOOM_query_destroy count=%d", s, s->refcount);
     if (s->refcount == 0)
     {
-        odr_destroy (s->odr);
-        xfree (s);
+        odr_destroy(s->odr);
+        xfree(s);
     }
 }
 
 ZOOM_API(int)
-ZOOM_query_prefix(ZOOM_query s, const char *str)
+    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 = (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, PROTO_Z3950, str);
+    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);
@@ -508,20 +662,20 @@ ZOOM_query_prefix(ZOOM_query s, const char *str)
 }
 
 ZOOM_API(int)
-ZOOM_query_cql(ZOOM_query s, const char *str)
+    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_getoidbystr(s->odr, "1.2.840.10003.16.2");
+    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 = (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;
 
@@ -538,16 +692,20 @@ ZOOM_query_cql(ZOOM_query s, const char *str)
  * 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)
+    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 = ZOOM_connection_create(0);
+        conn = freeme = ZOOM_connection_create(0);
 
-    if ((rpn = cql2pqf(conn, str)) == 0)
+    rpn = cql2pqf(conn, str);
+    if (freeme != 0)
+        ZOOM_connection_destroy(freeme);
+    if (rpn == 0)
         return -1;
 
     ret = ZOOM_query_prefix(s, rpn);
@@ -555,10 +713,45 @@ ZOOM_query_cql2rpn(ZOOM_query s, const char *str, ZOOM_connection conn)
     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)
+    ZOOM_query_sortby(ZOOM_query s, const char *criteria)
 {
-    s->sort_spec = yaz_sort_spec (s->odr, 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",
@@ -572,36 +765,46 @@ ZOOM_query_sortby(ZOOM_query s, const char *criteria)
 static zoom_ret do_write(ZOOM_connection c);
 
 ZOOM_API(void)
-ZOOM_connection_destroy(ZOOM_connection c)
+    ZOOM_connection_destroy(ZOOM_connection c)
 {
     ZOOM_resultset r;
     if (!c)
         return;
     yaz_log(log_api, "%p ZOOM_connection_destroy", c);
     if (c->cs)
-        cs_close (c->cs);
+        cs_close(c->cs);
     for (r = c->resultsets; r; r = r->next)
         r->connection = 0;
 
-    xfree (c->buf_in);
-    xfree (c->addinfo);
-    xfree (c->diagset);
-    odr_destroy (c->odr_in);
-    odr_destroy (c->odr_out);
-    ZOOM_options_destroy (c->options);
-    ZOOM_connection_remove_tasks (c);
-    xfree (c->host_port);
-    xfree (c->path);
-    xfree (c->proxy);
-    xfree (c->charset);
-    xfree (c->lang);
-    xfree (c->cookie_out);
-    xfree (c->cookie_in);
-    xfree (c->client_IP);
-    xfree (c);
-}
-
-void ZOOM_resultset_addref (ZOOM_resultset r)
+    xfree(c->buf_in);
+    xfree(c->addinfo);
+    xfree(c->diagset);
+    odr_destroy(c->odr_in);
+    odr_destroy(c->odr_out);
+    if (c->odr_print)
+    {
+        odr_setprint(c->odr_print, 0); /* prevent destroy from fclose'ing */
+        odr_destroy(c->odr_print);
+    }
+    ZOOM_options_destroy(c->options);
+    ZOOM_connection_remove_tasks(c);
+    ZOOM_connection_remove_events(c);
+    xfree(c->host_port);
+    xfree(c->path);
+    xfree(c->proxy);
+    xfree(c->charset);
+    xfree(c->lang);
+    xfree(c->cookie_out);
+    xfree(c->cookie_in);
+    xfree(c->client_IP);
+    xfree(c->user);
+    xfree(c->group);
+    xfree(c->password);
+    xfree(c->sru_version);
+    xfree(c);
+}
+
+void ZOOM_resultset_addref(ZOOM_resultset r)
 {
     if (r)
     {
@@ -611,77 +814,86 @@ void ZOOM_resultset_addref (ZOOM_resultset r)
     }
 }
 
-ZOOM_resultset ZOOM_resultset_create ()
+ZOOM_resultset ZOOM_resultset_create(void)
 {
-    ZOOM_resultset r = (ZOOM_resultset) xmalloc (sizeof(*r));
+    int i;
+    ZOOM_resultset r = (ZOOM_resultset) xmalloc(sizeof(*r));
 
     initlog();
 
     yaz_log(log_details, "%p ZOOM_resultset_create", r);
     r->refcount = 1;
     r->size = 0;
-    r->odr = odr_createmem (ODR_ENCODE);
-    r->start = 0;
+    r->odr = odr_createmem(ODR_ENCODE);
     r->piggyback = 1;
     r->setname = 0;
     r->schema = 0;
-    r->count = 0;
     r->step = 0;
-    r->record_cache = 0;
+    for (i = 0; i<RECORD_HASH_SIZE; i++)
+        r->record_hash[i] = 0;
     r->r_sort_spec = 0;
     r->query = 0;
     r->connection = 0;
     r->next = 0;
+    r->databaseNames = 0;
+    r->num_databaseNames = 0;
     return r;
 }
 
 ZOOM_API(ZOOM_resultset)
-ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
+    ZOOM_connection_search_pqf(ZOOM_connection c, const char *q)
 {
     ZOOM_resultset r;
     ZOOM_query s = ZOOM_query_create();
 
-    ZOOM_query_prefix (s, q);
+    ZOOM_query_prefix(s, q);
 
-    r = ZOOM_connection_search (c, s);
-    ZOOM_query_destroy (s);
+    r = ZOOM_connection_search(c, s);
+    ZOOM_query_destroy(s);
     return r;
 }
 
 ZOOM_API(ZOOM_resultset)
-ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
+    ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
 {
-    ZOOM_resultset r = ZOOM_resultset_create ();
+    ZOOM_resultset r = ZOOM_resultset_create();
     ZOOM_task task;
     const char *cp;
+    int start, count;
+    const char *syntax, *elementSetName;
 
     yaz_log(log_api, "%p ZOOM_connection_search set %p query %p", c, r, q);
     r->r_sort_spec = q->sort_spec;
     r->query = q;
 
     r->options = ZOOM_options_create_with_parent(c->options);
-
-    r->start = ZOOM_options_get_int(r->options, "start", 0);
-    r->count = ZOOM_options_get_int(r->options, "count", 0);
+    
+    start = ZOOM_options_get_int(r->options, "start", 0);
+    count = ZOOM_options_get_int(r->options, "count", 0);
     {
         /* If "presentChunk" is defined use that; otherwise "step" */
-        const char *cp = ZOOM_options_get (r->options, "presentChunk");
+        const char *cp = ZOOM_options_get(r->options, "presentChunk");
         r->step = ZOOM_options_get_int(r->options,
                                        (cp != 0 ? "presentChunk": "step"), 0);
     }
-    r->piggyback = ZOOM_options_get_bool (r->options, "piggyback", 1);
-    cp = ZOOM_options_get (r->options, "setname");
+    r->piggyback = ZOOM_options_get_bool(r->options, "piggyback", 1);
+    cp = ZOOM_options_get(r->options, "setname");
     if (cp)
         r->setname = xstrdup(cp);
-    cp = ZOOM_options_get (r->options, "schema");
+    cp = ZOOM_options_get(r->options, "schema");
     if (cp)
         r->schema = xstrdup(cp);
+
+    r->databaseNames = set_DatabaseNames(c, c->options, &r->num_databaseNames,
+                                         r->odr);
     
     r->connection = c;
 
     r->next = c->resultsets;
     c->resultsets = r;
 
+    
+
     if (c->host_port && c->proto == PROTO_HTTP)
     {
         if (!c->cs)
@@ -696,35 +908,39 @@ ZOOM_connection_search(ZOOM_connection c, ZOOM_query q)
         }
     }
 
-    task = ZOOM_connection_add_task (c, ZOOM_TASK_SEARCH);
+    task = ZOOM_connection_add_task(c, ZOOM_TASK_SEARCH);
     task->u.search.resultset = r;
-    ZOOM_resultset_addref (r);  
+    task->u.search.start = start;
+    task->u.search.count = count;
+
+    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
+    task->u.search.syntax = syntax ? xstrdup(syntax) : 0;
+    elementSetName = ZOOM_options_get(r->options, "elementSetName");
+    task->u.search.elementSetName = elementSetName 
+        ? xstrdup(elementSetName) : 0;
+   
+    ZOOM_resultset_addref(r);
 
     (q->refcount)++;
 
     if (!c->async)
     {
-        while (ZOOM_event (1, &c))
+        while (ZOOM_event(1, &c))
             ;
     }
     return r;
 }
 
-/*
- * This is the old result-set sorting API, which is maintained only
- * for the sake of binary compatibility.  There is no reason ever to
- * use this rather than ZOOM_resultset_sort1().
- */
 ZOOM_API(void)
-ZOOM_resultset_sort(ZOOM_resultset r,
-                    const char *sort_type, const char *sort_spec)
+    ZOOM_resultset_sort(ZOOM_resultset r,
+                         const char *sort_type, const char *sort_spec)
 {
     (void) ZOOM_resultset_sort1(r, sort_type, sort_spec);
 }
 
 ZOOM_API(int)
-ZOOM_resultset_sort1(ZOOM_resultset r,
-                     const char *sort_type, const char *sort_spec)
+    ZOOM_resultset_sort1(ZOOM_resultset r,
+                         const char *sort_type, const char *sort_spec)
 {
     ZOOM_connection c = r->connection;
     ZOOM_task task;
@@ -755,15 +971,15 @@ ZOOM_resultset_sort1(ZOOM_resultset r,
     }
     
     ZOOM_resultset_cache_reset(r);
-    task = ZOOM_connection_add_task (c, ZOOM_TASK_SORT);
+    task = ZOOM_connection_add_task(c, ZOOM_TASK_SORT);
     task->u.sort.resultset = r;
     task->u.sort.q = newq;
 
-    ZOOM_resultset_addref (r);  
+    ZOOM_resultset_addref(r);  
 
     if (!c->async)
     {
-        while (ZOOM_event (1, &c))
+        while (ZOOM_event(1, &c))
             ;
     }
 
@@ -773,22 +989,30 @@ ZOOM_resultset_sort1(ZOOM_resultset r,
 ZOOM_API(void)
     ZOOM_resultset_cache_reset(ZOOM_resultset r)
 {
-    ZOOM_record_cache rc;
-    
-    for (rc = r->record_cache; rc; rc = rc->next)
+    int i;
+    for (i = 0; i<RECORD_HASH_SIZE; i++)
     {
-        if (rc->rec.wrbuf_marc)
-            wrbuf_free (rc->rec.wrbuf_marc, 1);
-        if (rc->rec.wrbuf_iconv)
-            wrbuf_free (rc->rec.wrbuf_iconv, 1);
-        if (rc->rec.wrbuf_opac)
-            wrbuf_free (rc->rec.wrbuf_opac, 1);
+        ZOOM_record_cache rc;
+        for (rc = r->record_hash[i]; rc; rc = rc->next)
+        {
+            if (rc->rec.wrbuf_marc)
+                wrbuf_destroy(rc->rec.wrbuf_marc);
+            if (rc->rec.wrbuf_iconv)
+                wrbuf_destroy(rc->rec.wrbuf_iconv);
+            if (rc->rec.wrbuf_opac)
+                wrbuf_destroy(rc->rec.wrbuf_opac);
+        }
+        r->record_hash[i] = 0;
     }
-    r->record_cache = 0;
 }
 
 ZOOM_API(void)
-ZOOM_resultset_destroy(ZOOM_resultset r)
+    ZOOM_resultset_destroy(ZOOM_resultset r)
+{
+    resultset_destroy(r);
+}
+
+static void resultset_destroy(ZOOM_resultset r)
 {
     if (!r)
         return;
@@ -805,7 +1029,7 @@ ZOOM_resultset_destroy(ZOOM_resultset r)
             ZOOM_resultset *rp = &r->connection->resultsets;
             while (1)
             {
-                assert (*rp);   /* we must be in this list!! */
+                assert(*rp);   /* we must be in this list!! */
                 if (*rp == r)
                 {   /* OK, we're here - take us out of it */
                     *rp = (*rp)->next;
@@ -814,36 +1038,56 @@ ZOOM_resultset_destroy(ZOOM_resultset r)
                 rp = &(*rp)->next;
             }
         }
-        ZOOM_query_destroy (r->query);
-        ZOOM_options_destroy (r->options);
-        odr_destroy (r->odr);
-        xfree (r->setname);
-        xfree (r->schema);
-        xfree (r);
+        ZOOM_query_destroy(r->query);
+        ZOOM_options_destroy(r->options);
+        odr_destroy(r->odr);
+        xfree(r->setname);
+        xfree(r->schema);
+        xfree(r);
     }
 }
 
 ZOOM_API(size_t)
-ZOOM_resultset_size (ZOOM_resultset r)
+    ZOOM_resultset_size(ZOOM_resultset r)
 {
+    yaz_log(log_details, "ZOOM_resultset_size r=%p count=%d",
+            r, r->size);
     return r->size;
 }
 
-static void do_close (ZOOM_connection c)
+static void do_close(ZOOM_connection c)
 {
     if (c->cs)
         cs_close(c->cs);
     c->cs = 0;
-    c->mask = 0;
+    ZOOM_connection_set_mask(c, 0);
     c->state = STATE_IDLE;
 }
 
-static void ZOOM_resultset_retrieve (ZOOM_resultset r,
-                                     int force_sync, int start, int count)
+static int ZOOM_test_reconnect(ZOOM_connection c)
+{
+    ZOOM_Event event;
+
+    if (!c->reconnect_ok)
+        return 0;
+    do_close(c);
+    c->reconnect_ok = 0;
+    c->tasks->running = 0;
+    ZOOM_connection_insert_task(c, ZOOM_TASK_CONNECT);
+
+    event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
+    ZOOM_connection_put_event(c, event);
+
+    return 1;
+}
+
+static void ZOOM_resultset_retrieve(ZOOM_resultset r,
+                                    int force_sync, int start, int count)
 {
     ZOOM_task task;
     ZOOM_connection c;
     const char *cp;
+    const char *syntax, *elementSetName;
 
     if (!r)
         return;
@@ -867,12 +1111,18 @@ static void ZOOM_resultset_retrieve (ZOOM_resultset r,
             c->reconnect_ok = 1;
         }
     }
-    task = ZOOM_connection_add_task (c, ZOOM_TASK_RETRIEVE);
+    task = ZOOM_connection_add_task(c, ZOOM_TASK_RETRIEVE);
     task->u.retrieve.resultset = r;
     task->u.retrieve.start = start;
     task->u.retrieve.count = count;
 
-    cp = ZOOM_options_get (r->options, "schema");
+    syntax = ZOOM_options_get(r->options, "preferredRecordSyntax"); 
+    task->u.retrieve.syntax = syntax ? xstrdup(syntax) : 0;
+    elementSetName = ZOOM_options_get(r->options, "elementSetName");
+    task->u.retrieve.elementSetName = elementSetName 
+        ? xstrdup(elementSetName) : 0;
+
+    cp = ZOOM_options_get(r->options, "schema");
     if (cp)
     {
         if (!r->schema || strcmp(r->schema, cp))
@@ -882,31 +1132,31 @@ static void ZOOM_resultset_retrieve (ZOOM_resultset r,
         }
     }
 
-    ZOOM_resultset_addref (r);
+    ZOOM_resultset_addref(r);
 
     if (!r->connection->async || force_sync)
-        while (r->connection && ZOOM_event (1, &r->connection))
+        while (r->connection && ZOOM_event(1, &r->connection))
             ;
 }
 
 ZOOM_API(void)
-ZOOM_resultset_records (ZOOM_resultset r, ZOOM_record *recs,
-                        size_t start, size_t count)
+    ZOOM_resultset_records(ZOOM_resultset r, ZOOM_record *recs,
+                           size_t start, size_t count)
 {
     int force_present = 0;
 
     if (!r)
         return ;
-    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%d count=%d",
-            r, r, start, count);
+    yaz_log(log_api, "%p ZOOM_resultset_records r=%p start=%ld count=%ld",
+            r, r, (long) start, (long) count);
     if (count && recs)
         force_present = 1;
-    ZOOM_resultset_retrieve (r, force_present, start, count);
+    ZOOM_resultset_retrieve(r, force_present, start, count);
     if (force_present)
     {
         size_t i;
         for (i = 0; i< count; i++)
-            recs[i] = ZOOM_resultset_record_immediate (r, i+start);
+            recs[i] = ZOOM_resultset_record_immediate(r, i+start);
     }
 }
 
@@ -923,7 +1173,7 @@ static void get_cert(ZOOM_connection c)
     }
 }
 
-static zoom_ret do_connect (ZOOM_connection c)
+static zoom_ret do_connect(ZOOM_connection c)
 {
     void *add;
     const char *effective_host;
@@ -937,11 +1187,11 @@ static zoom_ret do_connect (ZOOM_connection c)
 
     if (c->cs)
         cs_close(c->cs);
-    c->cs = cs_create_host (effective_host, 0, &add);
+    c->cs = cs_create_host(effective_host, 0, &add);
 
     if (c->cs && c->cs->protocol == PROTO_HTTP)
     {
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
         const char *path = 0;
 
         c->proto = PROTO_HTTP;
@@ -949,7 +1199,7 @@ static zoom_ret do_connect (ZOOM_connection c)
         xfree(c->path);
         c->path = (char*) xmalloc(strlen(path)+2);
         c->path[0] = '/';
-        strcpy (c->path+1, path);
+        strcpy(c->path+1, path);
 #else
         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_PROTOCOL, "SRW");
         do_close(c);
@@ -958,7 +1208,7 @@ static zoom_ret do_connect (ZOOM_connection c)
     }
     if (c->cs)
     {
-        int ret = cs_connect (c->cs, add);
+        int ret = cs_connect(c->cs, add);
         if (ret == 0)
         {
             ZOOM_Event event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
@@ -969,22 +1219,23 @@ static zoom_ret do_connect (ZOOM_connection c)
             else
             {
                 /* no init request for SRW .. */
-                assert (c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task (c);
-                c->mask = 0;
-                ZOOM_connection_exec_task (c);
+                assert(c->tasks->which == ZOOM_TASK_CONNECT);
+                ZOOM_connection_remove_task(c);
+                ZOOM_connection_set_mask(c, 0);
+                ZOOM_connection_exec_task(c);
             }
             c->state = STATE_ESTABLISHED;
             return zoom_pending;
         }
         else if (ret > 0)
         {
-            c->state = STATE_CONNECTING; 
-            c->mask = ZOOM_SELECT_EXCEPT;
+            int mask = ZOOM_SELECT_EXCEPT;
             if (c->cs->io_pending & CS_WANT_WRITE)
-                c->mask += ZOOM_SELECT_WRITE;
+                mask += ZOOM_SELECT_WRITE;
             if (c->cs->io_pending & CS_WANT_READ)
-                c->mask += ZOOM_SELECT_READ;
+                mask += ZOOM_SELECT_READ;
+            ZOOM_connection_set_mask(c, mask);
+            c->state = STATE_CONNECTING; 
             return zoom_pending;
         }
     }
@@ -993,34 +1244,20 @@ static zoom_ret do_connect (ZOOM_connection c)
     return zoom_complete;
 }
 
-int z3950_connection_socket(ZOOM_connection c)
-{
-    if (c->cs)
-        return cs_fileno(c->cs);
-    return -1;
-}
-
-int z3950_connection_mask(ZOOM_connection c)
-{
-    if (c->cs)
-        return c->mask;
-    return 0;
-}
-
-static void otherInfo_attach (ZOOM_connection c, Z_APDU *a, ODR out)
+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;
-        int oidval;
 
-        sprintf (buf, "otherInfo%d", i);
-        val = ZOOM_options_get (c->options, buf);
+        sprintf(buf, "otherInfo%d", i);
+        val = ZOOM_options_get(c->options, buf);
         if (!val)
             break;
         cp = strchr(val, ':');
@@ -1029,40 +1266,44 @@ static void otherInfo_attach (ZOOM_connection c, Z_APDU *a, ODR out)
         len = cp - val;
         if (len >= sizeof(buf))
             len = sizeof(buf)-1;
-        memcpy (buf, val, len);
+        memcpy(buf, val, len);
         buf[len] = '\0';
-        oidval = oid_getvalbyname (buf);
-        if (oidval == VAL_NONE)
+        
+        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_oidval(oi, out, oidval, 1, cp+1);
+        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);
+    assert(a);
     if (c->cookie_out)
     {
         Z_OtherInformation **oi;
         yaz_oi_APDU(a, &oi);
-        yaz_oi_set_string_oidval(oi, out, VAL_COOKIE, 1, c->cookie_out);
+        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_oidval(oi, out, VAL_CLIENT_IP, 1, c->client_IP);
+        yaz_oi_set_string_oid(oi, out, yaz_oid_userinfo_client_ip, 
+                              1, c->client_IP);
     }
-    otherInfo_attach (c, a, out);
+    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);
+            fprintf(outf, "a=%p\n", a);
             odr_setprint(odr_pr, outf);
             z_APDU(odr_pr, &a, 0, 0);
             odr_destroy(odr_pr);
@@ -1072,43 +1313,38 @@ static int encode_APDU(ZOOM_connection c, Z_APDU *a, ODR out)
         odr_reset(out);
         return -1;
     }
+    if (c->odr_print)
+        z_APDU(c->odr_print, &a, 0, 0);
     yaz_log(log_details, "%p encoding_APDU encoding OK", c);
     return 0;
 }
 
-static zoom_ret send_APDU (ZOOM_connection c, Z_APDU *a)
+static zoom_ret send_APDU(ZOOM_connection c, Z_APDU *a)
 {
     ZOOM_Event event;
-    assert (a);
+    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);
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
     odr_reset(c->odr_out);
-    return do_write (c);
+    return do_write(c);
 }
 
 /* returns 1 if PDU was sent OK (still pending )
-           0 if PDU was not sent OK (nothing to wait for) 
+   0 if PDU was not sent OK (nothing to wait for) 
 */
 
-static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
+static zoom_ret ZOOM_connection_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));
-    const char *auth_groupId = ZOOM_options_get (c->options, "group");
-    const char *auth_userId = ZOOM_options_get (c->options, "user");
-    const char *auth_password = ZOOM_options_get (c->options, "password");
     char *version;
 
-    /* support the pass for backwards compatibility */
-    if (!auth_password)
-        auth_password = ZOOM_options_get (c->options, "pass");
-        
     ODR_MASK_SET(ireq->options, Z_Options_search);
     ODR_MASK_SET(ireq->options, Z_Options_present);
     ODR_MASK_SET(ireq->options, Z_Options_scan);
@@ -1119,74 +1355,52 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_1);
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_2);
     ODR_MASK_SET(ireq->protocolVersion, Z_ProtocolVersion_3);
-
+    
     /* Index Data's Z39.50 Implementor Id is 81 */
-    ireq->implementationId = odr_prepend(c->odr_out,
-        ZOOM_options_get(c->options, "implementationId"),
-        odr_prepend(c->odr_out, "81", ireq->implementationId));
-
-    ireq->implementationName = odr_prepend(c->odr_out,
-        ZOOM_options_get(c->options, "implementationName"),
-        odr_prepend(c->odr_out, "ZOOM-C", ireq->implementationName));
-
-    version = odr_strdup(c->odr_out, "$Revision: 1.62 $");
+    ireq->implementationId =
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationId"),
+                    odr_prepend(c->odr_out, "81", ireq->implementationId));
+    
+    ireq->implementationName = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationName"),
+                    odr_prepend(c->odr_out, "ZOOM-C",
+                                ireq->implementationName));
+    
+    version = odr_strdup(c->odr_out, "$Revision: 1.154 $");
     if (strlen(version) > 10)   /* check for unexpanded CVS strings */
         version[strlen(version)-2] = '\0';
-    ireq->implementationVersion = odr_prepend(c->odr_out,
-        ZOOM_options_get(c->options, "implementationVersion"),
-        odr_prepend(c->odr_out, &version[11], ireq->implementationVersion));
-
-    *ireq->maximumRecordSize =
-        ZOOM_options_get_int (c->options, "maximumRecordSize", 1024*1024);
-    *ireq->preferredMessageSize =
-        ZOOM_options_get_int (c->options, "preferredMessageSize", 1024*1024);
+    ireq->implementationVersion = 
+        odr_prepend(c->odr_out,
+                    ZOOM_options_get(c->options, "implementationVersion"),
+                    odr_prepend(c->odr_out, &version[11],
+                                ireq->implementationVersion));
+    
+    *ireq->maximumRecordSize = c->maximum_record_size;
+    *ireq->preferredMessageSize = c->preferred_message_size;
     
-    if (auth_groupId || auth_password)
+    if (c->group || c->password)
     {
         Z_IdPass *pass = (Z_IdPass *) odr_malloc(c->odr_out, sizeof(*pass));
-        int i = 0;
-        pass->groupId = 0;
-        if (auth_groupId && *auth_groupId)
-        {
-            pass->groupId = (char *)
-                odr_malloc(c->odr_out, strlen(auth_groupId)+1);
-            strcpy(pass->groupId, auth_groupId);
-            i++;
-        }
-        pass->userId = 0;
-        if (auth_userId && *auth_userId)
-        {
-            pass->userId = (char *)
-                odr_malloc(c->odr_out, strlen(auth_userId)+1);
-            strcpy(pass->userId, auth_userId);
-            i++;
-        }
-        pass->password = 0;
-        if (auth_password && *auth_password)
-        {
-            pass->password = (char *)
-                odr_malloc(c->odr_out, strlen(auth_password)+1);
-            strcpy(pass->password, auth_password);
-            i++;
-        }
-        if (i)
-        {
-            auth->which = Z_IdAuthentication_idPass;
-            auth->u.idPass = pass;
-            ireq->idAuthentication = auth;
-        }
+        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 (auth_userId)
+    else if (c->user)
     {
         auth->which = Z_IdAuthentication_open;
-        auth->u.open = (char *)
-            odr_malloc(c->odr_out, strlen(auth_userId)+1);
-        strcpy(auth->u.open, auth_userId);
+        auth->u.open = odr_strdup(c->odr_out, c->user);
         ireq->idAuthentication = auth;
     }
     if (c->proxy)
-        yaz_oi_set_string_oidval(&ireq->otherInfo, c->odr_out,
-                                 VAL_PROXY, 1, c->host_port);
+    {
+        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;
@@ -1196,154 +1410,119 @@ static zoom_ret ZOOM_connection_send_init (ZOOM_connection c)
         
         if ((oi_unit = yaz_oi_update(oi, c->odr_out, NULL, 0, 0)))
         {
-            char **charsets_addresses = 0;
-            char **langs_addresses = 0;
-            int charsets_count = 0;
-            int langs_count = 0;
-           
-            if (c->charset)
-                nmem_strsplit_blank(c->odr_out->mem, c->charset,
-                                    &charsets_addresses, &charsets_count);
-            if (c->lang)
-                nmem_strsplit_blank(c->odr_out->mem, c->lang,
-                                    &langs_addresses, &langs_count);
             ODR_MASK_SET(ireq->options, Z_Options_negotiationModel);
             oi_unit->which = Z_OtherInfo_externallyDefinedInfo;
             oi_unit->information.externallyDefinedInfo =
-                yaz_set_proposal_charneg(c->odr_out,
-                                         (const char **) charsets_addresses,
-                                         charsets_count,
-                                         (const char **) langs_addresses,
-                                         langs_count, 
-                                         1);
+                yaz_set_proposal_charneg_list(c->odr_out, " ",
+                                              c->charset, c->lang, 1);
         }
     }
-    assert (apdu);
-    return send_APDU (c, apdu);
+    assert(apdu);
+    return send_APDU(c, apdu);
 }
 
-#if HAVE_XML2
-static zoom_ret send_srw (ZOOM_connection c, Z_SRW_PDU *sr)
+#if YAZ_HAVE_XML2
+static zoom_ret send_srw(ZOOM_connection c, Z_SRW_PDU *sr)
 {
-    char ctype[50];
-    Z_SOAP_Handler h[2] = {
-        {"http://www.loc.gov/zing/srw/", 0, (Z_SOAP_fun) yaz_srw_codec},
-        {0, 0, 0}
-    };
-    ODR o = odr_createmem(ODR_ENCODE);
-    int ret;
-    Z_SOAP *p = (Z_SOAP*) odr_malloc(o, sizeof(*p));
     Z_GDU *gdu;
     ZOOM_Event event;
 
-    gdu = z_get_HTTP_Request(c->odr_out);
-    gdu->u.HTTP_Request->path = c->path;
+    gdu = z_get_HTTP_Request_host_path(c->odr_out, c->host_port, c->path);
 
-    if (c->host_port)
+    if (c->sru_mode == zoom_sru_get)
     {
-        const char *cp0 = strstr(c->host_port, "://");
-        const char *cp1 = 0;
-        if (cp0)
-            cp0 = cp0+3;
-        else
-            cp0 = c->host_port;
-
-        cp1 = strchr(cp0, '/');
-        if (!cp1)
-            cp1 = cp0+strlen(cp0);
-
-        if (cp0 && cp1)
-        {
-            char *h = (char*) odr_malloc(c->odr_out, cp1 - cp0 + 1);
-            memcpy (h, cp0, cp1 - cp0);
-            h[cp1-cp0] = '\0';
-            z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
-                              "Host", h);
-        }
+        yaz_sru_get_encode(gdu->u.HTTP_Request, sr, c->odr_out, c->charset);
     }
-
-    strcpy(ctype, "text/xml");
-    if (c->charset && strlen(c->charset) < 20)
+    else if (c->sru_mode == zoom_sru_post)
     {
-        strcat(ctype, "; charset=");
-        strcat(ctype, c->charset);
+        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);
     }
-    z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
-                      "Content-Type", ctype);
-    z_HTTP_header_add(c->odr_out, &gdu->u.HTTP_Request->headers,
-                      "SOAPAction", "\"\"");
-    p->which = Z_SOAP_generic;
-    p->u.generic = (Z_SOAP_Generic *) odr_malloc(o, sizeof(*p->u.generic));
-    p->u.generic->no = 0;
-    p->u.generic->ns = 0;
-    p->u.generic->p = sr;
-    p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
-
-    ret = z_soap_codec_enc(o, &p,
-                           &gdu->u.HTTP_Request->content_buf,
-                           &gdu->u.HTTP_Request->content_len, h,
-                           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);
-
-    odr_destroy(o);
-
-    event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event (c, event);
+        
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
     odr_reset(c->odr_out);
-    return do_write (c);
+    return do_write(c);
 }
 #endif
 
-#if 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;
+}
+
+#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 *recordPacking = 0;
+    const char *option_val = 0;
 
     if (c->error)                  /* don't continue on error */
         return zoom_complete;
-    assert (c->tasks);
-    if (c->tasks->which == ZOOM_TASK_SEARCH)
+    assert(c->tasks);
+    switch(c->tasks->which)
     {
+    case ZOOM_TASK_SEARCH:
         resultset = c->tasks->u.search.resultset;
-        resultset->setname = xstrdup ("default");
-        ZOOM_options_set (resultset->options, "setname", resultset->setname);
-    }
-    else if(c->tasks->which == ZOOM_TASK_RETRIEVE)
-    {
+        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;
 
-        resultset->start = c->tasks->u.retrieve.start;
-        resultset->count = c->tasks->u.retrieve.count;
+        start = &c->tasks->u.retrieve.start;
+        count = &c->tasks->u.retrieve.count;
         
-        if (resultset->start >= resultset->size)
+        if (*start >= resultset->size)
             return zoom_complete;
-        if (resultset->start + resultset->count > resultset->size)
-            resultset->count = resultset->size - resultset->start;
+        if (*start + *count > resultset->size)
+            *count = resultset->size - *start;
 
-        for (i = 0; i<resultset->count; i++)
+        for (i = 0; i < *count; i++)
         {
             ZOOM_record rec =
-                record_cache_lookup (resultset, i + resultset->start);
+                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);
+            }
         }
-        if (i == resultset->count)
+        *start += i;
+        *count -= i;
+
+        if (*count == 0)
             return zoom_complete;
+        break;
+    default:
+        return zoom_complete;
     }
     assert(resultset->query);
         
-    sr = yaz_srw_get(c->odr_out, Z_SRW_searchRetrieve_request);
-
+    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;
     }
@@ -1358,16 +1537,17 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
         set_ZOOM_error(c, ZOOM_ERROR_UNSUPPORTED_QUERY, 0);
         return zoom_complete;
     }
-    sr->u.request->startRecord = odr_intdup (c->odr_out, resultset->start + 1);
-    sr->u.request->maximumRecords = odr_intdup (
-        c->odr_out, resultset->step>0 ? resultset->step : resultset->count);
+    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);
     sr->u.request->recordSchema = resultset->schema;
-
-    recordPacking = ZOOM_resultset_option_get (resultset, "recordPacking");
-
-    if (recordPacking)
-        sr->u.request->recordPacking = odr_strdup(c->odr_out, recordPacking);
     
+    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
@@ -1377,7 +1557,7 @@ static zoom_ret ZOOM_connection_srw_send_search(ZOOM_connection c)
 }
 #endif
 
-static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
+static zoom_ret ZOOM_connection_send_search(ZOOM_connection c)
 {
     ZOOM_resultset r;
     int lslb, ssub, mspn;
@@ -1388,19 +1568,19 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
     const char *smallSetElementSetName;
     const char *mediumSetElementSetName;
 
-    assert (c->tasks);
-    assert (c->tasks->which == ZOOM_TASK_SEARCH);
+    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");
+        ZOOM_options_get(r->options, "elementSetName");
     smallSetElementSetName  =
-        ZOOM_options_get (r->options, "smallSetElementSetName");
+        ZOOM_options_get(r->options, "smallSetElementSetName");
     mediumSetElementSetName =
-        ZOOM_options_get (r->options, "mediumSetElementSetName");
+        ZOOM_options_get(r->options, "mediumSetElementSetName");
 
     if (!smallSetElementSetName)
         smallSetElementSetName = elementSetName;
@@ -1408,8 +1588,8 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
     if (!mediumSetElementSetName)
         mediumSetElementSetName = elementSetName;
 
-    assert (r);
-    assert (r->query);
+    assert(r);
+    assert(r->query);
 
     /* prepare query for the search request */
     search_req->query = r->query->z_query;
@@ -1418,16 +1598,33 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
         set_ZOOM_error(c, ZOOM_ERROR_INVALID_QUERY, 0);
         return zoom_complete;
     }
-
-    search_req->databaseNames =
-        set_DatabaseNames (c, r->options, &search_req->num_databaseNames);
+    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)
+            {
+                search_req->query = yaz_copy_Z_Query(search_req->query,
+                                                     c->odr_out);
+                
+                yaz_query_charset_convert_rpnquery(search_req->query->u.type_1,
+                                                   c->odr_out, cd);
+                yaz_iconv_close(cd);
+            }
+        }
+    }
+    search_req->databaseNames = r->databaseNames;
+    search_req->num_databaseNames = r->num_databaseNames;
 
     /* get syntax (no need to provide unless piggyback is in effect) */
-    syntax = ZOOM_options_get (r->options, "preferredRecordSyntax");
+    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);
+    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 */
@@ -1435,13 +1632,14 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
         *search_req->smallSetUpperBound = ssub;
         *search_req->mediumSetPresentNumber = mspn;
     }
-    else if (r->start == 0 && r->count > 0
+    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 : r->count;
+        *search_req->mediumSetPresentNumber = 
+            r->step>0 ? r->step : c->tasks->u.search.count;
     }
     else
     {
@@ -1453,24 +1651,24 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
     if (smallSetElementSetName && *smallSetElementSetName)
     {
         Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc (c->odr_out, sizeof(*esn));
+            odr_malloc(c->odr_out, sizeof(*esn));
         
         esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup (c->odr_out, smallSetElementSetName);
+        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));
+        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);
+        esn->u.generic = odr_strdup(c->odr_out, mediumSetElementSetName);
         search_req->mediumSetElementSetNames = esn;
     }
     if (syntax)
         search_req->preferredRecordSyntax =
-            yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
+            zoom_yaz_str_to_z3950oid(c, CLASS_RECSYN, syntax);
     
     if (!r->setname)
     {
@@ -1483,14 +1681,14 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
             for (ord = 1; ; ord++)
             {
                 ZOOM_resultset rp;
-                sprintf (setname, "%d", ord);
+                sprintf(setname, "%d", ord);
                 for (rp = c->resultsets; rp; rp = rp->next)
-                    if (rp->setname && !strcmp (rp->setname, setname))
+                    if (rp->setname && !strcmp(rp->setname, setname))
                         break;
                 if (!rp)
                     break;
             }
-            r->setname = xstrdup (setname);
+            r->setname = xstrdup(setname);
             yaz_log(log_details, "%p ZOOM_connection_send_search: allocating "
                     "set %s", c, r->setname);
         }
@@ -1498,19 +1696,21 @@ static zoom_ret ZOOM_connection_send_search (ZOOM_connection c)
         {
             yaz_log(log_details, "%p ZOOM_connection_send_search: using "
                     "default set", c);
-            r->setname = xstrdup ("default");
+            r->setname = xstrdup("default");
         }
-        ZOOM_options_set (r->options, "setname", r->setname);
+        ZOOM_options_set(r->options, "setname", r->setname);
     }
     search_req->resultSetName = odr_strdup(c->odr_out, r->setname);
-    return send_APDU (c, apdu);
+    return send_APDU(c, apdu);
 }
 
 static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
 {
-    int oclass;
+    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:
@@ -1520,11 +1720,9 @@ static void response_default_diag(ZOOM_connection c, Z_DefaultDiagFormat *r)
         addinfo = r->u.v3Addinfo;
         break;
     }
-    xfree (c->addinfo);
+    xfree(c->addinfo);
     c->addinfo = 0;
-    set_dset_error(c, *r->condition,
-                   yaz_z3950oid_to_str(r->diagnosticSetId, &oclass),
-                   addinfo, 0);
+    set_dset_error(c, *r->condition, oid_name, addinfo, 0);
 }
 
 static void response_diag(ZOOM_connection c, Z_DiagRec *p)
@@ -1536,7 +1734,7 @@ static void response_diag(ZOOM_connection c, Z_DiagRec *p)
 }
 
 ZOOM_API(ZOOM_record)
-ZOOM_record_clone (ZOOM_record srec)
+    ZOOM_record_clone(ZOOM_record srec)
 {
     char *buf;
     int size;
@@ -1544,54 +1742,72 @@ ZOOM_record_clone (ZOOM_record srec)
     ZOOM_record nrec;
 
     odr_enc = odr_createmem(ODR_ENCODE);
-    if (!z_NamePlusRecord (odr_enc, &srec->npr, 0, 0))
+    if (!z_NamePlusRecord(odr_enc, &srec->npr, 0, 0))
         return 0;
-    buf = odr_getbuf (odr_enc, &size, 0);
+    buf = odr_getbuf(odr_enc, &size, 0);
     
-    nrec = (ZOOM_record) xmalloc (sizeof(*nrec));
+    nrec = (ZOOM_record) xmalloc(sizeof(*nrec));
     nrec->odr = odr_createmem(ODR_DECODE);
     nrec->wrbuf_marc = 0;
     nrec->wrbuf_iconv = 0;
     nrec->wrbuf_opac = 0;
-    odr_setbuf (nrec->odr, buf, size, 0);
-    z_NamePlusRecord (nrec->odr, &nrec->npr, 0, 0);
+    odr_setbuf(nrec->odr, buf, size, 0);
+    z_NamePlusRecord(nrec->odr, &nrec->npr, 0, 0);
     
-    odr_destroy (odr_enc);
+    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)
+    ZOOM_resultset_record_immediate(ZOOM_resultset s,size_t pos)
 {
-    return record_cache_lookup (s, 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_resultset_record(ZOOM_resultset r, size_t pos)
 {
     ZOOM_record rec = ZOOM_resultset_record_immediate(r, pos);
 
     if (!rec)
     {
-        ZOOM_resultset_retrieve (r, 1, pos, 1);
-        rec = ZOOM_resultset_record_immediate (r, pos);
+        /*
+         * 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_destroy(ZOOM_record rec)
 {
     if (!rec)
         return;
     if (rec->wrbuf_marc)
-        wrbuf_free (rec->wrbuf_marc, 1);
+        wrbuf_destroy(rec->wrbuf_marc);
     if (rec->wrbuf_iconv)
-        wrbuf_free (rec->wrbuf_iconv, 1);
+        wrbuf_destroy(rec->wrbuf_iconv);
     if (rec->wrbuf_opac)
-        wrbuf_free (rec->wrbuf_opac, 1);
-    odr_destroy (rec->odr);
-    xfree (rec);
+        wrbuf_destroy(rec->wrbuf_opac);
+    odr_destroy(rec->odr);
+    xfree(rec);
 }
 
 static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
@@ -1634,15 +1850,15 @@ static const char *marc_iconv_return(ZOOM_record rec, int marc_type,
     yaz_marc_xml(mt, marc_type);
     if (!rec->wrbuf_marc)
         rec->wrbuf_marc = wrbuf_alloc();
-    wrbuf_rewind (rec->wrbuf_marc);
-    if (yaz_marc_decode_wrbuf (mt, buf, sz, rec->wrbuf_marc) > 0)
+    wrbuf_rewind(rec->wrbuf_marc);
+    if (yaz_marc_decode_wrbuf(mt, buf, sz, rec->wrbuf_marc) > 0)
     {
         yaz_marc_destroy(mt);
         if (cd)
             yaz_iconv_close(cd);
         if (len)
             *len = wrbuf_len(rec->wrbuf_marc);
-        return wrbuf_buf(rec->wrbuf_marc);
+        return wrbuf_cstr(rec->wrbuf_marc);
     }
     yaz_marc_destroy(mt);
     if (cd)
@@ -1660,6 +1876,7 @@ static const char *record_iconv_return(ZOOM_record rec, int *len,
 
     *from = '\0';
     strcpy(to, "UTF-8");
+
     if (record_charset && *record_charset)
     {
         /* Use "from,to" or just "from" */
@@ -1681,32 +1898,15 @@ static const char *record_iconv_return(ZOOM_record rec, int *len,
 
     if (*from && *to && (cd = yaz_iconv_open(to, from)))
     {
-        char outbuf[12];
-        size_t inbytesleft = sz;
-        const char *inp = buf;
-        
         if (!rec->wrbuf_iconv)
             rec->wrbuf_iconv = wrbuf_alloc();
 
         wrbuf_rewind(rec->wrbuf_iconv);
 
-        while (inbytesleft)
-        {
-            size_t outbytesleft = sizeof(outbuf);
-            char *outp = outbuf;
-            size_t r = yaz_iconv (cd, (char**) &inp,
-                                  &inbytesleft, 
-                                  &outp, &outbytesleft);
-            if (r == (size_t) (-1))
-            {
-                int e = yaz_iconv_error(cd);
-                if (e != YAZ_ICONV_E2BIG)
-                    break;
-            }
-            wrbuf_write(rec->wrbuf_iconv, outbuf, outp - outbuf);
-        }
-        wrbuf_puts(rec->wrbuf_iconv, "");
-        buf = wrbuf_buf(rec->wrbuf_iconv);
+        wrbuf_iconv_write(rec->wrbuf_iconv, cd, buf, sz);
+        wrbuf_iconv_reset(rec->wrbuf_iconv, cd);
+
+        buf = wrbuf_cstr(rec->wrbuf_iconv);
         sz = wrbuf_len(rec->wrbuf_iconv);
         yaz_iconv_close(cd);
     }
@@ -1715,34 +1915,95 @@ static const char *record_iconv_return(ZOOM_record rec, int *len,
     return buf;
 }
 
-ZOOM_API(const char *)
-ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
+
+ZOOM_API(int)
+    ZOOM_record_error(ZOOM_record rec, const char **cp,
+                      const char **addinfo, const char **diagset)
 {
-    char type[40];
-    char charset[40];
-    char xpath[512];
-    const char *cp;
-    int i;
     Z_NamePlusRecord *npr;
     
-    if (len)
-        *len = 0; /* default return */
-        
     if (!rec)
         return 0;
-    npr = rec->npr;
-    if (!npr)
-        return 0;
 
-    cp = type_spec;
-    for (i = 0; cp[i] && i < sizeof(type)-1; i++)
+    npr = rec->npr;
+    if (rec->diag_uri)
     {
-        if (cp[i] == ';' || cp[i] == ' ')
-            break;
-        type[i] = cp[i];
+        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);
     }
-    type[i] = '\0';
-    charset[0] = '\0';
+    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(const char *)
+    ZOOM_record_get(ZOOM_record rec, const char *type_spec, int *len)
+{
+    char type[40];
+    char charset[40];
+    char xpath[512];
+    const char *cp;
+    int i;
+    Z_NamePlusRecord *npr;
+    
+    if (len)
+        *len = 0; /* default return */
+        
+    if (!rec)
+        return 0;
+    npr = rec->npr;
+    if (!npr)
+        return 0;
+
+    cp = type_spec;
+    for (i = 0; cp[i] && i < sizeof(type)-1; i++)
+    {
+        if (cp[i] == ';' || cp[i] == ' ')
+            break;
+        type[i] = cp[i];
+    }
+    type[i] = '\0';
+    charset[0] = '\0';
     while (type_spec[i] == ';')
     {
         i++;
@@ -1771,21 +2032,25 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         while (type_spec[i] == ' ')
             i++;
     }
-    if (!strcmp (type, "database"))
+    if (!strcmp(type, "database"))
     {
         if (len)
             *len = (npr->databaseName ? strlen(npr->databaseName) : 0);
         return npr->databaseName;
     }
-    else if (!strcmp (type, "syntax"))
+    else if (!strcmp(type, "schema"))
+    {
+        if (len)
+            *len = rec->schema ? strlen(rec->schema) : 0;
+        return rec->schema;
+    }
+    else if (!strcmp(type, "syntax"))
     {
         const char *desc = 0;   
         if (npr->which == Z_NamePlusRecord_databaseRecord)
         {
             Z_External *r = (Z_External *) npr->u.databaseRecord;
-            oident *ent = oid_getentbyoid(r->direct_reference);
-            if (ent)
-                desc = ent->desc;
+            desc = yaz_oid_to_string(yaz_oid_std(), r->direct_reference, 0);
         }
         if (!desc)
             desc = "none";
@@ -1797,10 +2062,10 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         return 0;
 
     /* from now on - we have a database record .. */
-    if (!strcmp (type, "render"))
+    if (!strcmp(type, "render"))
     {
         Z_External *r = (Z_External *) npr->u.databaseRecord;
-        oident *ent = oid_getentbyoid(r->direct_reference);
+        const Odr_oid *oid = r->direct_reference;
 
         /* render bibliographic record .. */
         if (r->which == Z_External_OPAC)
@@ -1808,7 +2073,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
             r = r->u.opac->bibliographicRecord;
             if (!r)
                 return 0;
-            ent = oid_getentbyoid(r->direct_reference);
+            oid = r->direct_reference;
         }
         if (r->which == Z_External_sutrs)
             return record_iconv_return(rec, len,
@@ -1817,18 +2082,9 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
                                        charset);
         else if (r->which == Z_External_octet)
         {
-            const char *ret_buf;
-            switch (ent->value)
+            if (yaz_oid_is_iso2709(oid))
             {
-            case VAL_SOIF:
-            case VAL_HTML:
-            case VAL_SUTRS:
-                break;
-            case VAL_TEXT_XML:
-            case VAL_APPLICATION_XML:
-                break;
-            default:
-                ret_buf = marc_iconv_return(
+                const char *ret_buf = marc_iconv_return(
                     rec, YAZ_MARC_LINE, len,
                     (const char *) r->u.octet_aligned->buf,
                     r->u.octet_aligned->len,
@@ -1845,7 +2101,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         {
             if (!rec->wrbuf_marc)
                 rec->wrbuf_marc = wrbuf_alloc();
-            wrbuf_rewind (rec->wrbuf_marc);
+            wrbuf_rewind(rec->wrbuf_marc);
             yaz_display_grs1(rec->wrbuf_marc, r->u.grs1, 0);
             return record_iconv_return(rec, len,
                                        wrbuf_buf(rec->wrbuf_marc),
@@ -1854,10 +2110,10 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         }
         return 0;
     }
-    else if (!strcmp (type, "xml") || !strcmp(type, "oai"))
+    else if (!strcmp(type, "xml"))
     {
         Z_External *r = (Z_External *) npr->u.databaseRecord;
-        oident *ent = oid_getentbyoid(r->direct_reference);
+        const Odr_oid *oid = r->direct_reference;
 
         /* render bibliographic record .. */
         if (r->which == Z_External_OPAC)
@@ -1865,7 +2121,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
             r = r->u.opac->bibliographicRecord;
             if (!r)
                 return 0;
-            ent = oid_getentbyoid(r->direct_reference);
+            oid = r->direct_reference;
         }
         
         if (r->which == Z_External_sutrs)
@@ -1875,22 +2131,10 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
                                        charset);
         else if (r->which == Z_External_octet)
         {
-            const char *ret_buf;
             int marc_decode_type = YAZ_MARC_MARCXML;
-
-            if (!strcmp(type, "oai"))
-                marc_decode_type = YAZ_MARC_OAIMARC;
-            switch (ent->value)
+            if (yaz_oid_is_iso2709(oid))
             {
-            case VAL_SOIF:
-            case VAL_HTML:
-            case VAL_SUTRS:
-                break;
-            case VAL_TEXT_XML:
-            case VAL_APPLICATION_XML:
-                break;
-            default:
-                ret_buf = marc_iconv_return(
+                const char *ret_buf = marc_iconv_return(
                     rec, marc_decode_type, len,
                     (const char *) r->u.octet_aligned->buf,
                     r->u.octet_aligned->len,
@@ -1910,7 +2154,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         }
         return 0;
     }
-    else if (!strcmp (type, "raw"))
+    else if (!strcmp(type, "raw"))
     {
         Z_External *r = (Z_External *) npr->u.databaseRecord;
         
@@ -1944,7 +2188,7 @@ ZOOM_record_get (ZOOM_record rec, const char *type_spec, int *len)
         {
             if (!rec->wrbuf_opac)
                 rec->wrbuf_opac = wrbuf_alloc();
-            wrbuf_rewind (rec->wrbuf_opac);
+            wrbuf_rewind(rec->wrbuf_opac);
             yaz_display_OPAC(rec->wrbuf_opac, r->u.opac, 0);
             return record_iconv_return(rec, len,
                                        wrbuf_buf(rec->wrbuf_opac),
@@ -1964,69 +2208,77 @@ static int strcmp_null(const char *v1, const char *v2)
     return strcmp(v1, v2);
 }
 
-static void record_cache_add (ZOOM_resultset r, Z_NamePlusRecord *npr, 
-                              int pos)
+static size_t record_hash(int pos)
 {
-    ZOOM_record_cache rc;
-    const char *elementSetName =
-        ZOOM_resultset_option_get (r, "elementSetName");
-    const char *syntax = 
-        ZOOM_resultset_option_get (r, "preferredRecordSyntax");
+    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_cache; rc; rc = rc->next)
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
     {
-        if (pos == rc->pos)
+        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;
+        rc->rec.wrbuf_marc = 0;
+        rc->rec.wrbuf_iconv = 0;
+        rc->rec.wrbuf_opac = 0;
+        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)
         {
-            if (strcmp_null(r->schema, rc->schema))
-                continue;
-            if (strcmp_null(elementSetName,rc->elementSetName))
-                continue;
-            if (strcmp_null(syntax, rc->syntax))
-                continue;
-            /* not destroying rc->npr (it's handled by nmem )*/
-            rc->rec.npr = npr;
-            /* keeping wrbuf_marc too */
-            return;
+            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);
     }
-    rc = (ZOOM_record_cache) odr_malloc (r->odr, sizeof(*rc));
-    rc->rec.npr = npr; 
-    rc->rec.odr = 0;
-    rc->rec.wrbuf_marc = 0;
-    rc->rec.wrbuf_iconv = 0;
-    rc->rec.wrbuf_opac = 0;
-    if (elementSetName)
-        rc->elementSetName = odr_strdup (r->odr, elementSetName);
-    else
-        rc->elementSetName = 0;
-
-    if (syntax)
-        rc->syntax = odr_strdup (r->odr, syntax);
-    else
-        rc->syntax = 0;
-
-    if (r->schema)
-        rc->schema = odr_strdup (r->odr, r->schema);
-    else
-        rc->schema = 0;
-
-    rc->pos = pos;
-    rc->next = r->record_cache;
-    r->record_cache = rc;
 }
 
-static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos)
+static ZOOM_record record_cache_lookup(ZOOM_resultset r, int pos,
+                                       const char *syntax,
+                                       const char *elementSetName)
 {
     ZOOM_record_cache rc;
-    const char *elementSetName =
-        ZOOM_resultset_option_get (r, "elementSetName");
-    const char *syntax = 
-        ZOOM_resultset_option_get (r, "preferredRecordSyntax");
     
-    for (rc = r->record_cache; rc; rc = rc->next)
+    for (rc = r->record_hash[record_hash(pos)]; rc; rc = rc->next)
     {
         if (pos == rc->pos)
         {
@@ -2042,10 +2294,12 @@ static ZOOM_record record_cache_lookup (ZOOM_resultset r, int pos)
     return 0;
 }
                                              
-static void handle_records (ZOOM_connection c, Z_Records *sr,
-                            int present_phase)
+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 ;
@@ -2053,9 +2307,17 @@ static void handle_records (ZOOM_connection c, Z_Records *sr,
     {
     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;
@@ -2071,30 +2333,40 @@ static void handle_records (ZOOM_connection c, Z_Records *sr,
     }
     else 
     {
-        if (resultset->count + resultset->start > resultset->size)
-            resultset->count = resultset->size - resultset->start;
-        if (resultset->count < 0)
-            resultset->count = 0;
+        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);
+            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+ resultset->start);
+                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 (resultset->odr->mem, nmem);
-            nmem_destroy (nmem);
+            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, 14, 0);
-                record_cache_add(resultset, myrec, resultset->start);
+                record_cache_add(resultset, myrec, *start,
+                                 syntax, elementSetName, 0, 0);
             }
         }
         else if (present_phase)
@@ -2102,14 +2374,15 @@ static void handle_records (ZOOM_connection c, Z_Records *sr,
             /* present response and we didn't get any records! */
             Z_NamePlusRecord *myrec = 
                 zget_surrogateDiagRec(resultset->odr, 0, 14, 0);
-            record_cache_add(resultset, myrec, resultset->start);
+            record_cache_add(resultset, myrec, *start, syntax, elementSetName,
+                             0, 0);
         }
     }
 }
 
-static void handle_present_response (ZOOM_connection c, Z_PresentResponse *pr)
+static void handle_present_response(ZOOM_connection c, Z_PresentResponse *pr)
 {
-    handle_records (c, pr->records, 1);
+    handle_records(c, pr->records, 1);
 }
 
 static void handle_queryExpressionTerm(ZOOM_options opt, const char *name,
@@ -2218,7 +2491,7 @@ static void handle_searchResult(ZOOM_connection c, ZOOM_resultset resultset,
     }
 }
 
-static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
+static void handle_search_response(ZOOM_connection c, Z_SearchResponse *sr)
 {
     ZOOM_resultset resultset;
     ZOOM_Event event;
@@ -2231,21 +2504,31 @@ static void handle_search_response (ZOOM_connection c, Z_SearchResponse *sr)
 
     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_searchResult(c, resultset, sr->additionalSearchInfo);
 
     resultset->size = *sr->resultCount;
-    handle_records (c, sr->records, 0);
+    handle_records(c, sr->records, 0);
 }
 
-static void sort_response (ZOOM_connection c, Z_SortResponse *res)
+static void sort_response(ZOOM_connection c, Z_SortResponse *res)
 {
     if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag (c, res->diagnostics[0]);
+        response_diag(c, res->diagnostics[0]);
 }
 
-static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
+static int scan_response(ZOOM_connection c, Z_ScanResponse *res)
 {
-    NMEM nmem = odr_extract_mem (c->odr_in);
+    NMEM nmem = odr_extract_mem(c->odr_in);
     ZOOM_scanset scan;
 
     if (!c->tasks || c->tasks->which != ZOOM_TASK_SCAN)
@@ -2255,22 +2538,23 @@ static int scan_response (ZOOM_connection c, Z_ScanResponse *res)
     if (res->entries && res->entries->nonsurrogateDiagnostics)
         response_diag(c, res->entries->nonsurrogateDiagnostics[0]);
     scan->scan_response = res;
-    nmem_transfer (scan->odr->mem, nmem);
+    scan->srw_scan_response = 0;
+    nmem_transfer(odr_getmem(scan->odr), nmem);
     if (res->stepSize)
-        ZOOM_options_set_int (scan->options, "stepSize", *res->stepSize);
+        ZOOM_options_set_int(scan->options, "stepSize", *res->stepSize);
     if (res->positionOfTerm)
-        ZOOM_options_set_int (scan->options, "position", *res->positionOfTerm);
+        ZOOM_options_set_int(scan->options, "position", *res->positionOfTerm);
     if (res->scanStatus)
-        ZOOM_options_set_int (scan->options, "scanStatus", *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);
+        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)
+static zoom_ret send_sort(ZOOM_connection c,
+                          ZOOM_resultset resultset)
 {
     if (c->error)
         resultset->r_sort_spec = 0;
@@ -2281,13 +2565,13 @@ static zoom_ret send_sort (ZOOM_connection c,
         
         req->num_inputResultSetNames = 1;
         req->inputResultSetNames = (Z_InternationalString **)
-            odr_malloc (c->odr_out, sizeof(*req->inputResultSetNames));
+            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);
+            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 send_APDU(c, apdu);
     }
     return zoom_complete;
 }
@@ -2300,52 +2584,62 @@ static zoom_ret send_present(ZOOM_connection c)
     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;
-        resultset->start = c->tasks->u.retrieve.start;
-        resultset->count = c->tasks->u.retrieve.count;
-
-        if (resultset->start >= resultset->size)
-        {
-            yaz_log(log_details, "%p send_present start=%d >= size=%d",
-                    c, resultset->start, resultset->size);
-            return zoom_complete;
-        }
-        if (resultset->start + resultset->count > resultset->size)
-            resultset->count = resultset->size - resultset->start;
+        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, resultset->start, resultset->count);
-
-    syntax = ZOOM_resultset_option_get (resultset, "preferredRecordSyntax");
-    elementSetName = ZOOM_resultset_option_get (resultset, "elementSetName");
+            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;
-    if (resultset->start < 0)
-        return zoom_complete;
-    for (i = 0; i<resultset->count; i++)
+    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 + resultset->start);
+            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);
+        }
     }
-    if (i == resultset->count)
+    *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;
@@ -2354,26 +2648,28 @@ static zoom_ret send_present(ZOOM_connection c)
     apdu = zget_APDU(c->odr_out, Z_APDU_presentRequest);
     req = apdu->u.presentRequest;
 
-    resultset->start += i;
-    resultset->count -= i;
-
     if (i)
         yaz_log(log_details, "%p send_present skip=%d", c, i);
 
-    *req->resultSetStartPoint = resultset->start + 1;
-    *req->numberOfRecordsRequested = resultset->step>0 ?
-        resultset->step : resultset->count;
-    assert (*req->numberOfRecordsRequested > 0);
+    *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 =
-            yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, syntax);
+            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));
+            odr_malloc(c->odr_out, sizeof(*compo));
 
         req->recordComposition = compo;
         compo->which = Z_RecordComp_complex;
@@ -2388,14 +2684,14 @@ static zoom_ret send_present(ZOOM_connection c)
 
         compo->u.complex->generic->which = Z_Schema_oid;
         compo->u.complex->generic->schema.oid = (Odr_oid *)
-            yaz_str_to_z3950oid (c->odr_out, CLASS_SCHEMA, resultset->schema);
+            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 *)
-                yaz_str_to_z3950oid (c->odr_out, CLASS_RECSYN, resultset->schema);
+                zoom_yaz_str_to_z3950oid (c, CLASS_RECSYN, resultset->schema);
         }
         if (elementSetName && *elementSetName)
         {
@@ -2404,7 +2700,7 @@ static zoom_ret send_present(ZOOM_connection c)
             compo->u.complex->generic->elementSpec->which =
                 Z_ElementSpec_elementSetName;
             compo->u.complex->generic->elementSpec->u.elementSetName =
-                odr_strdup (c->odr_out, elementSetName);
+                odr_strdup(c->odr_out, elementSetName);
         }
         else
             compo->u.complex->generic->elementSpec = 0;
@@ -2416,83 +2712,64 @@ static zoom_ret send_present(ZOOM_connection c)
     else if (elementSetName && *elementSetName)
     {
         Z_ElementSetNames *esn = (Z_ElementSetNames *)
-            odr_malloc (c->odr_out, sizeof(*esn));
+            odr_malloc(c->odr_out, sizeof(*esn));
         Z_RecordComposition *compo = (Z_RecordComposition *)
-            odr_malloc (c->odr_out, sizeof(*compo));
+            odr_malloc(c->odr_out, sizeof(*compo));
         
         esn->which = Z_ElementSetNames_generic;
-        esn->u.generic = odr_strdup (c->odr_out, elementSetName);
+        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);
+    return send_APDU(c, apdu);
 }
 
 ZOOM_API(ZOOM_scanset)
-ZOOM_connection_scan (ZOOM_connection c, const char *start)
+    ZOOM_connection_scan(ZOOM_connection c, const char *start)
 {
     ZOOM_scanset s;
     ZOOM_query q = ZOOM_query_create();
 
-    ZOOM_query_prefix (q, start);
+    ZOOM_query_prefix(q, start);
 
     s = ZOOM_connection_scan1(c, q);
-    ZOOM_query_destroy (q);
+    ZOOM_query_destroy(q);
     return s;
 
 }
 
 ZOOM_API(ZOOM_scanset)
-ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q)
+    ZOOM_connection_scan1(ZOOM_connection c, ZOOM_query q)
 {
-    ZOOM_scanset scan = (ZOOM_scanset) xmalloc (sizeof(*scan));
-    char *start;
-    char *freeme = 0;
+    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->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;
 
-    /*
-     * We need to check the query-type, so we can recognise CQL and
-     * compile it into a form that we can use here.  The ZOOM_query
-     * structure has no explicit `type' member, but inspection of the
-     * ZOOM_query_prefix() and ZOOM_query_cql() functions shows how
-     * the structure is set up in each case.
-     */
+    scan->query = q;
+    (q->refcount)++;
+    scan->databaseNames = set_DatabaseNames(c, c->options,
+                                            &scan->num_databaseNames,
+                                            scan->odr);
 
-    if (q->z_query->which == Z_Query_type_1) {
-        yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p PQF '%s'",
-                c, q, q->query_string);
-        start = q->query_string;
-    } else if (q->z_query->which == Z_Query_type_104) {
-        yaz_log(log_api, "%p ZOOM_connection_scan1 q=%p CQL '%s'",
-                c, q, q->query_string);
-        start = freeme = cql2pqf(c, q->query_string);
-        if (start == 0)
-            return 0;
-    } else {
-        yaz_log(YLOG_FATAL, "%p ZOOM_connection_scan1 q=%p unknown type '%s'",
-                c, q, q->query_string);
-        abort();
-    }
-
-    scan->termListAndStartPoint =
-        p_query_scan(scan->odr, PROTO_Z3950, &scan->attributeSet, start);
-    xfree (freeme);
-    if (scan->termListAndStartPoint != 0)
-    {
-        ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_SCAN);
+    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))
+            while (ZOOM_event(1, &c))
                 ;
         }
     }
@@ -2500,21 +2777,23 @@ ZOOM_connection_scan1 (ZOOM_connection c, ZOOM_query q)
 }
 
 ZOOM_API(void)
-ZOOM_scanset_destroy (ZOOM_scanset scan)
+    ZOOM_scanset_destroy(ZOOM_scanset scan)
 {
     if (!scan)
         return;
     (scan->refcount)--;
     if (scan->refcount == 0)
     {
-        odr_destroy (scan->odr);
+        ZOOM_query_destroy(scan->query);
+
+        odr_destroy(scan->odr);
         
-        ZOOM_options_destroy (scan->options);
-        xfree (scan);
+        ZOOM_options_destroy(scan->options);
+        xfree(scan);
     }
 }
 
-static zoom_ret send_package (ZOOM_connection c)
+static zoom_ret send_package(ZOOM_connection c)
 {
     ZOOM_Event event;
 
@@ -2523,14 +2802,16 @@ static zoom_ret send_package (ZOOM_connection c)
         return zoom_complete;
     assert (c->tasks->which == ZOOM_TASK_PACKAGE);
     
-    event = ZOOM_Event_create (ZOOM_EVENT_SEND_APDU);
-    ZOOM_connection_put_event (c, event);
+    event = ZOOM_Event_create(ZOOM_EVENT_SEND_APDU);
+    ZOOM_connection_put_event(c, event);
     
-    return do_write_ex (c, c->tasks->u.package->buf_out,
-                        c->tasks->u.package->len_out);
+    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 send_scan (ZOOM_connection 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);
@@ -2542,143 +2823,276 @@ static zoom_ret send_scan (ZOOM_connection c)
     assert (c->tasks->which == ZOOM_TASK_SCAN);
     scan = c->tasks->u.scan.scan;
 
-    req->termListAndStartPoint = scan->termListAndStartPoint;
-    req->attributeSet = scan->attributeSet;
+    /* 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", 10);
 
     req->preferredPositionInResponse =
-        odr_intdup (c->odr_out,
-                    ZOOM_options_get_int(scan->options, "position", 1));
+        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));
+        odr_intdup(c->odr_out,
+                   ZOOM_options_get_int(scan->options, "stepSize", 0));
     
-    req->databaseNames = set_DatabaseNames (c, scan->options, 
-                                            &req->num_databaseNames);
+    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;
+    }
 
-    return send_APDU (c, apdu);
+    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_scanset_size(ZOOM_scanset scan)
 {
-    if (!scan || !scan->scan_response || !scan->scan_response->entries)
+    if (!scan)
         return 0;
-    return scan->scan_response->entries->num_entries;
+
+    if (scan->scan_response && scan->scan_response->entries)
+        return scan->scan_response->entries->num_entries;
+    else if (scan->srw_scan_response)
+        return scan->srw_scan_response->num_terms;
+    return 0;
 }
 
-ZOOM_API(const char *)
-ZOOM_scanset_term (ZOOM_scanset scan, size_t pos,
-                   int *occ, int *len)
+static void ZOOM_scanset_term_x(ZOOM_scanset scan, size_t pos,
+                                int *occ,
+                                const char **value_term, size_t *value_len,
+                                const char **disp_term, size_t *disp_len)
 {
-    const char *term = 0;
-    size_t noent = ZOOM_scanset_size (scan);
-    Z_ScanResponse *res = scan->scan_response;
+    size_t noent = ZOOM_scanset_size(scan);
     
-    *len = 0;
+    *value_term = 0;
+    *value_len = 0;
+
+    *disp_term = 0;
+    *disp_len = 0;
+
     *occ = 0;
-    if (pos >= noent)
-        return 0;
-    if (res->entries->entries[pos]->which == Z_Entry_termInfo)
+    if (pos >= noent || pos < 0)
+        return;
+    if (scan->scan_response)
     {
-        Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
-        
-        if (t->term->which == Z_Term_general)
+        Z_ScanResponse *res = scan->scan_response;
+        if (res->entries->entries[pos]->which == Z_Entry_termInfo)
+        {
+            Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
+            
+            *value_term = (const char *) t->term->u.general->buf;
+            *value_len = t->term->u.general->len;
+            if (t->displayTerm)
+            {
+                *disp_term = t->displayTerm;
+                *disp_len = strlen(*disp_term);
+            }
+            else if (t->term->which == Z_Term_general)
+            {
+                *disp_term = (const char *) t->term->u.general->buf;
+                *disp_len = t->term->u.general->len;
+            }
+            *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
+        }
+    }
+    if (scan->srw_scan_response)
+    {
+        Z_SRW_scanResponse *res = scan->srw_scan_response;
+        Z_SRW_scanTerm *t = res->terms + pos;
+        if (t)
         {
-            term = (const char *) t->term->u.general->buf;
-            *len = t->term->u.general->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;
         }
-        *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
     }
-    return term;
 }
 
 ZOOM_API(const char *)
-ZOOM_scanset_display_term (ZOOM_scanset scan, size_t pos,
-                           int *occ, int *len)
+    ZOOM_scanset_term(ZOOM_scanset scan, size_t pos,
+                      int *occ, int *len)
 {
-    const char *term = 0;
-    size_t noent = ZOOM_scanset_size (scan);
-    Z_ScanResponse *res = scan->scan_response;
+    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 = 0;
-    *occ = 0;
-    if (pos >= noent)
-        return 0;
-    if (res->entries->entries[pos]->which == Z_Entry_termInfo)
-    {
-        Z_TermInfo *t = res->entries->entries[pos]->u.termInfo;
+    *len = value_len;
+    return value_term;
+}
 
-        if (t->displayTerm)
-        {
-            term = t->displayTerm;
-            *len = strlen(term);
-        }
-        else if (t->term->which == Z_Term_general)
-        {
-            term = (const char *) t->term->u.general->buf;
-            *len = t->term->u.general->len;
-        }
-        *occ = t->globalOccurrences ? *t->globalOccurrences : 0;
-    }
-    return term;
+ZOOM_API(const char *)
+    ZOOM_scanset_display_term(ZOOM_scanset scan, size_t pos,
+                              int *occ, int *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)
+    ZOOM_scanset_option_get(ZOOM_scanset scan, const char *key)
 {
-    return ZOOM_options_get (scan->options, 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_scanset_option_set(ZOOM_scanset scan, const char *key,
+                            const char *val)
 {
-    ZOOM_options_set (scan->options, key, val);
+    ZOOM_options_set(scan->options, key, val);
 }
 
-static Z_APDU *create_es_package (ZOOM_package p, int type)
+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;
     
-    *req->function = Z_ExtendedServicesRequest_create;
-    
     str = ZOOM_options_get(p->options, "package-name");
     if (str && *str)
-        req->packageName = nmem_strdup (p->odr_out->mem, str);
+        req->packageName = odr_strdup(p->odr_out, str);
     
     str = ZOOM_options_get(p->options, "user-id");
     if (str)
-        req->userId = nmem_strdup (p->odr_out->mem, str);
+        req->userId = odr_strdup_null(p->odr_out, str);
     
-    req->packageType = yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
-                                              type);
+    req->packageType = odr_oiddup(p->odr_out, oid);
 
     str = ZOOM_options_get(p->options, "function");
     if (str)
     {
         if (!strcmp (str, "create"))
-            *req->function = 1;
+            *req->function = Z_ExtendedServicesRequest_create;
         if (!strcmp (str, "delete"))
-            *req->function = 2;
+            *req->function = Z_ExtendedServicesRequest_delete;
         if (!strcmp (str, "modify"))
-            *req->function = 3;
+            *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)
+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);
+    return ZOOM_options_get(p->options, idx+4);
 }
 
-static Z_External *encode_ill_request (ZOOM_package p)
+static Z_External *encode_ill_request(ZOOM_package p)
 {
     ODR out = p->odr_out;
     ILL_Request *req;
@@ -2691,45 +3105,38 @@ static Z_External *encode_ill_request (ZOOM_package p)
         
     req = ill_get_ILLRequest(&ctl, "ill", 0);
         
-    if (!ill_Request (out, &req, 0, 0))
+    if (!ill_Request(out, &req, 0, 0))
     {
         int ill_request_size;
-        char *ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
+        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);
+            odr_setbuf(out, ill_request_buf, ill_request_size, 1);
         return 0;
     }
     else
     {
-        oident oid;
         int illRequest_size = 0;
-        char *illRequest_buf = odr_getbuf (out, &illRequest_size, 0);
-                
-        oid.proto = PROTO_GENERAL;
-        oid.oclass = CLASS_GENERAL;
-        oid.value = VAL_ISO_ILL_1;
+        char *illRequest_buf = odr_getbuf(out, &illRequest_size, 0);
                 
-        r = (Z_External *) odr_malloc (out, sizeof(*r));
-        r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
+        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_oct *)
-            odr_malloc (out, sizeof(*r->u.single_ASN1_type));
-        r->u.single_ASN1_type->buf = (unsigned char*)
-            odr_malloc (out, illRequest_size);
-        r->u.single_ASN1_type->len = illRequest_size;
-        r->u.single_ASN1_type->size = illRequest_size;
-        memcpy (r->u.single_ASN1_type->buf, illRequest_buf, illRequest_size);
+        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));
+    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 *) 
@@ -2740,19 +3147,19 @@ static Z_ItemOrder *encode_item_order(ZOOM_package p)
         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));
+        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 = str ?
-        nmem_strdup (p->odr_out->mem, str) : 0;
+    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 = str ?
-        nmem_strdup (p->odr_out->mem, str) : 0;
+    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 = str ?
-        nmem_strdup (p->odr_out->mem, str) : 0;
+    req->u.esRequest->toKeep->contact->email =
+        odr_strdup_null(p->odr_out, str);
         
     req->u.esRequest->toKeep->addlBilling = 0;
         
@@ -2769,10 +3176,10 @@ static Z_ItemOrder *encode_item_order(ZOOM_package p)
     else
     {
         req->u.esRequest->notToKeep->resultSetItem = (Z_IOResultSetItem *)
-           odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
+            odr_malloc(p->odr_out, sizeof(Z_IOResultSetItem));
 
         req->u.esRequest->notToKeep->resultSetItem->resultSetId =
-           nmem_strdup (p->odr_out->mem, str);
+            odr_strdup(p->odr_out, str);
         req->u.esRequest->notToKeep->resultSetItem->item =
             (int *) odr_malloc(p->odr_out, sizeof(int));
         
@@ -2780,7 +3187,15 @@ static Z_ItemOrder *encode_item_order(ZOOM_package p)
         *req->u.esRequest->notToKeep->resultSetItem->item =
             (str ? atoi(str) : 1);
     }
-    req->u.esRequest->notToKeep->itemRequest = encode_ill_request(p);
+
+    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;
 }
@@ -2789,21 +3204,20 @@ Z_APDU *create_admin_package(ZOOM_package p, int type,
                              Z_ESAdminOriginPartToKeep **toKeepP,
                              Z_ESAdminOriginPartNotToKeep **notToKeepP)
 {
-    Z_APDU *apdu = create_es_package (p, VAL_ADMINSERVICE);
+    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));
+        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);
+        char **db = set_DatabaseNames(p->connection, p->options, &num_db,
+                                      p->odr_out);
         if (num_db > 0)
             first_db = db[0];
             
-        r->direct_reference =
-            yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
-                                   VAL_ADMINSERVICE);
+        r->direct_reference = odr_oiddup(p->odr_out, yaz_oid_extserv_admin);
         r->descriptor = 0;
         r->indirect_reference = 0;
         r->which = Z_External_ESAdmin;
@@ -2838,10 +3252,17 @@ Z_APDU *create_admin_package(ZOOM_package p, int type,
 
 static Z_APDU *create_xmlupdate_package(ZOOM_package p)
 {
-    Z_APDU *apdu = create_es_package(p, VAL_XMLES);
+    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));
-    const char *doc = ZOOM_options_get(p->options, "doc");
+    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;
@@ -2849,14 +3270,8 @@ static Z_APDU *create_xmlupdate_package(ZOOM_package p)
     ext->indirect_reference = 0;
     
     ext->which = Z_External_octet;
-    ext->u.single_ASN1_type = (Odr_oct *)
-        odr_malloc (p->odr_out, sizeof(Odr_oct));
-
-    if (!doc)
-        doc = "";
-    ext->u.single_ASN1_type->buf = (unsigned char*)
-        odr_strdup(p->odr_out, doc);
-    ext->u.single_ASN1_type->size = ext->u.single_ASN1_type->len = strlen(doc);
+    ext->u.single_ASN1_type =
+        odr_create_Odr_oct(p->odr_out, (const unsigned char *) doc, len);
     return apdu;
 }
 
@@ -2865,30 +3280,73 @@ 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);
+    char **db = set_DatabaseNames(p->connection, p->options, &num_db, p->odr_out);
     const char *action = ZOOM_options_get(p->options, "action");
-    const char *recordIdOpaque = ZOOM_options_get(p->options, "recordIdOpaque");
+    int recordIdOpaque_len;
+    const char *recordIdOpaque = ZOOM_options_getl(p->options, "recordIdOpaque",
+        &recordIdOpaque_len);
     const char *recordIdNumber = ZOOM_options_get(p->options, "recordIdNumber");
-    const char *record_buf = ZOOM_options_get(p->options, "record");
+    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");
-    int syntax_oid = VAL_NONE;
+    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;
-    
-    if (syntax_str)
-        syntax_oid = oid_getvalbyname(syntax_str);
-    if (!record_buf)
+    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";
-        syntax_oid = VAL_SUTRS;
+        record_len = 4;
+        syntax_str = "SUTRS";
     }
-    if (syntax_oid == VAL_NONE)
-        syntax_oid = VAL_TEXT_XML;
-    
+
+    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];
     
-    if (!action)
-        action = "specialUpdate";
+    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;
@@ -2903,19 +3361,19 @@ static Z_APDU *create_update_package(ZOOM_package p)
     else
         return 0;
 
-    apdu = create_es_package (p, VAL_DBUPDATE);
+    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));
+            odr_malloc(p->odr_out, sizeof(*r));
+        const char *elementSetName =
+            ZOOM_options_get(p->options, "elementSetName");
         
         apdu->u.extendedServicesRequest->taskSpecificParameters = r;
         
-        r->direct_reference =
-            yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
-                                   VAL_DBUPDATE);
+        r->direct_reference = odr_oiddup(p->odr_out, package_oid);
         r->descriptor = 0;
         r->which = Z_External_update;
         r->indirect_reference = 0;
@@ -2931,7 +3389,9 @@ static Z_APDU *create_update_package(ZOOM_package p)
         
         toKeep->databaseName = odr_strdup(p->odr_out, first_db);
         toKeep->schema = 0;
-        toKeep->elementSetName = 0;
+        
+        toKeep->elementSetName = odr_strdup_null(p->odr_out, elementSetName);
+            
         toKeep->actionQualifier = 0;
         toKeep->action = odr_intdup(p->odr_out, action_no);
         
@@ -2946,12 +3406,10 @@ static Z_APDU *create_update_package(ZOOM_package p)
         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
         if (recordIdOpaque)
         {
-            notToKeep->elements[0]->u.opaque = (Odr_oct *)
-                odr_malloc (p->odr_out, sizeof(Odr_oct));
-            notToKeep->elements[0]->u.opaque->size =
-                notToKeep->elements[0]->u.opaque->len = strlen(recordIdOpaque);
-            notToKeep->elements[0]->u.opaque->buf = (unsigned char*)
-                odr_strdup(p->odr_out, recordIdOpaque);
+            notToKeep->elements[0]->u.opaque = 
+                odr_create_Odr_oct(p->odr_out,
+                                   (const unsigned char *) recordIdOpaque,
+                                   recordIdOpaque_len);
         }
         else if (recordIdNumber)
         {
@@ -2963,46 +3421,64 @@ static Z_APDU *create_update_package(ZOOM_package p)
         else
             notToKeep->elements[0]->u.opaque = 0;
         notToKeep->elements[0]->supplementalId = 0;
-        notToKeep->elements[0]->correlationInfo = 0;
-        notToKeep->elements[0]->record =
-            z_ext_record(p->odr_out, syntax_oid,
-                         record_buf, strlen(record_buf));
+        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);
+        ODR print = odr_createmem(ODR_PRINT);
 
-       z_APDU(print, &apdu, 0, 0);
-       odr_destroy(print);
+        z_APDU(print, &apdu, 0, 0);
+        odr_destroy(print);
     }
     return apdu;
 }
 
 ZOOM_API(void)
-    ZOOM_package_send (ZOOM_package p, const char *type)
+    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);
+    odr_reset(p->odr_out);
+    xfree(p->buf_out);
     p->buf_out = 0;
     if (!strcmp(type, "itemorder"))
     {
-        apdu = create_es_package (p, VAL_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));
+            Z_External *r = (Z_External *) odr_malloc(p->odr_out, sizeof(*r));
             
-            r->direct_reference =
-                yaz_oidval_to_z3950oid(p->odr_out, CLASS_EXTSERV,
-                                       VAL_ITEMORDER);
+            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);
+            r->u.itemOrder = encode_item_order(p);
 
             apdu->u.extendedServicesRequest->taskSpecificParameters = r;
         }
@@ -3036,16 +3512,16 @@ ZOOM_API(void)
         {
             char *buf;
 
-            ZOOM_task task = ZOOM_connection_add_task (c, ZOOM_TASK_PACKAGE);
+            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->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))
+                while (ZOOM_event(1, &c))
                     ;
             }
         }
@@ -3053,13 +3529,13 @@ ZOOM_API(void)
 }
 
 ZOOM_API(ZOOM_package)
-    ZOOM_connection_package (ZOOM_connection c, ZOOM_options options)
+    ZOOM_connection_package(ZOOM_connection c, ZOOM_options options)
 {
-    ZOOM_package p = (ZOOM_package) xmalloc (sizeof(*p));
+    ZOOM_package p = (ZOOM_package) xmalloc(sizeof(*p));
 
     p->connection = c;
-    p->odr_out = odr_createmem (ODR_ENCODE);
-    p->options = ZOOM_options_create_with_parent2 (options, c->options);
+    p->odr_out = odr_createmem(ODR_ENCODE);
+    p->options = ZOOM_options_create_with_parent2(options, c->options);
     p->refcount = 1;
     p->buf_out = 0;
     p->len_out = 0;
@@ -3074,48 +3550,60 @@ ZOOM_API(void)
     (p->refcount)--;
     if (p->refcount == 0)
     {
-        odr_destroy (p->odr_out);
-        xfree (p->buf_out);
+        odr_destroy(p->odr_out);
+        xfree(p->buf_out);
         
-        ZOOM_options_destroy (p->options);
-        xfree (p);
+        ZOOM_options_destroy(p->options);
+        xfree(p);
     }
 }
 
 ZOOM_API(const char *)
-ZOOM_package_option_get (ZOOM_package p, const char *key)
+    ZOOM_package_option_get(ZOOM_package p, const char *key)
 {
-    return ZOOM_options_get (p->options, key);
+    return ZOOM_options_get(p->options, key);
 }
 
+ZOOM_API(const char *)
+    ZOOM_package_option_getl(ZOOM_package p, const char *key, int *lenp)
+{
+    return ZOOM_options_getl(p->options, key, lenp);
+}
 
 ZOOM_API(void)
-ZOOM_package_option_set (ZOOM_package p, const char *key,
-                              const char *val)
+    ZOOM_package_option_set(ZOOM_package p, const char *key,
+                            const char *val)
 {
-    ZOOM_options_set (p->options, key, val);
+    ZOOM_options_set(p->options, key, val);
 }
 
-static int ZOOM_connection_exec_task (ZOOM_connection c)
+ZOOM_API(void)
+    ZOOM_package_option_setl(ZOOM_package p, const char *key,
+                             const char *val, int len)
 {
-    ZOOM_task task = c->tasks;
-    zoom_ret ret = zoom_complete;
+    ZOOM_options_setl(p->options, key, val, len);
+}
+
+static int ZOOM_connection_exec_task(ZOOM_connection c)
+{
+    ZOOM_task task = c->tasks;
+    zoom_ret ret = zoom_complete;
 
     if (!task)
         return 0;
     yaz_log(log_details, "%p ZOOM_connection_exec_task type=%d run=%d",
-             c, task->which, task->running);
+            c, task->which, task->running);
     if (c->error != ZOOM_ERROR_NONE)
     {
         yaz_log(log_details, "%p ZOOM_connection_exec_task "
                 "removing tasks because of error = %d", c, c->error);
-        ZOOM_connection_remove_tasks (c);
+        ZOOM_connection_remove_tasks(c);
         return 0;
     }
     if (task->running)
     {
         yaz_log(log_details, "%p ZOOM_connection_exec_task "
-                 "task already running", c);
+                "task already running", c);
         return 0;
     }
     task->running = 1;
@@ -3134,13 +3622,16 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
             if (c->proto == PROTO_HTTP)
                 ret = ZOOM_connection_srw_send_search(c);
             else
-                ret = send_present (c);
+                ret = send_present(c);
             break;
         case ZOOM_TASK_CONNECT:
             ret = do_connect(c);
             break;
         case ZOOM_TASK_SCAN:
-            ret = send_scan(c);
+            if (c->proto == PROTO_HTTP)
+                ret = ZOOM_connection_srw_send_scan(c);
+            else
+                ret = ZOOM_connection_send_scan(c);
             break;
         case ZOOM_TASK_PACKAGE:
             ret = send_package(c);
@@ -3156,13 +3647,13 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
     {
         yaz_log(log_details, "%p ZOOM_connection_exec_task "
                 "remove tasks because no connection exist", c);
-        ZOOM_connection_remove_tasks (c);
+        ZOOM_connection_remove_tasks(c);
     }
     if (ret == zoom_complete)
     {
         yaz_log(log_details, "%p ZOOM_connection_exec_task "
-                 "task removed (complete)", c);
-        ZOOM_connection_remove_task (c);
+                "task removed (complete)", c);
+        ZOOM_connection_remove_task(c);
         return 0;
     }
     yaz_log(log_details, "%p ZOOM_connection_exec_task "
@@ -3170,40 +3661,108 @@ static int ZOOM_connection_exec_task (ZOOM_connection c)
     return 1;
 }
 
-static zoom_ret send_sort_present (ZOOM_connection c)
+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);
+        r = send_sort(c, c->tasks->u.search.resultset);
     if (r == zoom_complete)
-        r = send_present (c);
+        r = send_present(c);
     return r;
 }
 
-static int es_response (ZOOM_connection c,
-                        Z_ExtendedServicesResponse *res)
+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;
-    if (res->diagnostics && res->num_diagnostics > 0)
-        response_diag(c, res->diagnostics[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;
-        Odr_oct *id = taskPackage->targetReference;
-        
-        if (id)
-            ZOOM_options_setl (c->tasks->u.package->options,
-                               "targetReference", (char*) id->buf, id->len);
+        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);
+        ZOOM_options_setl(c->tasks->u.package->options,
+                          "xmlUpdateDoc", (char*) doc->buf, doc->len);
     }
     return 1;
 }
@@ -3237,16 +3796,26 @@ static void interpret_otherinformation_field(ZOOM_connection c,
     }
 }
 
-static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
+
+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;
     
-    c->mask = 0;
+    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_apd: Received Init response", c);
+        yaz_log(log_api, "%p recv_apdu: Received Init response", c);
         initrs = apdu->u.initResponse;
         ZOOM_connection_option_set(c, "serverImplementationId",
                                    initrs->implementationId ?
@@ -3267,6 +3836,10 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
         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;
@@ -3279,9 +3852,9 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
         else
         {
             char *cookie =
-                yaz_oi_get_string_oidval (&apdu->u.initResponse->otherInfo,
-                                          VAL_COOKIE, 1, 0);
-            xfree (c->cookie_in);
+                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);
@@ -3290,10 +3863,10 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
                 c->support_named_resultsets = 1;
             if (c->tasks)
             {
-                assert (c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task (c);
+                assert(c->tasks->which == ZOOM_TASK_CONNECT);
+                ZOOM_connection_remove_task(c);
             }
-            ZOOM_connection_exec_task (c);
+            ZOOM_connection_exec_task(c);
         }
         if (ODR_MASK_GET(initrs->options, Z_Options_negotiationModel))
         {
@@ -3312,13 +3885,13 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
                         c,
                         charset ? charset : "none", lang ? lang : "none", sel);
                 if (charset)
-                    ZOOM_connection_option_set (c, "negotiation-charset",
-                                                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-lang",
+                                               lang);
 
-                ZOOM_connection_option_set (
+                ZOOM_connection_option_set(
                     c,  "negotiation-charset-in-effect-for-records",
                     (sel != 0) ? "1" : "0");
                 nmem_destroy(tmpmem);
@@ -3327,41 +3900,35 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
         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);
+        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);
+        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);
+        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);
+        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);
+        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 (c->reconnect_ok)
-        {
-            do_close(c);
-            c->tasks->running = 0;
-            ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
-        }
-        else
+        if (!ZOOM_test_reconnect(c))
         {
             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
             do_close(c);
@@ -3374,7 +3941,7 @@ static void recv_apdu (ZOOM_connection c, Z_APDU *apdu)
     }
 }
 
-#if HAVE_XML2
+#if YAZ_HAVE_XML2
 static void handle_srw_response(ZOOM_connection c,
                                 Z_SRW_searchRetrieveResponse *res)
 {
@@ -3382,22 +3949,37 @@ static void handle_srw_response(ZOOM_connection c,
     int i;
     NMEM nmem;
     ZOOM_Event event;
+    int *start;
+    const char *syntax, *elementSetName;
 
     if (!c->tasks)
         return;
 
-    if (c->tasks->which == ZOOM_TASK_SEARCH)
+    switch(c->tasks->which)
+    {
+    case ZOOM_TASK_SEARCH:
         resultset = c->tasks->u.search.resultset;
-    else if (c->tasks->which == ZOOM_TASK_RETRIEVE)
+        start = &c->tasks->u.search.start;
+        syntax = c->tasks->u.search.syntax;
+        elementSetName = c->tasks->u.search.elementSetName;        
+        break;
+    case ZOOM_TASK_RETRIEVE:
         resultset = c->tasks->u.retrieve.resultset;
-    else
-        return ;
-
+        start = &c->tasks->u.retrieve.start;
+        syntax = c->tasks->u.retrieve.syntax;
+        elementSetName = c->tasks->u.retrieve.elementSetName;
+        break;
+    default:
+        return;
+    }
     event = ZOOM_Event_create(ZOOM_EVENT_RECV_SEARCH);
     ZOOM_connection_put_event(c, event);
 
     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->numberOfRecords)
@@ -3406,7 +3988,10 @@ static void handle_srw_response(ZOOM_connection c,
     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));
 
@@ -3414,62 +3999,88 @@ static void handle_srw_response(ZOOM_connection c,
             *res->records[i].recordPosition > 0)
             pos = *res->records[i].recordPosition - 1;
         else
-            pos = resultset->start + i;
+            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 =
-            yaz_oidval_to_z3950oid(c->odr_in, CLASS_RECSYN, VAL_TEXT_XML);
+            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*)
-            res->records[i].recordData_buf;
+            sru_rec->recordData_buf;
         npr->u.databaseRecord->u.octet_aligned->len = 
             npr->u.databaseRecord->u.octet_aligned->size = 
-            res->records[i].recordData_len;
-        record_cache_add (resultset, npr, pos);
-    }
-    if (res->num_diagnostics > 0)
-    {
-        const char *uri = res->diagnostics[0].uri;
-        if (uri)
+            sru_rec->recordData_len;
+        
+        if (sru_rec->recordSchema 
+            && !strcmp(sru_rec->recordSchema,
+                       "info:srw/schema/1/diagnostics-v1.1"))
         {
-            int code = 0;       
-            const char *cp;
-            if ((cp = strrchr(uri, '/')))
-                code = atoi(cp+1);
-            set_dset_error(c, code, uri,
-                           res->diagnostics[0].details, 0);
+            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);
     }
+    if (res->num_diagnostics > 0)
+        set_SRU_error(c, &res->diagnostics[0]);
     nmem = odr_extract_mem(c->odr_in);
-    nmem_transfer(resultset->odr->mem, nmem);
+    nmem_transfer(odr_getmem(resultset->odr), nmem);
+    nmem_destroy(nmem);
+}
+#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 HAVE_XML2
+#if YAZ_HAVE_XML2
 static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 {
     int ret = -1;
-    const char *content_type = z_HTTP_header_lookup(hres->headers,
-                                                    "Content-Type");
+    const char *addinfo = 0;
     const char *connection_head = z_HTTP_header_lookup(hres->headers,
                                                        "Connection");
-    c->mask = 0;
+    ZOOM_connection_set_mask(c, 0);
     yaz_log(log_details, "%p handle_http", c);
-
-    if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
+    
+    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] = {
-            {"http://www.loc.gov/zing/srw/", 0,
-             (Z_SOAP_fun) yaz_srw_codec},
+            {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
             {0, 0, 0}
         };
         ret = z_soap_codec(o, &soap_package,
@@ -3479,8 +4090,12 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
             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);
             if (sr->which == Z_SRW_searchRetrieve_response)
                 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;
         }
@@ -3499,8 +4114,8 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
         if (hres->code != 200)
             set_HTTP_error(c, hres->code, 0, 0);
         else
-            set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
-        do_close (c);
+            set_ZOOM_error(c, ZOOM_ERROR_DECODE, addinfo);
+        do_close(c);
     }
     ZOOM_connection_remove_task(c);
     if (!strcmp(hres->version, "1.0"))
@@ -3518,33 +4133,25 @@ static void handle_http(ZOOM_connection c, Z_HTTP_Response *hres)
 }
 #endif
 
-static int do_read (ZOOM_connection c)
+static int do_read(ZOOM_connection c)
 {
     int r, more;
     ZOOM_Event event;
     
-    event = ZOOM_Event_create (ZOOM_EVENT_RECV_DATA);
-    ZOOM_connection_put_event (c, event);
+    event = ZOOM_Event_create(ZOOM_EVENT_RECV_DATA);
+    ZOOM_connection_put_event(c, event);
     
-    r = cs_get (c->cs, &c->buf_in, &c->len_in);
+    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);
     if (r == 1)
         return 0;
     if (r <= 0)
     {
-        if (c->reconnect_ok)
-        {
-            do_close (c);
-            c->reconnect_ok = 0;
-            yaz_log(log_details, "%p do_read reconnect read", c);
-            c->tasks->running = 0;
-            ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
-        }
-        else
+        if (!ZOOM_test_reconnect(c))
         {
             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-            do_close (c);
+            do_close(c);
         }
     }
     else
@@ -3552,79 +4159,88 @@ static int do_read (ZOOM_connection c)
         Z_GDU *gdu;
         ZOOM_Event event;
 
-        odr_reset (c->odr_in);
-        odr_setbuf (c->odr_in, c->buf_in, r, 0);
-        event = ZOOM_Event_create (ZOOM_EVENT_RECV_APDU);
-        ZOOM_connection_put_event (c, event);
+        odr_reset(c->odr_in);
+        odr_setbuf(c->odr_in, c->buf_in, r, 0);
+        event = ZOOM_Event_create(ZOOM_EVENT_RECV_APDU);
+        ZOOM_connection_put_event(c, event);
 
-        if (!z_GDU (c->odr_in, &gdu, 0, 0))
+        if (!z_GDU(c->odr_in, &gdu, 0, 0))
         {
             int x;
             int err = odr_geterrorx(c->odr_in, &x);
-            char msg[60];
+            char msg[100];
             const char *element = odr_getelement(c->odr_in);
-            sprintf (msg, "ODR code %d:%d element=%-20s",
-                     err, x, element ? element : "<unknown>");
+            yaz_snprintf(msg, sizeof(msg),
+                    "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);
-            do_close (c);
+            if (log_api)
+            {
+                FILE *ber_file = yaz_log_file();
+                if (ber_file)
+                    odr_dumpBER(ber_file, c->buf_in, r);
+            }
+            do_close(c);
         }
-        else if (gdu->which == Z_GDU_Z3950)
-            recv_apdu (c, gdu->u.z3950);
-        else if (gdu->which == Z_GDU_HTTP_Response)
+        else
         {
-#if HAVE_XML2
-            handle_http (c, gdu->u.HTTP_Response);
+            if (c->odr_print)
+                z_GDU(c->odr_print, &gdu, 0, 0);
+            if (gdu->which == Z_GDU_Z3950)
+                recv_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);
+                set_ZOOM_error(c, ZOOM_ERROR_DECODE, 0);
+                do_close(c);
 #endif
+            }
         }
         c->reconnect_ok = 0;
     }
     return 1;
 }
 
-static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
+static zoom_ret do_write_ex(ZOOM_connection c, char *buf_out, int len_out)
 {
     int r;
     ZOOM_Event event;
     
     event = ZOOM_Event_create(ZOOM_EVENT_SEND_DATA);
-    ZOOM_connection_put_event (c, event);
+    ZOOM_connection_put_event(c, event);
 
     yaz_log(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);
-        if (c->reconnect_ok)
+        if (ZOOM_test_reconnect(c))
         {
-            do_close (c);
-            c->reconnect_ok = 0;
-            yaz_log(log_details, "%p do_write_ex reconnect write", c);
-            c->tasks->running = 0;
-            ZOOM_connection_insert_task (c, ZOOM_TASK_CONNECT);
             return zoom_pending;
         }
         if (c->state == STATE_CONNECTING)
             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
         else
             set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
-        do_close (c);
+        do_close(c);
         return zoom_complete;
     }
     else if (r == 1)
     {    
-        c->mask = ZOOM_SELECT_EXCEPT;
+        int mask = ZOOM_SELECT_EXCEPT;
         if (c->cs->io_pending & CS_WANT_WRITE)
-            c->mask += ZOOM_SELECT_WRITE;
+            mask += ZOOM_SELECT_WRITE;
         if (c->cs->io_pending & CS_WANT_READ)
-            c->mask += ZOOM_SELECT_READ;
+            mask += ZOOM_SELECT_READ;
+        ZOOM_connection_set_mask(c, mask);
         yaz_log(log_details, "%p do_write_ex write incomplete mask=%d",
                 c, c->mask);
     }
     else
     {
-        c->mask = ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT;
+        ZOOM_connection_set_mask(c, ZOOM_SELECT_READ|ZOOM_SELECT_EXCEPT);
         yaz_log(log_details, "%p do_write_ex write complete mask=%d",
                 c, c->mask);
     }
@@ -3633,82 +4249,82 @@ static zoom_ret do_write_ex (ZOOM_connection c, char *buf_out, int len_out)
 
 static zoom_ret do_write(ZOOM_connection c)
 {
-    return do_write_ex (c, c->buf_out, c->len_out);
+    return do_write_ex(c, c->buf_out, c->len_out);
 }
 
 
 ZOOM_API(const char *)
-ZOOM_connection_option_get (ZOOM_connection c, const char *key)
+    ZOOM_connection_option_get(ZOOM_connection c, const char *key)
 {
-    return ZOOM_options_get (c->options, key);
+    return ZOOM_options_get(c->options, key);
 }
 
 ZOOM_API(const char *)
-ZOOM_connection_option_getl (ZOOM_connection c, const char *key, int *lenp)
+    ZOOM_connection_option_getl(ZOOM_connection c, const char *key, int *lenp)
 {
-    return ZOOM_options_getl (c->options, key, lenp);
+    return ZOOM_options_getl(c->options, key, lenp);
 }
 
 ZOOM_API(void)
-ZOOM_connection_option_set (ZOOM_connection c, const char *key,
-                            const char *val)
+    ZOOM_connection_option_set(ZOOM_connection c, const char *key,
+                               const char *val)
 {
-    ZOOM_options_set (c->options, key, val);
+    ZOOM_options_set(c->options, key, val);
 }
 
 ZOOM_API(void)
-ZOOM_connection_option_setl (ZOOM_connection c, const char *key,
-                             const char *val, int len)
+    ZOOM_connection_option_setl(ZOOM_connection c, const char *key,
+                                const char *val, int len)
 {
-    ZOOM_options_setl (c->options, key, val, len);
+    ZOOM_options_setl(c->options, key, val, len);
 }
 
 ZOOM_API(const char *)
-ZOOM_resultset_option_get (ZOOM_resultset r, const char *key)
+    ZOOM_resultset_option_get(ZOOM_resultset r, const char *key)
 {
-    return ZOOM_options_get (r->options, key);
+    return ZOOM_options_get(r->options, key);
 }
 
 ZOOM_API(void)
-ZOOM_resultset_option_set (ZOOM_resultset r, const char *key,
-                                  const char *val)
+    ZOOM_resultset_option_set(ZOOM_resultset r, const char *key,
+                              const char *val)
 {
-    ZOOM_options_set (r->options, key, val);
+    ZOOM_options_set(r->options, key, val);
 }
 
 
 ZOOM_API(int)
-ZOOM_connection_errcode (ZOOM_connection c)
+    ZOOM_connection_errcode(ZOOM_connection c)
 {
-    return ZOOM_connection_error (c, 0, 0);
+    return ZOOM_connection_error(c, 0, 0);
 }
 
 ZOOM_API(const char *)
-ZOOM_connection_errmsg (ZOOM_connection c)
+    ZOOM_connection_errmsg(ZOOM_connection c)
 {
     const char *msg;
-    ZOOM_connection_error (c, &msg, 0);
+    ZOOM_connection_error(c, &msg, 0);
     return msg;
 }
 
 ZOOM_API(const char *)
-ZOOM_connection_addinfo (ZOOM_connection c)
+    ZOOM_connection_addinfo(ZOOM_connection c)
 {
     const char *addinfo;
-    ZOOM_connection_error (c, 0, &addinfo);
+    ZOOM_connection_error(c, 0, &addinfo);
     return addinfo;
 }
 
 ZOOM_API(const char *)
-ZOOM_connection_diagset (ZOOM_connection c)
+    ZOOM_connection_diagset(ZOOM_connection c)
 {
     const char *diagset;
-    ZOOM_connection_error_x (c, 0, 0, &diagset);
+    ZOOM_connection_error_x(c, 0, 0, &diagset);
     return diagset;
 }
 
 ZOOM_API(const char *)
-ZOOM_diag_str (int error)
+    ZOOM_diag_str(int error)
 {
     switch (error)
     {
@@ -3740,14 +4356,18 @@ ZOOM_diag_str (int error)
         return "CQL parsing error";
     case ZOOM_ERROR_CQL_TRANSFORM:
         return "CQL transformation error";
+    case ZOOM_ERROR_CCL_CONFIG:
+        return "CCL configuration error";
+    case ZOOM_ERROR_CCL_PARSE:
+        return "CCL parsing error";
     default:
-        return diagbib1_str (error);
+        return diagbib1_str(error);
     }
 }
 
 ZOOM_API(int)
-ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
-                         const char **addinfo, const char **diagset)
+    ZOOM_connection_error_x(ZOOM_connection c, const char **cp,
+                            const char **addinfo, const char **diagset)
 {
     int error = c->error;
     if (cp)
@@ -3771,270 +4391,98 @@ ZOOM_connection_error_x (ZOOM_connection c, const char **cp,
 }
 
 ZOOM_API(int)
-ZOOM_connection_error (ZOOM_connection c, const char **cp,
-                       const char **addinfo)
+    ZOOM_connection_error(ZOOM_connection c, const char **cp,
+                          const char **addinfo)
 {
     return ZOOM_connection_error_x(c, cp, addinfo, 0);
 }
 
-static int ZOOM_connection_do_io(ZOOM_connection c, int mask)
+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",
-             c, mask, r);
+            c, mask, r);
     
     if (r == CS_NONE)
     {
-        event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
+        event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
         set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-        do_close (c);
-        ZOOM_connection_put_event (c, event);
+        do_close(c);
+        ZOOM_connection_put_event(c, event);
     }
     else if (r == CS_CONNECT)
     {
-        int ret;
-        event = ZOOM_Event_create (ZOOM_EVENT_CONNECT);
-
-        ret = cs_rcvconnect (c->cs);
+        int ret = ret = cs_rcvconnect(c->cs);
         yaz_log(log_details, "%p ZOOM_connection_do_io "
                 "cs_rcvconnect returned %d", c, ret);
         if (ret == 1)
         {
-            c->mask = ZOOM_SELECT_EXCEPT;
+            int mask = ZOOM_SELECT_EXCEPT;
             if (c->cs->io_pending & CS_WANT_WRITE)
-                c->mask += ZOOM_SELECT_WRITE;
+                mask += ZOOM_SELECT_WRITE;
             if (c->cs->io_pending & CS_WANT_READ)
-                c->mask += ZOOM_SELECT_READ;
-            ZOOM_connection_put_event (c, event);
+                mask += ZOOM_SELECT_READ;
+            ZOOM_connection_set_mask(c, mask);
+            event = ZOOM_Event_create(ZOOM_EVENT_NONE);
+            ZOOM_connection_put_event(c, event);
         }
         else if (ret == 0)
         {
-            ZOOM_connection_put_event (c, event);
+            event = ZOOM_Event_create(ZOOM_EVENT_CONNECT);
+            ZOOM_connection_put_event(c, event);
             get_cert(c);
             if (c->proto == PROTO_Z3950)
                 ZOOM_connection_send_init(c);
             else
             {
                 /* no init request for SRW .. */
-                assert (c->tasks->which == ZOOM_TASK_CONNECT);
-                ZOOM_connection_remove_task (c);
-                c->mask = 0;
-                ZOOM_connection_exec_task (c);
+                assert(c->tasks->which == ZOOM_TASK_CONNECT);
+                ZOOM_connection_remove_task(c);
+                ZOOM_connection_set_mask(c, 0);
+                ZOOM_connection_exec_task(c);
             }
             c->state = STATE_ESTABLISHED;
         }
         else
         {
             set_ZOOM_error(c, ZOOM_ERROR_CONNECT, c->host_port);
-            do_close (c);
-            ZOOM_connection_put_event (c, event);
+            do_close(c);
         }
     }
     else
     {
+        if (mask & ZOOM_SELECT_EXCEPT)
+        {
+            if (!ZOOM_test_reconnect(c))
+            {
+                set_ZOOM_error(c, ZOOM_ERROR_CONNECTION_LOST, c->host_port);
+                do_close(c);
+            }
+            return;
+        }
         if (mask & ZOOM_SELECT_READ)
-            do_read (c);
+            do_read(c);
         if (c->cs && (mask & ZOOM_SELECT_WRITE))
-            do_write (c);
+            do_write(c);
     }
-    return 1;
 }
 
 ZOOM_API(int)
-ZOOM_connection_last_event(ZOOM_connection cs)
+    ZOOM_connection_last_event(ZOOM_connection cs)
 {
     if (!cs)
         return ZOOM_EVENT_NONE;
     return cs->last_event;
 }
 
-ZOOM_API(int)
-ZOOM_event (int no, ZOOM_connection *cs)
-{
-    int timeout = 30;      /* default timeout in seconds */
-    int timeout_set = 0;   /* whether it was overriden at all */
-#if HAVE_SYS_POLL_H
-    struct pollfd pollfds[1024];
-    ZOOM_connection poll_cs[1024];
-#else
-    struct timeval tv;
-    fd_set input, output, except;
-#endif
-    int i, r, nfds;
-    int max_fd = 0;
-
-    for (i = 0; i<no; i++)
-    {
-        ZOOM_connection c = cs[i];
-        ZOOM_Event event;
-        if (c && (event = ZOOM_connection_get_event(c)))
-        {
-            ZOOM_Event_destroy (event);
-            return i+1;
-        }
-    }
-    for (i = 0; i<no; i++)
-    {
-        ZOOM_connection c = cs[i];
-        ZOOM_Event event;
-        if (c && ZOOM_connection_exec_task (c))
-        {
-            if ((event = ZOOM_connection_get_event(c)))
-            {
-                ZOOM_Event_destroy (event);
-                return i+1;
-            }
-        }
-    }
-#if HAVE_SYS_POLL_H
-
-#else
-    FD_ZERO (&input);
-    FD_ZERO (&output);
-    FD_ZERO (&except);
-#endif
-    nfds = 0;
-    for (i = 0; i<no; i++)
-    {
-        ZOOM_connection c = cs[i];
-        int fd, mask;
-        int this_timeout;
-        
-        if (!c)
-            continue;
-        fd = z3950_connection_socket(c);
-        mask = z3950_connection_mask(c);
-
-        if (fd == -1)
-            continue;
-        if (max_fd < fd)
-            max_fd = fd;
-        
-        /* -1 is used for indefinite timeout (no timeout), so -2 here. */
-        this_timeout = ZOOM_options_get_int (c->options, "timeout", -2);
-        if (this_timeout != -2)
-        {
-            /* ensure the minimum timeout is used */
-            if (!timeout_set)
-                timeout = this_timeout;
-            else if (this_timeout != -1 && this_timeout < timeout)
-                timeout = this_timeout;
-            timeout_set = 1;
-        }               
-#if HAVE_SYS_POLL_H
-        if (mask)
-        {
-            short poll_events = 0;
-
-            if (mask & ZOOM_SELECT_READ)
-                poll_events += POLLIN;
-            if (mask & ZOOM_SELECT_WRITE)
-                poll_events += POLLOUT;
-            if (mask & ZOOM_SELECT_EXCEPT)
-                poll_events += POLLERR;
-            pollfds[nfds].fd = fd;
-            pollfds[nfds].events = poll_events;
-            pollfds[nfds].revents = 0;
-            poll_cs[nfds] = c;
-            nfds++;
-        }
-#else
-        if (mask & ZOOM_SELECT_READ)
-        {
-            FD_SET (fd, &input);
-            nfds++;
-        }
-        if (mask & ZOOM_SELECT_WRITE)
-        {
-            FD_SET (fd, &output);
-            nfds++;
-        }
-        if (mask & ZOOM_SELECT_EXCEPT)
-        {
-            FD_SET (fd, &except);
-            nfds++;
-        }
-#endif
-    }
-    if (!nfds)
-        return 0;
-
-#if HAVE_SYS_POLL_H
-    r = poll (pollfds, nfds, (timeout == -1 ? -1 : timeout * 1000));
-    for (i = 0; i<nfds; i++)
-    {
-        ZOOM_connection c = poll_cs[i];
-        if (r && c->mask)
-        {
-            int mask = 0;
-            if (pollfds[i].revents & POLLIN)
-                mask += ZOOM_SELECT_READ;
-            if (pollfds[i].revents & POLLOUT)
-                mask += ZOOM_SELECT_WRITE;
-            if (pollfds[i].revents & POLLERR)
-                mask += ZOOM_SELECT_EXCEPT;
-            if (mask)
-                ZOOM_connection_do_io(c, mask);
-        }
-        else if (r == 0 && 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_connection_put_event(c, event);
-        }
-    }
-#else
-    tv.tv_sec = timeout;
-    tv.tv_usec = 0;
-    r = select (max_fd+1, &input, &output, &except, (timeout == -1 ? 0 : &tv));
-    for (i = 0; i<no; i++)
-    {
-        ZOOM_connection c = cs[i];
-        int fd, mask;
 
-        if (!c)
-            continue;
-        fd = z3950_connection_socket(c);
-        mask = 0;
-        if (r && c->mask)
-        {
-            /* no timeout and real socket */
-            if (FD_ISSET(fd, &input))
-                mask += ZOOM_SELECT_READ;
-            if (FD_ISSET(fd, &output))
-                mask += ZOOM_SELECT_WRITE;
-            if (FD_ISSET(fd, &except))
-                mask += ZOOM_SELECT_EXCEPT;
-            if (mask)
-                ZOOM_connection_do_io(c, mask);
-        }
-        if (r == 0 && 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_connection_put_event(c, event);
-        }
-    }
-#endif
-    for (i = 0; i<no; i++)
-    {
-        ZOOM_connection c = cs[i];
-        ZOOM_Event event;
-        if (c && (event = ZOOM_connection_get_event(c)))
-        {
-            ZOOM_Event_destroy (event);
-            return i+1;
-        }
-    }
-    return 0;
+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
@@ -4045,10 +4493,9 @@ static char *cql2pqf(ZOOM_connection c, const char *cql)
 {
     CQL_parser parser;
     int error;
-    struct cql_node *node;
     const char *cqlfile;
-    static cql_transform_t trans;
-    char pqfbuf[512];
+    cql_transform_t trans;
+    char *result = 0;
 
     parser = cql_parser_create();
     if ((error = cql_parser_string(parser, cql)) != 0) {
@@ -4057,44 +4504,128 @@ static char *cql2pqf(ZOOM_connection c, const char *cql)
         return 0;
     }
 
-    node = cql_parser_result(parser);
-    /* ### Do not call cql_parser_destroy() yet: it destroys `node'! */
-
     cqlfile = ZOOM_connection_option_get(c, "cqlfile");
-    if (cqlfile == 0) {
-        cql_parser_destroy(parser);
-        cql_node_destroy(node);
+    if (cqlfile == 0) 
+    {
         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, "no CQL transform file");
-        return 0;
     }
-
-    if ((trans = cql_transform_open_fname(cqlfile)) == 0) {
+    else if ((trans = cql_transform_open_fname(cqlfile)) == 0) 
+    {
         char buf[512];        
-        cql_parser_destroy(parser);
-        cql_node_destroy(node);
         sprintf(buf, "can't open CQL transform file '%.200s': %.200s",
                 cqlfile, strerror(errno));
         set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
-        return 0;
     }
-
-    error = cql_transform_buf(trans, node, pqfbuf, sizeof pqfbuf);
-    cql_parser_destroy(parser);
-    cql_node_destroy(node);
-    if (error != 0) {
-        char buf[512];
-        const char *addinfo;
-        error = cql_transform_error(trans, &addinfo);
+    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);
-        sprintf(buf, "%.200s (addinfo=%.200s)", cql_strerror(error), addinfo);
-        set_ZOOM_error(c, ZOOM_ERROR_CQL_TRANSFORM, buf);
+        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_connection_put_event(c, event);
+    }
+    return 0;
+}
+
+ZOOM_API(int)
+    ZOOM_connection_process(ZOOM_connection c)
+{
+    ZOOM_Event event;
+    if (!c)
         return 0;
+
+    event = ZOOM_connection_get_event(c);
+    if (event)
+    {
+        ZOOM_Event_destroy(event);
+        return 1;
+    }
+    ZOOM_connection_exec_task(c);
+    event = ZOOM_connection_get_event(c);
+    if (event)
+    {
+        ZOOM_Event_destroy(event);
+        return 1;
+    }
+    return 0;
+}
+
+ZOOM_API(int)
+    ZOOM_event_nonblock(int no, ZOOM_connection *cs)
+{
+    int i;
+
+    yaz_log(log_details, "ZOOM_process_event(no=%d,cs=%p)", no, cs);
+    
+    for (i = 0; i<no; i++)
+    {
+        ZOOM_connection c = cs[i];
+
+        if (c && ZOOM_connection_process(c))
+            return i+1;
     }
+    return 0;
+}
 
-    cql_transform_close(trans);
-    return xstrdup(pqfbuf);
+ZOOM_API(int) ZOOM_connection_fire_event_socket(ZOOM_connection c, int mask)
+{
+    if (c->mask && mask)
+        ZOOM_connection_do_io(c, mask);
+    return 0;
 }
 
+ZOOM_API(int) ZOOM_connection_get_socket(ZOOM_connection c)
+{
+    if (c->cs)
+        return cs_fileno(c->cs);
+    return -1;
+}
+
+ZOOM_API(int) ZOOM_connection_set_mask(ZOOM_connection c, int mask)
+{
+    c->mask = mask;
+    if (!c->cs)
+        return -1; 
+    return 0;
+}
+
+ZOOM_API(int) ZOOM_connection_get_mask(ZOOM_connection c)
+{
+    if (c->cs)
+        return c->mask;
+    return 0;
+}
+
+ZOOM_API(int) ZOOM_connection_get_timeout(ZOOM_connection c)
+{
+    return ZOOM_options_get_int(c->options, "timeout", 30);
+}
 
 /*
  * Local variables: