Allow YAZ 2 series only
[ir-tcl-moved-to-github.git] / ir-tcl.c
index 1048cf0..9b4ee5c 100644 (file)
--- a/ir-tcl.c
+++ b/ir-tcl.c
@@ -1,11 +1,88 @@
 /*
  * IR toolkit for tcl/tk
- * (c) Index Data 1995-1997
+ * (c) Index Data 1995-2003
  * See the file LICENSE for details.
- * Sebastian Hammer, Adam Dickmeiss
  *
  * $Log: ir-tcl.c,v $
- * Revision 1.103  1997-11-19 11:22:10  adam
+ * Revision 1.128  2005-03-10 13:54:56  adam
+ * Remove CCL support for scan
+ *
+ * Revision 1.127  2004/05/10 08:38:45  adam
+ * Do not use obsolete YAZ defines
+ *
+ * Revision 1.126  2003/11/29 17:24:09  adam
+ * Added getXml method (Franck Falcoz)
+ *
+ * Revision 1.125  2003/04/29 10:51:23  adam
+ * Null terminate octet aligned records
+ *
+ * Revision 1.124  2003/03/05 22:02:47  adam
+ * Add Tcl_InitStubs
+ *
+ * Revision 1.123  2003/03/05 21:21:41  adam
+ * APDU log. default largeSetLowerBound changed from 2 to 1
+ *
+ * Revision 1.122  2003/03/05 18:02:08  adam
+ * Fix bug with idAuthentication that didn't work for empty group.
+ *
+ * Revision 1.121  2003/01/30 13:27:07  adam
+ * Changed version to 1.4.1. Added WIN32 version resource.
+ * IrTcl ignores unexpected PDU's, rather than die.
+ *
+ * Revision 1.120  2002/03/20 14:48:54  adam
+ * implemented USR.1 SearchResult-1
+ *
+ * Revision 1.119  2001/12/03 00:31:06  adam
+ * Towards 1.4. Configure updates.
+ *
+ * Revision 1.118  2001/03/27 16:27:21  adam
+ * Fixed bug in do_responseStatus.
+ *
+ * Revision 1.117  2001/03/26 11:39:34  adam
+ * Fixed bug in ir_deleteDiags - crash when receiving multiple diags.
+ *
+ * Revision 1.116  2001/02/09 11:58:04  adam
+ * Updated for Tcl8.1 and higher where internal encoding is UTF-8.
+ *
+ * Revision 1.115  2000/09/13 12:18:49  adam
+ * Logging utility patch (YAZ version 1.7).
+ *
+ * Revision 1.114  1999/05/17 20:37:41  adam
+ * Fixed problem with ASN code.
+ *
+ * Revision 1.113  1999/04/20 10:01:46  adam
+ * Modified calls to ODR encoders/decoders (name argument).
+ *
+ * Revision 1.112  1999/03/22 06:51:34  adam
+ * Implemented sort.
+ *
+ * Revision 1.111  1999/02/11 11:30:09  adam
+ * Updated for WIN32.
+ *
+ * Revision 1.110  1998/10/20 15:15:31  adam
+ * Changed scan response handler.
+ *
+ * Revision 1.109  1998/10/13 21:23:26  adam
+ * Fixed searchStatus method.
+ *
+ * Revision 1.108  1998/10/12 11:48:08  adam
+ * Removed printf call.
+ *
+ * Revision 1.107  1998/06/10 13:00:46  adam
+ * Added ir-version command.
+ *
+ * Revision 1.106  1998/05/20 12:25:35  adam
+ * Fixed bug that occurred in rare cases when encoding of incoming
+ * records failed.
+ *
+ * Revision 1.105  1998/04/02 14:31:08  adam
+ * This version works with compiled ASN.1 code.
+ *
+ * Revision 1.104  1998/02/27 14:26:07  adam
+ * Changed client so that it still works if target sets numberOfRecords
+ * in response to an illegal value.
+ *
+ * Revision 1.103  1997/11/19 11:22:10  adam
  * Object identifiers can be accessed in GRS-1 records.
  *
  * Revision 1.102  1997/09/17 12:22:40  adam
 
 #include <stdlib.h>
 #include <stdio.h>
-#ifdef WINDOWS
+#ifdef WIN32
 
 #else
 #include <unistd.h>
 #endif
 
 static char *wrongArgs = "wrong # args: should be \"";
+static FILE *odr_print_file = 0;
 
 static int ir_tcl_error_exec (Tcl_Interp *interp, int argc, char **argv)
 {
@@ -465,6 +543,17 @@ static void delete_IR_record (IrTcl_RecordList *rl)
     xfree (rl->elements);
 }
 
+static void purge_IR_records (IrTcl_SetObj *setobj)
+{
+    IrTcl_RecordList *rl;
+    while ((rl = setobj->record_list))
+    {
+        setobj->record_list = rl->next;
+        delete_IR_record (rl);
+        xfree (rl);
+    } 
+}
+
 static IrTcl_RecordList *new_IR_record (IrTcl_SetObj *setobj, 
                                         int no, int which, 
                                         const char *elements)
@@ -646,13 +735,14 @@ int ir_tcl_named_bits (struct ir_named_entry *tab, Odr_bitmask *ob,
         ODR_MASK_ZERO (ob);
         for (no = 0; no < argc; no++)
         {
+           int ok = 0;
             for (ti = tab; ti->name; ti++)
-                if (!strcmp (argv[no], ti->name))
+                if (!strcmp(argv[no], "@all") || !strcmp (argv[no], ti->name))
                 {
                     ODR_MASK_SET (ob, ti->pos);
-                    break;
+                    ok = 1;
                 }
-            if (!ti->name)
+            if (!ok)
             {
                 Tcl_AppendResult (interp, "bad bit mask ", argv[no], NULL);
                 return ir_tcl_error_exec (interp, argc, argv);
@@ -721,7 +811,7 @@ static int do_init_request (void *obj, Tcl_Interp *interp,
     req->preferredMessageSize = &p->preferredMessageSize;
     req->maximumRecordSize = &p->maximumRecordSize;
 
-    if (p->idAuthenticationGroupId)
+    if (p->idAuthenticationGroupId || p->idAuthenticationUserId)
     {
         Z_IdPass *pass = odr_malloc (p->odr_out, sizeof(*pass));
         Z_IdAuthentication *auth = odr_malloc (p->odr_out, sizeof(*auth));
@@ -744,9 +834,7 @@ static int do_init_request (void *obj, Tcl_Interp *interp,
             pass->password = NULL;
         req->idAuthentication = auth;
     }
-    else if (!p->idAuthenticationOpen || !*p->idAuthenticationOpen)
-        req->idAuthentication = NULL;
-    else
+    else if (p->idAuthenticationOpen && *p->idAuthenticationOpen)
     {
         Z_IdAuthentication *auth = odr_malloc (p->odr_out, sizeof(*auth));
 
@@ -755,6 +843,8 @@ static int do_init_request (void *obj, Tcl_Interp *interp,
         auth->u.open = p->idAuthenticationOpen;
         req->idAuthentication = auth;
     }
+    else
+        req->idAuthentication = NULL;
     req->implementationId = p->implementationId;
     req->implementationName = p->implementationName;
     req->implementationVersion = p->implementationVersion;
@@ -980,7 +1070,7 @@ static int do_implementationName (void *obj, Tcl_Interp *interp,
 
     if (argc == 0)
         return ir_tcl_strdup (interp, &p->implementationName,
-                          "Index Data/IrTcl on YAZ");
+                          "IrTcl/YAZ");
     else if (argc == -1)
         return ir_tcl_strdel (interp, &p->implementationName);
     if (argc == 3)
@@ -1003,7 +1093,7 @@ static int do_implementationId (void *obj, Tcl_Interp *interp,
     IrTcl_Obj *p = obj;
 
     if (argc == 0)
-        return ir_tcl_strdup (interp, &p->implementationId, "YAZ (id=81)");
+        return ir_tcl_strdup (interp, &p->implementationId, "81");
     else if (argc == -1)
         return ir_tcl_strdel (interp, &p->implementationId);
     Tcl_AppendResult (interp, p->implementationId, (char*) NULL);
@@ -1020,10 +1110,10 @@ static int do_implementationVersion (void *obj, Tcl_Interp *interp,
 
     if (argc == 0)
         return ir_tcl_strdup (interp, &p->implementationVersion, 
-                          "YAZ: " YAZ_VERSION
 #ifdef IR_TCL_VERSION
-                          " / Irtcl: " IR_TCL_VERSION
+                          IR_TCL_VERSION "/"
 #endif
+                          YAZ_VERSION
                           );
     else if (argc == -1)
         return ir_tcl_strdel (interp, &p->implementationVersion);
@@ -1116,6 +1206,12 @@ static int do_idAuthentication (void *obj, Tcl_Interp *interp,
     {
         if (argc == 3)
         {
+            xfree (p->idAuthenticationGroupId);
+            xfree (p->idAuthenticationUserId);
+            xfree (p->idAuthenticationPassword);
+            p->idAuthenticationGroupId = NULL;
+            p->idAuthenticationUserId = NULL;
+            p->idAuthenticationPassword = NULL;
             if (argv[2][0] && 
                 ir_tcl_strdup (interp, &p->idAuthenticationOpen, argv[2])
                 == TCL_ERROR)
@@ -1123,6 +1219,8 @@ static int do_idAuthentication (void *obj, Tcl_Interp *interp,
         }
         else if (argc == 5)
         {
+            xfree (p->idAuthenticationOpen);
+            p->idAuthenticationOpen = NULL;
             if (argv[2][0] && 
                 ir_tcl_strdup (interp, &p->idAuthenticationGroupId, argv[2])
                 == TCL_ERROR)
@@ -1139,7 +1237,7 @@ static int do_idAuthentication (void *obj, Tcl_Interp *interp,
     }
     if (p->idAuthenticationOpen)
         Tcl_AppendElement (interp, p->idAuthenticationOpen);
-    else if (p->idAuthenticationGroupId)
+    else if (p->idAuthenticationGroupId || p->idAuthenticationUserId)
     {
         Tcl_AppendElement (interp, p->idAuthenticationGroupId);
         Tcl_AppendElement (interp, p->idAuthenticationUserId);
@@ -1320,11 +1418,11 @@ static int do_logLevel (void *o, Tcl_Interp *interp,
     if (argc <= 2)
         return TCL_OK;
     if (argc == 3)
-        log_init (log_mask_str (argv[2]), "", NULL);
+        yaz_log_init (yaz_log_mask_str (argv[2]), "", NULL);
     else if (argc == 4)
-        log_init (log_mask_str (argv[2]), argv[3], NULL);
+        yaz_log_init (yaz_log_mask_str (argv[2]), argv[3], NULL);
     else if (argc == 5)
-        log_init (log_mask_str (argv[2]), argv[3], argv[4]);
+        yaz_log_init (yaz_log_mask_str (argv[2]), argv[3], argv[4]);
     return TCL_OK;
 }
 
@@ -1490,7 +1588,7 @@ static int do_triggerResourceControl (void *obj, Tcl_Interp *interp,
     }
     apdu = zget_APDU (p->odr_out, Z_APDU_triggerResourceControlRequest);
     req = apdu->u.triggerResourceControlRequest;
-    *req->requestedAction = Z_TriggerResourceCtrl_cancel;
+    *req->requestedAction = Z_TriggerResourceControlRequest_cancel;
     req->resultSetWanted = &is_false; 
     
     return ir_tcl_send_APDU (interp, p, apdu, "triggerResourceControl",
@@ -1626,7 +1724,7 @@ static int do_largeSetLowerBound (void *o, Tcl_Interp *interp,
 
     if (argc <= 0)
     {
-        p->largeSetLowerBound = 2;
+        p->largeSetLowerBound = 1;
         return TCL_OK;
     }
     return ir_tcl_get_set_int (&p->largeSetLowerBound, interp, argc, argv);
@@ -1886,7 +1984,11 @@ static void ir_obj_delete (ClientData clientData)
     ir_tcl_del_q (obj);
     odr_destroy (obj->odr_in);
     odr_destroy (obj->odr_out);
-    odr_destroy (obj->odr_pr);
+    if (obj->odr_pr)
+    {
+        obj->odr_pr->print = 0;
+        odr_destroy (obj->odr_pr);
+    }
     xfree (obj);
 }
 
@@ -1923,7 +2025,12 @@ int ir_obj_init (ClientData clientData, Tcl_Interp *interp,
     obj->odr_in = odr_createmem (ODR_DECODE);
     odr_choice_enable_bias (obj->odr_in, 0);
     obj->odr_out = odr_createmem (ODR_ENCODE);
-    obj->odr_pr = odr_createmem (ODR_PRINT);
+    obj->odr_pr = 0;
+    if (odr_print_file)
+    {
+        obj->odr_pr = odr_createmem (ODR_PRINT);
+       odr_setprint(obj->odr_pr, odr_print_file);
+    }
     obj->state = IR_TCL_R_Idle;
     obj->interp = interp;
 
@@ -1983,7 +2090,11 @@ static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
     Odr_oct ccl_query;
     IrTcl_SetObj *obj = o;
     IrTcl_Obj *p;
-    int r;
+    int r, code;
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    Tcl_DString ds;
+#endif
+    char *query_str;
 
     if (argc <= 0)
         return TCL_OK;
@@ -1996,16 +2107,23 @@ static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
                           NULL);
         return TCL_ERROR;
     }
-    logf (LOG_DEBUG, "search %s %s", *argv, argv[2]);
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    query_str = Tcl_UtfToExternalDString(0, argv[2], -1, &ds);
+#else
+    query_str = argv[2];
+#endif
+    logf (LOG_DEBUG, "search %s %s", *argv, query_str);
     if (!obj->set_inher.num_databaseNames)
     {
         Tcl_AppendResult (interp, "no databaseNames", NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
     if (!p->cs_link)
     {
         Tcl_AppendResult (interp, "not connected", NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
     apdu = zget_APDU (p->odr_out, Z_APDU_searchRequest);
     req = apdu->u.searchRequest;
@@ -2070,11 +2188,12 @@ static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
     {
         Z_RPNQuery *RPNquery;
 
-        RPNquery = p_query_rpn (p->odr_out, p->protocol_type, argv[2]);
+        RPNquery = p_query_rpn (p->odr_out, p->protocol_type, query_str);
         if (!RPNquery)
         {
             Tcl_AppendResult (interp, "query syntax error", NULL);
-            return ir_tcl_error_exec (interp, argc, argv);
+            code = ir_tcl_error_exec (interp, argc, argv);
+           goto out;
         }
         query.which = Z_Query_type_1;
         query.u.type_1 = RPNquery;
@@ -2092,18 +2211,19 @@ static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
         bib1.oclass = CLASS_ATTSET;
         bib1.value = VAL_BIB1;
 
-        rpn = ccl_find_str(p->bibset, argv[2], &error, &pos);
+        rpn = ccl_find_str(p->bibset, query_str, &error, &pos);
         if (error)
         {
             Tcl_AppendResult (interp, "ccl syntax error ", ccl_err_msg(error),
                               NULL);
-            return ir_tcl_error_exec (interp, argc, argv);
+            code = ir_tcl_error_exec (interp, argc, argv);
+           goto out;
         }
 #if 0
         ccl_pr_tree (rpn, stderr);
         fprintf (stderr, "\n");
 #endif
-        assert((RPNquery = ccl_rpn_query(rpn)));
+        RPNquery = ccl_rpn_query(p->odr_out, rpn);
         RPNquery->attributeSetId = oid_getoidbyent (&bib1);
         query.which = Z_Query_type_1;
         query.u.type_1 = RPNquery;
@@ -2113,16 +2233,22 @@ static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
     {
         query.which = Z_Query_type_2;
         query.u.type_2 = &ccl_query;
-        ccl_query.buf = (unsigned char *) argv[2];
-        ccl_query.len = strlen (argv[2]);
+        ccl_query.buf = (unsigned char *) query_str;
+        ccl_query.len = strlen (query_str);
     }
     else
     {
         Tcl_AppendResult (interp, "invalid query method ",
                           obj->set_inher.queryType, NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
-    return ir_tcl_send_APDU (interp, p, apdu, "search", *argv);
+    code = ir_tcl_send_APDU (interp, p, apdu, "search", *argv);
+ out:
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    Tcl_DStringFree (&ds);
+#endif
+    return code;
 }
 
 /*
@@ -2186,6 +2312,36 @@ static int do_presentResponse (void *o, Tcl_Interp *interp,
 }
 
 /*
+ * do_sortResponse: add sort response handler
+ */
+static int do_sortResponse (void *o, Tcl_Interp *interp,
+                            int argc, char **argv)
+{
+    IrTcl_SetObj *obj = o;
+
+    if (argc == 0)
+    {
+        obj->sortResponse = NULL;
+        return TCL_OK;
+    }
+    else if (argc == -1)
+        return ir_tcl_strdel (interp, &obj->sortResponse);
+    if (argc == 3)
+    {
+        xfree (obj->sortResponse);
+        if (argv[2][0])
+        {
+            if (ir_tcl_strdup (interp, &obj->sortResponse, argv[2])
+                == TCL_ERROR)
+                return TCL_ERROR;
+        }
+        else
+            obj->sortResponse = NULL;
+    }
+    return TCL_OK;
+}
+
+/*
  * do_resultCount: Get number of hits
  */
 static int do_resultCount (void *o, Tcl_Interp *interp,
@@ -2214,6 +2370,54 @@ static int do_searchStatus (void *o, Tcl_Interp *interp,
     return ir_tcl_get_set_int (&obj->searchStatus, interp, argc, argv);
 }
 
+static void reset_searchResult (IrTcl_SetObj *setobj)
+{
+    int i;
+    for (i = 0; i<setobj->searchResult_num; i++)
+        xfree (setobj->searchResult_terms[i]);
+    xfree (setobj->searchResult_terms);
+    xfree (setobj->searchResult_count);
+    setobj->searchResult_terms = 0;
+    setobj->searchResult_num = 0;   
+}
+
+
+/*
+ * do_searchResult Get USR:Search-Result1 (after search response)
+ */
+static int do_searchResult (void *o, Tcl_Interp *interp,
+                            int argc, char **argv)
+{
+    IrTcl_SetObj *obj = o;
+    int i;
+    
+    if (argc == 0)
+    {
+        obj->searchResult_num = 0;
+        obj->searchResult_terms = 0;
+        obj->searchResult_count = 0;
+        return TCL_OK;
+    }
+    else if (argc == -1)
+    {
+        reset_searchResult (obj);
+        return TCL_OK;
+    }
+    for (i = 0; i<obj->searchResult_num; i++)
+    {
+        char str[40];
+        sprintf (str, "%d", obj->searchResult_count[i]);
+        Tcl_AppendResult (interp, "{", NULL);
+        if (obj->searchResult_terms[i])
+            Tcl_AppendElement (interp, obj->searchResult_terms[i]); 
+        else
+            Tcl_AppendElement (interp, ""); 
+        Tcl_AppendElement (interp, str);
+        Tcl_AppendResult (interp, "} ", NULL);
+    }
+    return TCL_OK;
+}
+
 /*
  * do_presentStatus: Get search status (after search/present response)
  */
@@ -2228,6 +2432,35 @@ static int do_presentStatus (void *o, Tcl_Interp *interp,
 }
 
 /*
+ * do_sortStatus: Get sort status (after sort response)
+ */
+static int do_sortStatus (void *o, Tcl_Interp *interp,
+                         int argc, char **argv)
+{
+    IrTcl_SetObj *obj = o;
+    char *res;
+
+    if (argc <= 0)
+    {
+       obj->sortStatus = Z_SortResponse_failure;
+        return TCL_OK;
+    }
+    switch (obj->sortStatus)
+    {
+    case Z_SortResponse_success:
+        res = "success"; break;
+    case Z_SortResponse_partial_1:
+        res = "partial"; break;
+    case Z_SortResponse_failure:
+        res = "failure"; break;
+    default:
+       res = "unknown"; break;
+    }
+    Tcl_AppendElement (interp, res);
+    return TCL_OK;
+}
+
+/*
  * do_nextResultSetPosition: Get next result set position
  *       (after search/present response)
  */
@@ -2532,6 +2765,41 @@ static int do_getSutrs (void *o, Tcl_Interp *interp, int argc, char **argv)
     return TCL_OK;
 }
 
