Changed version to 1.4.1. Added WIN32 version resource.
[ir-tcl-moved-to-github.git] / ir-tcl.c
index 57792b0..abe271c 100644 (file)
--- a/ir-tcl.c
+++ b/ir-tcl.c
@@ -1,11 +1,35 @@
 /*
  * IR toolkit for tcl/tk
- * (c) Index Data 1995-1999
+ * (c) Index Data 1995-2003
  * See the file LICENSE for details.
- * Sebastian Hammer, Adam Dickmeiss
  *
  * $Log: ir-tcl.c,v $
- * Revision 1.113  1999-04-20 10:01:46  adam
+ * 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
@@ -1024,7 +1048,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)
@@ -1047,7 +1071,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);
@@ -1064,10 +1088,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);
@@ -1364,11 +1388,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;
 }
 
@@ -2027,7 +2051,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;
@@ -2040,16 +2068,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;
@@ -2114,11 +2149,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;
@@ -2136,18 +2172,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;
@@ -2157,16 +2194,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;
 }
 
 /*
@@ -2233,7 +2276,7 @@ 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)
+                            int argc, char **argv)
 {
     IrTcl_SetObj *obj = o;
 
@@ -2288,6 +2331,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)
  */
@@ -2756,6 +2847,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;
 }
@@ -3043,7 +3138,7 @@ static int do_sort (void *o, Tcl_Interp *interp, int argc, char **argv)
 #ifdef ASN_COMPILED
     req->num_inputResultSetNames = 1;
     req->inputResultSetNames = (Z_InternationalString **)
-        odr_malloc (out, sizeof(*req->inputResultSetNames));
+        odr_malloc (p->odr_out, sizeof(*req->inputResultSetNames));
     req->inputResultSetNames[0] = obj->setName;
 #else
     req->inputResultSetNames =
@@ -3189,6 +3284,7 @@ static IrTcl_Method ir_set_method_tab[] = {
     { "sort",                    do_sort, NULL },
     { "sortResponse",            do_sortResponse, NULL},
     { "sortStatus",              do_sortStatus, NULL},
+    { "searchResult",            do_searchResult, NULL},
     { NULL, NULL}
 };
 
@@ -3374,10 +3470,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)
@@ -3388,16 +3490,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);
@@ -3407,28 +3516,34 @@ 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 1 
+/* !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);
+    rpn = ccl_find_str(p->bibset, start_term, &r, &pos);
     if (r)
     {
         Tcl_AppendResult (interp, "ccl syntax error ", ccl_err_msg(r), NULL);
-        return ir_tcl_error_exec (interp, argc, argv);
+        code = ir_tcl_error_exec (interp, argc, argv);
+       goto out;
     }
     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;
+    if (!(req->termListAndStartPoint = ccl_scan_query (p->odr_out, rpn)))
+    {
+        code = TCL_ERROR;
+       goto out;
+    }
 #endif
     req->stepSize = &obj->stepSize;
     req->numberOfTermsRequested = &obj->numberOfTermsRequested;
@@ -3439,7 +3554,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;
 }
 
 /*
@@ -3721,11 +3841,11 @@ static int ir_log_init_proc (ClientData clientData, Tcl_Interp *interp,
         return TCL_OK;
     }
     if (argc == 2)
-        log_init (log_mask_str (argv[1]), "", NULL);
+        yaz_log_init (yaz_log_mask_str (argv[1]), "", NULL);
     else if (argc == 3)
-        log_init (log_mask_str (argv[1]), argv[2], NULL);
+        yaz_log_init (yaz_log_mask_str (argv[1]), argv[2], NULL);
     else 
-        log_init (log_mask_str (argv[1]), argv[2], argv[3]);
+        yaz_log_init (yaz_log_mask_str (argv[1]), argv[2], argv[3]);
     return TCL_OK;
 }
 
@@ -3742,7 +3862,7 @@ 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;
 }
@@ -3813,7 +3933,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;
@@ -4013,6 +4133,77 @@ static void ir_handleZRecords (void *o, Z_Records *zrs, IrTcl_SetObj *setobj,
     }
 }
 
+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);
+        }
+    }
+}
+
 static void ir_searchResponse (void *o, Z_SearchResponse *searchrs,
                                IrTcl_SetObj *setobj)
 {    
@@ -4036,6 +4227,7 @@ static void ir_searchResponse (void *o, Z_SearchResponse *searchrs,
 
     logf (LOG_DEBUG, "status %d hits %d", 
           setobj->searchStatus, setobj->resultCount);
+    set_searchResult (searchrs->additionalSearchInfo, setobj);
     if (zrs)
     {
         const char *es;
@@ -4211,6 +4403,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)
@@ -4249,6 +4442,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)
         {
@@ -4293,10 +4488,12 @@ static void ir_select_read (ClientData clientData)
         }
         /* 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);