+/*
+ * do_getXml: Get XML Record
+ */
+static int do_getXml (void *o, Tcl_Interp *interp, int argc, char **argv)
+{
+    IrTcl_SetObj *obj = o;
+    int offset;
+    IrTcl_RecordList *rl;
+
+    if (argc <= 0)
+        return TCL_OK;
+    if (argc != 3)
+    {
+        Tcl_AppendResult (interp, wrongArgs, *argv, " ", argv[1],
+                          " position\"", NULL);
+        return TCL_ERROR;
+    }
+    if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
+        return TCL_ERROR;
+    rl = find_IR_record (obj, offset);
+    if (!rl)
+    {
+        Tcl_AppendResult (interp, "No record at #", argv[2], NULL);
+        return TCL_ERROR;
+    }
+    if (rl->which != Z_NamePlusRecord_databaseRecord)
+    {
+        Tcl_AppendResult (interp, "No DB record at #", argv[2], NULL);
+        return TCL_ERROR;
+    }
+    if (!rl->u.dbrec.buf || rl->u.dbrec.type != VAL_TEXT_XML)
+        return TCL_OK;
+    Tcl_AppendElement (interp, rl->u.dbrec.buf);
+    return TCL_OK;
+}
 
 /*
  * do_getGrs: Get a GRS-1 Record
@@ -2609,7 +2877,7 @@ static int do_getExplain (void *o, Tcl_Interp *interp, int argc, char **argv)
         return TCL_OK;
     assert (rl->u.dbrec.buf);
     odr_setbuf (p->odr_in, rl->u.dbrec.buf, rl->u.dbrec.size, 0);
-    if (!(*etype->fun)(p->odr_in, &rr, 0))
+    if (!(*etype->fun)(p->odr_in, (char **) &rr, 0, 0))
         return TCL_OK;
     
     if (etype->what != Z_External_explainRecord)
@@ -2653,6 +2921,10 @@ static int do_responseStatus (void *o, Tcl_Interp *interp,
         Tcl_AppendElement (interp, "NSD");
         return ir_diagResult (interp, obj->nonSurrogateDiagnosticList,
                               obj->nonSurrogateDiagnosticNum);
+    case Z_Records_multipleNSD:
+        Tcl_AppendElement (interp, "NSD");
+        return ir_diagResult (interp, obj->nonSurrogateDiagnosticList,
+                               obj->nonSurrogateDiagnosticNum);
     }
     return TCL_OK;
 }
@@ -2896,6 +3168,171 @@ static int do_saveFile (void *o, Tcl_Interp *interp,
 }
 
 
+/* ------------------------------------------------------- */
+/*
+ * do_sort: Do sort request
+ */
+static int do_sort (void *o, Tcl_Interp *interp, int argc, char **argv)
+{
+    Z_SortRequest *req;
+    Z_APDU *apdu;
+    IrTcl_SetObj *obj = o;
+    IrTcl_Obj *p;
+    char sort_string[64], sort_flags[64];
+    char *arg;
+    int off;
+    Z_SortKeySpecList *sksl;
+    int oid[OID_SIZE];
+    oident bib1;
+
+    if (argc <= 0)
+        return TCL_OK;
+
+    p = obj->parent;
+    assert (argc > 1);
+    if (argc != 3)
+    {
+       Tcl_AppendResult (interp, wrongArgs, *argv, " ", argv[1], "query\"",
+                          NULL);
+        return TCL_ERROR;
+    }
+    logf (LOG_DEBUG, "sort %s %s", *argv, argv[2]);
+    if (!p->cs_link)
+    {
+        Tcl_AppendResult (interp, "not connected", NULL);
+        return ir_tcl_error_exec (interp, argc, argv);
+    }
+    apdu = zget_APDU (p->odr_out, Z_APDU_sortRequest);
+    sksl = (Z_SortKeySpecList *) odr_malloc (p->odr_out, sizeof(*sksl));
+    req = apdu->u.sortRequest;
+
+    set_referenceId (p->odr_out, &req->referenceId,
+                     obj->set_inher.referenceId);
+
+#ifdef ASN_COMPILED
+    req->num_inputResultSetNames = 1;
+    req->inputResultSetNames = (Z_InternationalString **)
+        odr_malloc (p->odr_out, sizeof(*req->inputResultSetNames));
+    req->inputResultSetNames[0] = obj->setName;
+#else
+    req->inputResultSetNames =
+        (Z_StringList *)odr_malloc (p->odr_out,
+                                   sizeof(*req->inputResultSetNames));
+    req->inputResultSetNames->num_strings = 1;
+    req->inputResultSetNames->strings =
+        (char **)odr_malloc (p->odr_out,
+                            sizeof(*req->inputResultSetNames->strings));
+    req->inputResultSetNames->strings[0] = obj->setName;
+#endif
+
+    req->sortedResultSetName = (char *) obj->setName;
+
+
+    req->sortSequence = sksl;
+    sksl->num_specs = 0;
+    sksl->specs = (Z_SortKeySpec **)
+       odr_malloc (p->odr_out, sizeof(sksl->specs) * 20);
+    
+    bib1.proto = PROTO_Z3950;
+    bib1.oclass = CLASS_ATTSET;
+    bib1.value = VAL_BIB1;
+    arg = argv[2];
+    while ((sscanf (arg, "%63s %63s%n", sort_string, sort_flags, &off)) == 2 
+           && off > 1)
+    {
+        int i;
+        char *sort_string_sep;
+        Z_SortKeySpec *sks = (Z_SortKeySpec *)
+           odr_malloc (p->odr_out, sizeof(*sks));
+        Z_SortKey *sk = (Z_SortKey *)
+           odr_malloc (p->odr_out, sizeof(*sk));
+
+        arg += off;
+        sksl->specs[sksl->num_specs++] = sks;
+        sks->sortElement = (Z_SortElement *)
+           odr_malloc (p->odr_out, sizeof(*sks->sortElement));
+        sks->sortElement->which = Z_SortElement_generic;
+        sks->sortElement->u.generic = sk;
+        
+        if ((sort_string_sep = strchr (sort_string, '=')))
+        {
+            Z_AttributeElement *el = (Z_AttributeElement *)
+               odr_malloc (p->odr_out, sizeof(*el));
+            sk->which = Z_SortKey_sortAttributes;
+            sk->u.sortAttributes =
+                (Z_SortAttributes *)
+               odr_malloc (p->odr_out, sizeof(*sk->u.sortAttributes));
+            sk->u.sortAttributes->id = oid_ent_to_oid(&bib1, oid);
+            sk->u.sortAttributes->list =
+                (Z_AttributeList *)
+               odr_malloc (p->odr_out, sizeof(*sk->u.sortAttributes->list));
+            sk->u.sortAttributes->list->num_attributes = 1;
+            sk->u.sortAttributes->list->attributes =
+                (Z_AttributeElement **)odr_malloc (p->odr_out,
+                            sizeof(*sk->u.sortAttributes->list->attributes));
+            sk->u.sortAttributes->list->attributes[0] = el;
+            el->attributeSet = 0;
+            el->attributeType = (int *)
+               odr_malloc (p->odr_out, sizeof(*el->attributeType));
+            *el->attributeType = atoi (sort_string);
+            el->which = Z_AttributeValue_numeric;
+            el->value.numeric = (int *)
+               odr_malloc (p->odr_out, sizeof(*el->value.numeric));
+            *el->value.numeric = atoi (sort_string_sep + 1);
+        }
+        else
+        {
+            sk->which = Z_SortKey_sortField;
+            sk->u.sortField = (char *)odr_malloc (p->odr_out, strlen(sort_string)+1);
+            strcpy (sk->u.sortField, sort_string);
+        }
+        sks->sortRelation = (int *)
+           odr_malloc (p->odr_out, sizeof(*sks->sortRelation));
+        *sks->sortRelation = Z_SortKeySpec_ascending;
+        sks->caseSensitivity = (int *)
+           odr_malloc (p->odr_out, sizeof(*sks->caseSensitivity));
+        *sks->caseSensitivity = Z_SortKeySpec_caseSensitive;
+       
+#ifdef ASN_COMPILED
+        sks->which = Z_SortKeySpec_null;
+        sks->u.null = odr_nullval ();
+#else
+        sks->missingValueAction = NULL;
+#endif
+       
+        for (i = 0; sort_flags[i]; i++)
+        {
+            switch (sort_flags[i])
+            {
+            case 'a':
+            case 'A':
+            case '>':
+                *sks->sortRelation = Z_SortKeySpec_descending;
+                break;
+            case 'd':
+            case 'D':
+            case '<':
+                *sks->sortRelation = Z_SortKeySpec_ascending;
+                break;
+            case 'i':
+            case 'I':
+                *sks->caseSensitivity = Z_SortKeySpec_caseInsensitive;
+                break;
+            case 'S':
+            case 's':
+                *sks->caseSensitivity = Z_SortKeySpec_caseSensitive;
+                break;
+            }
+        }
+    }
+    if (!sksl->num_specs)
+    {
+        printf ("Missing sort specifications\n");
+        return -1;
+    }
+    return ir_tcl_send_APDU (interp, p, apdu, "sort", *argv);
+}
+
 static IrTcl_Method ir_set_method_tab[] = {
     { "search",                  do_search, NULL},
     { "searchResponse",          do_searchResponse, NULL},
@@ -2910,6 +3347,7 @@ static IrTcl_Method ir_set_method_tab[] = {
     { "type",                    do_type, NULL},
     { "getMarc",                 do_getMarc, NULL},
     { "getSutrs",                do_getSutrs, NULL},
+    { "getXml",                  do_getXml, NULL},
     { "getGrs",                  do_getGrs, NULL},
     { "getExplain",              do_getExplain, NULL},
     { "recordType",              do_recordType, NULL},
@@ -2918,6 +3356,10 @@ static IrTcl_Method ir_set_method_tab[] = {
     { "responseStatus",          do_responseStatus, NULL},
     { "loadFile",                do_loadFile, NULL},
     { "saveFile",                do_saveFile, NULL},
+    { "sort",                    do_sort, NULL },
+    { "sortResponse",            do_sortResponse, NULL},
+    { "sortStatus",              do_sortStatus, NULL},
+    { "searchResult",            do_searchResult, NULL},
     { NULL, NULL}
 };
 
@@ -3103,10 +3545,16 @@ static int do_scan (void *o, Tcl_Interp *interp, int argc, char **argv)
     Z_APDU *apdu;
     IrTcl_ScanObj *obj = o;
     IrTcl_Obj *p = obj->parent;
+    char *start_term;
+    int code;
 #if CCL2RPN
     oident bib1;
     struct ccl_rpn_node *rpn;
     int pos;
+    int r;
+#endif
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    Tcl_DString ds;
 #endif
 
     if (argc <= 0)
@@ -3117,16 +3565,23 @@ static int do_scan (void *o, Tcl_Interp *interp, int argc, char **argv)
                           " scanQuery\"", NULL);
         return TCL_ERROR;
     }
-    logf (LOG_DEBUG, "scan %s %s", *argv, argv[2]);
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    start_term = Tcl_UtfToExternalDString(0, argv[2], -1, &ds);
+#else
+    start_term = argv[2];
+#endif
+    logf (LOG_DEBUG, "scan %s %s", *argv, start_term);
     if (!p->set_inher.num_databaseNames)
     {
         Tcl_AppendResult (interp, "no databaseNames", NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
     if (!p->cs_link)
     {
         Tcl_AppendResult (interp, "not connected", NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
 
     apdu = zget_APDU (p->odr_out, Z_APDU_scanRequest);
@@ -3136,29 +3591,14 @@ static int do_scan (void *o, Tcl_Interp *interp, int argc, char **argv)
     req->num_databaseNames = p->set_inher.num_databaseNames;
     req->databaseNames = p->set_inher.databaseNames;
 
-#if !CCL2RPN
     if (!(req->termListAndStartPoint =
           p_query_scan (p->odr_out, p->protocol_type,
-                        &req->attributeSet, argv[2])))
+                        &req->attributeSet, start_term)))
     {
         Tcl_AppendResult (interp, "query syntax error", NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+       code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
-#else
-    rpn = ccl_find_str(p->bibset, argv[2], &r, &pos);
-    if (r)
-    {
-        Tcl_AppendResult (interp, "ccl syntax error ", ccl_err_msg(r), NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
-    }
-    bib1.proto = p->protocol_type;
-    bib1.oclass = CLASS_ATTSET;
-    bib1.value = VAL_BIB1;
-
-    req->attributeSet = oid_getoidbyent (&bib1);
-    if (!(req->termListAndStartPoint = ccl_scan_query (rpn)))
-        return TCL_ERROR;
-#endif
     req->stepSize = &obj->stepSize;
     req->numberOfTermsRequested = &obj->numberOfTermsRequested;
     req->preferredPositionInResponse = &obj->preferredPositionInResponse;
@@ -3168,7 +3608,12 @@ static int do_scan (void *o, Tcl_Interp *interp, int argc, char **argv)
     logf (LOG_DEBUG, "preferredPositionInResponse=%d",
           *req->preferredPositionInResponse);
     
-    return ir_tcl_send_APDU (interp, p, apdu, "scan", *argv);
+    code = ir_tcl_send_APDU (interp, p, apdu, "scan", *argv);
+ out:
+#if TCL_MAJOR_VERSION > 8 || (TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION > 0)
+    Tcl_DStringFree (&ds);
+#endif
+    return code;
 }
 
 /*
@@ -3325,8 +3770,7 @@ static int do_scanLine (void *obj, Tcl_Interp *interp, int argc, char **argv)
     }
     if (Tcl_GetInt (interp, argv[2], &i) == TCL_ERROR)
         return TCL_ERROR;
-    if (!p->entries_flag || p->which != Z_ListEntries_entries || !p->entries
-        || i >= p->num_entries || i < 0)
+    if (!p->entries_flag || !p->entries || i >= p->num_entries || i < 0)
         return TCL_OK;
     switch (p->entries[i].which)
     {
@@ -3444,18 +3888,24 @@ static int ir_scan_obj_mk (ClientData clientData, Tcl_Interp *interp,
 static int ir_log_init_proc (ClientData clientData, Tcl_Interp *interp,
                              int argc, char **argv)
 {
+    int lev;
     if (argc <= 1 || argc > 4)
     {
         Tcl_AppendResult (interp, wrongArgs, *argv,
                           " ?level ?prefix ?filename\"", NULL);
         return TCL_OK;
     }
+    lev = yaz_log_mask_str (argv[1]);
     if (argc == 2)
-        log_init (log_mask_str (argv[1]), "", NULL);
+        yaz_log_init (lev, "", NULL);
     else if (argc == 3)
-        log_init (log_mask_str (argv[1]), argv[2], NULL);
+        yaz_log_init (lev, argv[2], NULL);
     else 
-        log_init (log_mask_str (argv[1]), argv[2], argv[3]);
+        yaz_log_init (lev, argv[2], argv[3]);
+    if (lev & LOG_DEBUG)
+        odr_print_file = yaz_log_file();
+    else
+        odr_print_file = 0;
     return TCL_OK;
 }
 
@@ -3472,12 +3922,24 @@ static int ir_log_proc (ClientData clientData, Tcl_Interp *interp,
                           " level string\"", NULL);
         return TCL_OK;
     }
-    mask = log_mask_str_x (argv[1], 0);
+    mask = yaz_log_mask_str_x (argv[1], 0);
     logf (LOG_DEBUG, "%s", argv[2]);
     return TCL_OK;
 }
 
 
+/* 
+ * ir_version: log ir version
+ */
+static int ir_version (ClientData clientData, Tcl_Interp *interp,
+                        int argc, char **argv)
+{
+    Tcl_AppendElement (interp, IR_TCL_VERSION);
+    Tcl_AppendElement (interp, YAZ_VERSION);
+    return TCL_OK;
+}
+
+
 /* ------------------------------------------------------- */
 static void ir_initResponse (void *obj, Z_InitResponse *initrs)
 {
@@ -3531,7 +3993,7 @@ static void ir_deleteDiags (IrTcl_Diagnostic **dst_list, int *dst_num)
 {
     int i;
     for (i = 0; i<*dst_num; i++)
-        xfree (dst_list[i]->addinfo);
+        xfree ((*dst_list)[i].addinfo);
     xfree (*dst_list);
     *dst_list = NULL;
     *dst_num = 0;
@@ -3541,7 +4003,7 @@ static void ir_handleDiags (IrTcl_Diagnostic **dst_list, int *dst_num,
                             Z_DiagRec **list, int num)
 {
     int i;
-    char *addinfo;
+    char *addinfo = NULL;
 
     *dst_num = num;
     *dst_list = ir_tcl_malloc (sizeof(**dst_list) * num);
@@ -3552,7 +4014,19 @@ static void ir_handleDiags (IrTcl_Diagnostic **dst_list, int *dst_num,
         {
         case Z_DiagRec_defaultFormat:
             (*dst_list)[i].condition = *list[i]->u.defaultFormat->condition;
+#ifdef ASN_COMPILED
+           switch (list[i]->u.defaultFormat->which)
+           {
+           case Z_DefaultDiagFormat_v2Addinfo:
+               addinfo = list[i]->u.defaultFormat->u.v2Addinfo;
+               break;
+           case Z_DefaultDiagFormat_v3Addinfo:
+               addinfo = list[i]->u.defaultFormat->u.v3Addinfo;
+               break;
+           }
+#else
             addinfo = list[i]->u.defaultFormat->addinfo;
+#endif
             if (addinfo && 
                 ((*dst_list)[i].addinfo = ir_tcl_malloc (strlen(addinfo)+1)))
                 strcpy ((*dst_list)[i].addinfo, addinfo);
@@ -3572,7 +4046,8 @@ static void ir_handleDBRecord (IrTcl_Obj *p, IrTcl_RecordList *rl,
 {
     struct oident *ident;
     Z_ext_typeent *etype;
-                
+
+    logf (LOG_DEBUG, "handleDBRecord size=%d", oe->u.octet_aligned->len);
     rl->u.dbrec.size = oe->u.octet_aligned->len;
     rl->u.dbrec.buf = NULL;
     
@@ -3589,8 +4064,11 @@ static void ir_handleDBRecord (IrTcl_Obj *p, IrTcl_RecordList *rl,
         
         odr_setbuf (p->odr_in, (char*) oe->u.octet_aligned->buf,
                     oe->u.octet_aligned->len, 0);
-        if (!(*etype->fun)(p->odr_in, &rr, 0))
+        if (!(*etype->fun)(p->odr_in, (char **) &rr, 0, 0))
+        {
+            rl->u.dbrec.type = VAL_NONE;
             return;
+        }
         switch (etype->what)
         {
         case Z_External_sutrs:
@@ -3624,8 +4102,11 @@ static void ir_handleDBRecord (IrTcl_Obj *p, IrTcl_RecordList *rl,
         if (oe->which == Z_External_octet && rl->u.dbrec.size > 0)
         {
             char *buf = (char*) oe->u.octet_aligned->buf;
-            if ((rl->u.dbrec.buf = ir_tcl_malloc (rl->u.dbrec.size)))
+            if ((rl->u.dbrec.buf = ir_tcl_malloc (rl->u.dbrec.size+1)))
+           {
                 memcpy (rl->u.dbrec.buf, buf, rl->u.dbrec.size);
+                rl->u.dbrec.buf[rl->u.dbrec.size] = '\0';
+           }
         }
         else if (rl->u.dbrec.type == VAL_SUTRS && 
                  oe->which == Z_External_sutrs)
@@ -3661,11 +4142,15 @@ static void ir_handleZRecords (void *o, Z_Records *zrs, IrTcl_SetObj *setobj,
                     &setobj->nonSurrogateDiagnosticNum);
     if (zrs->which == Z_Records_DBOSD)
     {
-       int num_rec = setobj->numberOfRecordsReturned;
+       int num_rec = zrs->u.databaseOrSurDiagnostics->num_records;
+
+        if (num_rec != setobj->numberOfRecordsReturned)
+        {
+           logf (LOG_WARN, "numberOfRecordsReturned=%d but num records=%d",
+                       setobj->numberOfRecordsReturned, num_rec);
+            setobj->numberOfRecordsReturned = num_rec;
+        }
 
-       if (num_rec > zrs->u.databaseOrSurDiagnostics->num_records)
-           num_rec = zrs->u.databaseOrSurDiagnostics->num_records;
-        logf (LOG_DEBUG, "Got %d records", num_rec);
         for (offset = 0; offset < num_rec; offset++)
         {
             Z_NamePlusRecord *znpr = zrs->u.databaseOrSurDiagnostics->
@@ -3695,12 +4180,90 @@ static void ir_handleZRecords (void *o, Z_Records *zrs, IrTcl_SetObj *setobj,
     }
     else
     {
+#ifdef ASN_COMPILED
+        Z_DiagRec dr, *dr_p = &dr;
+        dr.which = Z_DiagRec_defaultFormat;
+        dr.u.defaultFormat = zrs->u.nonSurrogateDiagnostic;
+#else
+        Z_DiagRec *dr_p = zrs->u.nonSurrogateDiagnostic;
+#endif
         logf (LOG_DEBUG, "NonSurrogateDiagnostic");
+
         setobj->numberOfRecordsReturned = 0;
         ir_handleDiags (&setobj->nonSurrogateDiagnosticList,
                         &setobj->nonSurrogateDiagnosticNum,
-                        &zrs->u.nonSurrogateDiagnostic,
-                        1);
+                        &dr_p, 1);
+    }
+}
+
+static char *set_queryExpression (Z_QueryExpression *qe)
+{
+    char *termz = 0;
+    if (!qe)
+        return 0;
+    if (qe->which == Z_QueryExpression_term)
+    {
+        if (qe->u.term->queryTerm)
+        {
+            Z_Term *term = qe->u.term->queryTerm;
+            if (term->which == Z_Term_general)
+            {
+                termz = xmalloc (term->u.general->len+1);
+                memcpy (termz, term->u.general->buf, term->u.general->len);
+                termz[term->u.general->len] = 0;
+            }
+        }
+    }
+    return termz;
+}
+
+static void set_searchResult (Z_OtherInformation *o,
+                              IrTcl_SetObj *setobj)
+{
+    int i;
+    if (!o)
+        return ;
+    for (i = 0; i < o->num_elements; i++)
+    {
+        if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
+        {
+            ODR odr = odr_createmem (ODR_DECODE);
+            Z_External *ext = o->list[i]->information.externallyDefinedInfo;
+            Z_SearchInfoReport *sr = 0;
+
+            if (ext->which == Z_External_single)
+            {
+                odr_setbuf (odr, ext->u.single_ASN1_type->buf,
+                            ext->u.single_ASN1_type->len, 0);
+                z_SearchInfoReport (odr, &sr, 0, "searchInfo");
+            }
+            if (ext->which == Z_External_searchResult1)
+                sr = ext->u.searchResult1;
+            if (sr)
+            {
+                int j;
+                reset_searchResult (setobj);
+
+                setobj->searchResult_num = sr->num;
+                setobj->searchResult_terms =
+                    xmalloc (sr->num * sizeof(*setobj->searchResult_terms));
+                setobj->searchResult_count =
+                    xmalloc (sr->num * sizeof(*setobj->searchResult_count));
+
+                for (j = 0; j < sr->num; j++)
+                {
+                    setobj->searchResult_terms[j] =
+                        set_queryExpression (
+                            sr->elements[j]->subqueryExpression);
+                    if (sr->elements[j]->subqueryCount)
+                        setobj->searchResult_count[j] = 
+                            *sr->elements[j]->subqueryCount;
+                    else
+                        setobj->searchResult_count[j] =  0;
+                }
+            }
+            odr_destroy (odr);
+        }
     }
 }
 
@@ -3715,16 +4278,19 @@ static void ir_searchResponse (void *o, Z_SearchResponse *searchrs,
         logf (LOG_DEBUG, "Search response, no object!");
         return;
     }
-    setobj->searchStatus = searchrs->searchStatus ? 1 : 0;
+    setobj->searchStatus = *searchrs->searchStatus;
     get_referenceId (&setobj->set_inher.referenceId, searchrs->referenceId);
     setobj->resultCount = *searchrs->resultCount;
     if (searchrs->presentStatus)
         setobj->presentStatus = *searchrs->presentStatus;
+    else
+        setobj->presentStatus = Z_SearchResponse_none;
     if (searchrs->nextResultSetPosition)
         setobj->nextResultSetPosition = *searchrs->nextResultSetPosition;
 
     logf (LOG_DEBUG, "status %d hits %d", 
           setobj->searchStatus, setobj->resultCount);
+    set_searchResult (searchrs->additionalSearchInfo, setobj);
     if (zrs)
     {
         const char *es;
@@ -3797,67 +4363,95 @@ static void ir_scanResponse (void *o, Z_ScanResponse *scanrs,
 
     xfree (scanobj->entries);
     scanobj->entries = NULL;
-
+    scanobj->num_entries = 0;
+    scanobj->entries_flag = 0;
+       
     ir_deleteDiags (&scanobj->nonSurrogateDiagnosticList,
                     &scanobj->nonSurrogateDiagnosticNum);
     if (scanrs->entries)
     {
         int i;
-        Z_Entry *ze;
+        Z_Entry **ze;
 
         scanobj->entries_flag = 1;
-        scanobj->which = scanrs->entries->which;
-        switch (scanobj->which)
-        {
-        case Z_ListEntries_entries:
-            scanobj->num_entries = scanrs->entries->u.entries->num_entries;
+       if (scanrs->entries)
+       {
+            scanobj->num_entries = scanrs->entries->num_entries;
             scanobj->entries = ir_tcl_malloc (scanobj->num_entries * 
-                                       sizeof(*scanobj->entries));
-            for (i=0; i<scanobj->num_entries; i++)
-            {
-                ze = scanrs->entries->u.entries->entries[i];
-                scanobj->entries[i].which = ze->which;
-                switch (ze->which)
-                {
-                case Z_Entry_termInfo:
-                    if (ze->u.termInfo->term->which == Z_Term_general)
-                    {
-                        int l = ze->u.termInfo->term->u.general->len;
-                        scanobj->entries[i].u.term.buf = ir_tcl_malloc (1+l);
-                        memcpy (scanobj->entries[i].u.term.buf, 
-                                ze->u.termInfo->term->u.general->buf,
-                                l);
-                        scanobj->entries[i].u.term.buf[l] = '\0';
-                    }
-                    else
-                        scanobj->entries[i].u.term.buf = NULL;
-                    if (ze->u.termInfo->globalOccurrences)
-                        scanobj->entries[i].u.term.globalOccurrences = 
-                            *ze->u.termInfo->globalOccurrences;
-                    else
-                        scanobj->entries[i].u.term.globalOccurrences = 0;
-                    break;
-                case Z_Entry_surrogateDiagnostic:
-                    ir_handleDiags (&scanobj->entries[i].u.diag.list,
-                                    &scanobj->entries[i].u.diag.num,
-                                    &ze->u.surrogateDiagnostic,
-                                    1);
-                    break;
-                }
-            }
-            break;
-        case Z_ListEntries_nonSurrogateDiagnostics:
-            ir_handleDiags (&scanobj->nonSurrogateDiagnosticList,
+                                             sizeof(*scanobj->entries));
+           ze = scanrs->entries->entries;
+       }
+       for (i=0; i<scanobj->num_entries; i++, ze++)
+       {
+           scanobj->entries[i].which = (*ze)->which;
+           switch ((*ze)->which)
+           {
+           case Z_Entry_termInfo:
+               if ((*ze)->u.termInfo->term->which == Z_Term_general)
+               {
+                   int l = (*ze)->u.termInfo->term->u.general->len;
+                   scanobj->entries[i].u.term.buf = ir_tcl_malloc (1+l);
+                   memcpy (scanobj->entries[i].u.term.buf, 
+                           (*ze)->u.termInfo->term->u.general->buf,
+                           l);
+                   scanobj->entries[i].u.term.buf[l] = '\0';
+               }
+               else
+                   scanobj->entries[i].u.term.buf = NULL;
+               if ((*ze)->u.termInfo->globalOccurrences)
+                   scanobj->entries[i].u.term.globalOccurrences = 
+                       *(*ze)->u.termInfo->globalOccurrences;
+               else
+                   scanobj->entries[i].u.term.globalOccurrences = 0;
+               break;
+           case Z_Entry_surrogateDiagnostic:
+               ir_handleDiags (&scanobj->entries[i].u.diag.list,
+                               &scanobj->entries[i].u.diag.num,
+                               &(*ze)->u.surrogateDiagnostic,
+                               1);
+               break;
+           }
+       }
+       if (scanrs->entries->nonsurrogateDiagnostics)
+           ir_handleDiags (&scanobj->nonSurrogateDiagnosticList,
                             &scanobj->nonSurrogateDiagnosticNum,
-                            scanrs->entries->u.nonSurrogateDiagnostics->
-                            diagRecs,
-                            scanrs->entries->u.nonSurrogateDiagnostics->
-                            num_diagRecs);
-            break;
-        }
+                            scanrs->entries->nonsurrogateDiagnostics,
+                            scanrs->entries->num_nonsurrogateDiagnostics);
     }
-    else
-        scanobj->entries_flag = 0;
+}
+
+
+static void ir_sortResponse (void *o, Z_SortResponse *sortrs,
+                             IrTcl_SetObj *setobj)
+{
+    IrTcl_Obj *p = o;
+    
+    logf (LOG_DEBUG, "Received sortResponse");
+    
+    if (!setobj)
+       return;
+
+    purge_IR_records (setobj);
+
+    get_referenceId (&p->set_inher.referenceId, sortrs->referenceId);
+    
+    setobj->sortStatus = *sortrs->sortStatus;
+
+    ir_deleteDiags (&setobj->nonSurrogateDiagnosticList,
+                    &setobj->nonSurrogateDiagnosticNum);
+#ifdef ASN_COMPILED
+    if (sortrs->diagnostics)
+       ir_handleDiags (&setobj->nonSurrogateDiagnosticList,
+                       &setobj->nonSurrogateDiagnosticNum,
+                       sortrs->diagnostics,
+                       sortrs->num_diagnostics);
+#else
+    if (sortrs->diagnostics)
+       ir_handleDiags (&setobj->nonSurrogateDiagnosticList,
+                       &setobj->nonSurrogateDiagnosticNum,
+                       sortrs->diagnostics->diagRecs,
+                       sortrs->diagnostics->num_diagRecs);
+#endif
 }
 
 /*
@@ -3872,6 +4466,7 @@ static void ir_select_read (ClientData clientData)
     char *object_name;
     Tcl_CmdInfo cmd_info;
     const char *apdu_call;
+    int round = 0;
 
     logf(LOG_DEBUG, "Read handler fd=%d", cs_fileno(p->cs_link));
     if (p->state == IR_TCL_R_Connecting)
@@ -3910,6 +4505,8 @@ static void ir_select_read (ClientData clientData)
     {
         p->state = IR_TCL_R_Reading;
 
+         round++;
+         yaz_log(LOG_DEBUG, "round %d", round);
         /* read incoming APDU */
         if ((r=cs_get (p->cs_link, &p->buf_in, &p->len_in)) == 1)
         {
@@ -3937,7 +4534,7 @@ static void ir_select_read (ClientData clientData)
         p->apduOffset = -1;
         odr_setbuf (p->odr_in, p->buf_in, r, 0);
         logf (LOG_DEBUG, "cs_get ok, total size %d", r);
-        if (!z_APDU (p->odr_in, &apdu, 0))
+        if (!z_APDU (p->odr_in, &apdu, 0, 0))
         {
             logf (LOG_DEBUG, "cs_get failed: %s",
                 odr_errmsg (odr_geterror (p->odr_in)));
@@ -3952,12 +4549,16 @@ static void ir_select_read (ClientData clientData)
             ir_obj_delete ((ClientData) p);
             return;
         }
+       if (p->odr_pr)
+           z_APDU(p->odr_pr, &apdu, 0, 0);
         /* handle APDU and invoke callback */
         rq = p->request_queue;
-        if (!rq)
-        {
-            logf (LOG_FATAL, "Internal error. No queue entry");
-            exit (1);
+       if (!rq)
+       {
+           /* no corresponding request. Skip it. */
+           logf(LOG_DEBUG, "no corresponding request. Skipping it");
+            p->state = IR_TCL_R_Idle;
+           return;
         }
         object_name = rq->object_name;
         logf (LOG_DEBUG, "Object %s", object_name);
@@ -3992,6 +4593,13 @@ static void ir_select_read (ClientData clientData)
                 apdu_call = ((IrTcl_ScanObj *) 
                              cmd_info.clientData)->scanResponse;
                 break;
+           case Z_APDU_sortResponse:
+               p->eventType = "sort";
+                ir_sortResponse (p, apdu->u.sortResponse, 
+                                 (IrTcl_SetObj *) cmd_info.clientData);
+                apdu_call = ((IrTcl_SetObj *) 
+                             cmd_info.clientData)->sortResponse;
+                break;         
             default:
                 logf (LOG_WARN, "Received unknown APDU type (%d)",
                       apdu->which);
@@ -4141,6 +4749,11 @@ DllEntryPoint(hInst, reason, reserved)
  */
 EXPORT (int,Irtcl_Init) (Tcl_Interp *interp)
 {
+#if USE_TCL_STUBS
+    if (Tcl_InitStubs(interp, "8.1", 0) == NULL) {
+        return TCL_ERROR;
+    }
+#endif
     Tcl_CreateCommand (interp, "ir", ir_obj_mk, (ClientData) NULL,
                        (Tcl_CmdDeleteProc *) NULL);
     Tcl_CreateCommand (interp, "ir-set", ir_set_obj_mk,
@@ -4151,6 +4764,8 @@ EXPORT (int,Irtcl_Init) (Tcl_Interp *interp)
                        (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
     Tcl_CreateCommand (interp, "ir-log", ir_log_proc,
                        (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+    Tcl_CreateCommand (interp, "ir-version", ir_version, (ClientData) NULL,
+                       (Tcl_CmdDeleteProc *) NULL);
     nmem_init ();
     return TCL_OK;
 }