Simplified select handling. Only one function ir_tcl_select_set has
[ir-tcl-moved-to-github.git] / ir-tcl.c
index 50cf918..d87d4d8 100644 (file)
--- a/ir-tcl.c
+++ b/ir-tcl.c
@@ -5,7 +5,35 @@
  * Sebastian Hammer, Adam Dickmeiss
  *
  * $Log: ir-tcl.c,v $
- * Revision 1.70  1996-01-10 09:18:34  adam
+ * Revision 1.78  1996-02-21 10:16:08  adam
+ * Simplified select handling. Only one function ir_tcl_select_set has
+ * to be externally defined.
+ *
+ * Revision 1.77  1996/02/20  17:52:58  adam
+ * Uses the YAZ oid system to name record syntax object identifiers.
+ *
+ * Revision 1.76  1996/02/20  16:09:51  adam
+ * Bug fix: didn't set element set names stamp correctly on result
+ * set records when element set names were set to the empty string.
+ *
+ * Revision 1.75  1996/02/19  15:41:53  adam
+ * Better log messages.
+ * Minor improvement of connect method.
+ *
+ * Revision 1.74  1996/02/05  17:58:03  adam
+ * Ported ir-tcl to use the beta releases of tcl7.5/tk4.1.
+ *
+ * Revision 1.73  1996/01/29  11:35:19  adam
+ * Bug fix: cs_type member renamed to comstackType to avoid conflict with
+ * cs_type macro defined by YAZ.
+ *
+ * Revision 1.72  1996/01/19  17:45:34  quinn
+ * Added debugging output
+ *
+ * Revision 1.71  1996/01/19  16:22:38  adam
+ * New method: apduDump - returns information about last incoming APDU.
+ *
+ * Revision 1.70  1996/01/10  09:18:34  adam
  * PDU specific callbacks implemented: initRespnse, searchResponse,
  *  presentResponse and scanResponse.
  * Bug fix in the command line shell (tclmain.c) - discovered on OSF/1.
 
 #include <stdlib.h>
 #include <stdio.h>
-#ifdef WINDOWS
+#include <unistd.h>
 #include <time.h>
-#else
-#include <sys/time.h>
-#endif
 #include <assert.h>
 
 #define CS_BLOCK 0
 
+#define IRTCL_GENERIC_FILES 0
+
 #include "ir-tclp.h"
 
 typedef struct {
@@ -277,12 +304,36 @@ static void ir_deleteDiags (IrTcl_Diagnostic **dst_list, int *dst_num);
 static int do_disconnect (void *obj, Tcl_Interp *interp, 
                           int argc, char **argv);
 
+static void ir_select_notify (ClientData clientData, int r, int w, int e);
+
+void ir_select_add (int fd, void *obj)
+{
+    ir_tcl_select_set (ir_select_notify, fd, obj, 1, 0, 0);
+}
+
+void ir_select_add_write (int fd, void *obj)
+{
+    ir_tcl_select_set (ir_select_notify, fd, obj, 1, 1, 0);
+}
+
+void ir_select_remove (int fd, void *obj)
+{
+    ir_tcl_select_set (NULL, fd, obj, 0, 0, 0);
+}
+
+void ir_select_remove_write (int fd, void *obj)
+{
+    ir_tcl_select_set (ir_select_notify, fd, obj, 1, 0, 0);
+}
+
 static IrTcl_RecordList *new_IR_record (IrTcl_SetObj *setobj, 
                                         int no, int which, 
                                         const char *elements)
 {
     IrTcl_RecordList *rl;
 
+    if (elements && !*elements)
+        elements = NULL;
     for (rl = setobj->record_list; rl; rl = rl->next)
     {
         if (no == rl->no && (!rl->elements || !elements ||
@@ -315,30 +366,6 @@ static IrTcl_RecordList *new_IR_record (IrTcl_SetObj *setobj,
     return rl;
 }
 
-static struct {
-    enum oid_value value;
-    const char *name;
-} IrTcl_recordSyntaxTab[] = { 
-{ VAL_UNIMARC,    "UNIMARC" },
-{ VAL_INTERMARC,  "INTERMARC" },
-{ VAL_CCF,        "CCF" },
-{ VAL_USMARC,     "USMARC" },
-{ VAL_UKMARC,     "UKMARC" },
-{ VAL_NORMARC,    "NORMARC" },
-{ VAL_LIBRISMARC, "LIBRISMARC" },
-{ VAL_DANMARC,    "DANMARC" },
-{ VAL_FINMARC,    "FINMARC" },
-{ VAL_MAB,        "MAB" },
-{ VAL_CANMARC,    "CANMARC" },
-{ VAL_SBN,        "SBN" },
-{ VAL_PICAMARC,   "PICAMARC" },
-{ VAL_AUSMARC,    "AUSMARC" },
-{ VAL_IBERMARC,   "IBERMARC" },
-{ VAL_SUTRS,      "SUTRS" },
-{ VAL_GRS1,       "GRS1" },
-{ 0, NULL }
-};
-
 /* 
  * IrTcl_eval
  */
@@ -360,13 +387,21 @@ int IrTcl_eval (Tcl_Interp *interp, const char *command)
 /*
  * IrTcl_getRecordSyntaxStr: Return record syntax name of object id
  */
-static const char *IrTcl_getRecordSyntaxStr (enum oid_value value)
+static char *IrTcl_getRecordSyntaxStr (enum oid_value value)
 {
-    int i;
-    for (i = 0; IrTcl_recordSyntaxTab[i].name; i++) 
-        if (IrTcl_recordSyntaxTab[i].value == value)
-            return IrTcl_recordSyntaxTab[i].name;
-    return "USMARC";
+    int *o;
+    struct oident ent, *entp;
+
+    ent.proto = PROTO_Z3950;
+    ent.oclass = CLASS_RECSYN;
+    ent.value = value;
+
+    o = oid_getoidbyent (&ent);
+    entp = oid_getentbyoid (o);
+    
+    if (!entp)
+        return "";
+    return entp->desc;
 }
 
 /*
@@ -374,11 +409,7 @@ static const char *IrTcl_getRecordSyntaxStr (enum oid_value value)
  */
 static enum oid_value IrTcl_getRecordSyntaxVal (const char *name)
 {
-    int i;
-    for (i = 0; IrTcl_recordSyntaxTab[i].name; i++) 
-        if (!strcmp (IrTcl_recordSyntaxTab[i].name, name))
-            return IrTcl_recordSyntaxTab[i].value;
-    return 0;
+    return oid_getvalbyname (name);
 }
 
 static IrTcl_RecordList *find_IR_record (IrTcl_SetObj *setobj, int no)
@@ -684,6 +715,54 @@ static int do_options (void *obj, Tcl_Interp *interp,
 }
 
 /*
+ * do_apduInfo: Get APDU information
+ */
+static int do_apduInfo (void *obj, Tcl_Interp *interp, int argc, char **argv)
+{
+    char buf[16];
+    FILE *apduf;
+    IrTcl_Obj *p = obj;
+
+    if (argc <= 0)
+        return TCL_OK;
+    sprintf (buf, "%d", p->apduLen);
+    Tcl_AppendElement (interp, buf);
+    sprintf (buf, "%d", p->apduOffset);
+    Tcl_AppendElement (interp, buf);
+    if (!p->buf_in)
+    {
+        Tcl_AppendElement (interp, "");
+        return TCL_OK;
+    }
+    apduf = fopen ("apdu.tmp", "w");
+    if (!apduf)
+    {
+        Tcl_AppendElement (interp, "");
+        return TCL_OK;
+    }
+    odr_dumpBER (apduf, p->buf_in, p->apduLen);
+    fclose (apduf);
+    if (!(apduf = fopen ("apdu.tmp", "r")))
+        Tcl_AppendElement (interp, "");
+    else
+    {
+        int c;
+        
+        Tcl_AppendResult (interp, " {", NULL);
+        while ((c = getc (apduf)) != EOF)
+        {
+            buf[0] = c;
+            buf[1] = '\0';
+            Tcl_AppendResult (interp, buf, NULL);
+        }
+        fclose (apduf);
+        Tcl_AppendResult (interp, "}", NULL);
+    }
+    unlink ("apdu.tmp");
+    return TCL_OK;
+}
+
+/*
  * do_failInfo: Get fail information
  */
 static int do_failInfo (void *obj, Tcl_Interp *interp, int argc, char **argv)
@@ -964,9 +1043,7 @@ static int do_connect (void *obj, Tcl_Interp *interp,
             interp->result = "already connected";
             return TCL_ERROR;
         }
-        if (ir_tcl_strdup (interp, &p->hostname, argv[2]) == TCL_ERROR)
-            return TCL_ERROR;
-        if (!strcmp (p->cs_type, "tcpip"))
+        if (!strcmp (p->comstackType, "tcpip"))
         {
             p->cs_link = cs_create (tcpip_type, CS_BLOCK, p->protocol_type);
             addr = tcpip_strtoaddr (argv[2]);
@@ -977,7 +1054,7 @@ static int do_connect (void *obj, Tcl_Interp *interp,
             }
             logf (LOG_DEBUG, "tcp/ip connect %s", argv[2]);
         }
-        else if (!strcmp (p->cs_type, "mosi"))
+        else if (!strcmp (p->comstackType, "mosi"))
         {
 #if MOSI
             p->cs_link = cs_create (mosi_type, CS_BLOCK, p->protocol_type);
@@ -996,20 +1073,39 @@ static int do_connect (void *obj, Tcl_Interp *interp,
         else 
         {
             Tcl_AppendResult (interp, "Bad comstack type: ", 
-                              p->cs_type, NULL);
+                              p->comstackType, NULL);
             return TCL_ERROR;
         }
+        if (ir_tcl_strdup (interp, &p->hostname, argv[2]) == TCL_ERROR)
+            return TCL_ERROR;
+#if IRTCL_GENERIC_FILES
+#ifdef WINDOWS
+        p->csFile = Tcl_GetFile (cs_fileno(p->cs_link), TCL_WIN_SOCKET);
+#else
+        p->csFile = Tcl_GetFile (cs_fileno(p->cs_link), TCL_UNIX_FD);
+#endif
+#endif
         if ((r=cs_connect (p->cs_link, addr)) < 0)
         {
             interp->result = "connect fail";
             do_disconnect (p, NULL, 2, NULL);
             return TCL_ERROR;
         }
+       logf(LOG_DEBUG, "cs_connect() returned %d fd=%d", r,
+             cs_fileno(p->cs_link));
         p->eventType = "connect";
+#if IRTCL_GENERIC_FILES
+        ir_select_add (p->csFile, p);
+#else
         ir_select_add (cs_fileno (p->cs_link), p);
+#endif
         if (r == 1)
         {
+#if IRTCL_GENERIC_FILES
+            ir_select_add_write (p->csFile, p);
+#else
             ir_select_add_write (cs_fileno (p->cs_link), p);
+#endif
             p->state = IR_TCL_R_Connecting;
         }
         else
@@ -1038,20 +1134,33 @@ static int do_disconnect (void *obj, Tcl_Interp *interp,
         p->eventType = NULL;
         p->hostname = NULL;
         p->cs_link = NULL;
+#if IRTCL_GENERIC_FILES
+        p->csFile = 0;
+#endif
         return TCL_OK;
     }
     if (p->hostname)
     {
+       logf(LOG_DEBUG, "Closing connection to %s", p->hostname);
         free (p->hostname);
         p->hostname = NULL;
+#if IRTCL_GENERIC_FILES
+        ir_select_remove_write (p->csFile, p);
+        ir_select_remove (p->csFile, p);
+#else
         ir_select_remove_write (cs_fileno (p->cs_link), p);
         ir_select_remove (cs_fileno (p->cs_link), p);
+#endif
 
         odr_reset (p->odr_in);
 
         assert (p->cs_link);
         cs_close (p->cs_link);
         p->cs_link = NULL;
+#if IRTCL_GENERIC_FILES
+        Tcl_FreeFile (p->csFile);
+        p->csFile = NULL;
+#endif
 
         ODR_MASK_ZERO (&p->options);
         ODR_MASK_SET (&p->options, 0);
@@ -1078,16 +1187,16 @@ static int do_comstack (void *o, Tcl_Interp *interp,
     IrTcl_Obj *obj = o;
 
     if (argc == 0)
-        return ir_tcl_strdup (interp, &obj->cs_type, "tcpip");
+        return ir_tcl_strdup (interp, &obj->comstackType, "tcpip");
     else if (argc == -1)
-        return ir_tcl_strdel (interp, &obj->cs_type);
+        return ir_tcl_strdel (interp, &obj->comstackType);
     else if (argc == 3)
     {
-        free (obj->cs_type);
-        if (ir_tcl_strdup (interp, &obj->cs_type, argv[2]) == TCL_ERROR)
+        free (obj->comstackType);
+        if (ir_tcl_strdup (interp, &obj->comstackType, argv[2]) == TCL_ERROR)
             return TCL_ERROR;
     }
-    Tcl_AppendElement (interp, obj->cs_type);
+    Tcl_AppendElement (interp, obj->comstackType);
     return TCL_OK;
 }
 
@@ -1479,6 +1588,11 @@ static int do_preferredRecordSyntax (void *obj, Tcl_Interp *interp,
                            ir_tcl_malloc (sizeof(*p->preferredRecordSyntax))))
             *p->preferredRecordSyntax = IrTcl_getRecordSyntaxVal (argv[2]);
     }
+    else if (argc == 2)
+    {
+        Tcl_AppendElement (interp, IrTcl_getRecordSyntaxStr
+                           (*p->preferredRecordSyntax));
+    }
     return TCL_OK;
             
 }
@@ -1565,6 +1679,7 @@ static IrTcl_Method ir_method_tab[] = {
 { 1, "protocol",                    do_protocol },
 { 0, "failback",                    do_failback },
 { 0, "failInfo",                    do_failInfo },
+{ 0, "apduInfo",                    do_apduInfo },
 { 0, "logLevel",                    do_logLevel },
 
 { 0, "eventType",                   do_eventType },
@@ -1684,7 +1799,7 @@ static int ir_obj_mk (ClientData clientData, Tcl_Interp *interp,
     }
 #endif
 
-    logf (LOG_DEBUG, "ir object create");
+    logf (LOG_DEBUG, "ir object create %s", argv[1]);
     obj->odr_in = odr_createmem (ODR_DECODE);
     obj->odr_out = odr_createmem (ODR_ENCODE);
     obj->odr_pr = odr_createmem (ODR_PRINT);
@@ -1928,7 +2043,10 @@ static int do_resultCount (void *o, Tcl_Interp *interp,
     IrTcl_SetObj *obj = o;
 
     if (argc <= 0)
+    {
+        obj->resultCount = 0;
         return TCL_OK;
+    }
     return get_set_int (&obj->resultCount, interp, argc, argv);
 }
 
@@ -3277,7 +3395,7 @@ static void ir_scanResponse (void *o, Z_ScanResponse *scanrs,
 /*
  * ir_select_read: handle incoming packages
  */
-void ir_select_read (ClientData clientData)
+static void ir_select_read (ClientData clientData)
 {
     IrTcl_Obj *p = clientData;
     Z_APDU *apdu;
@@ -3287,8 +3405,10 @@ void ir_select_read (ClientData clientData)
     Tcl_CmdInfo cmd_info;
     const char *apdu_call;
 
+    logf(LOG_DEBUG, "Read handler fd=%d", cs_fileno(p->cs_link));
     if (p->state == IR_TCL_R_Connecting)
     {
+       logf(LOG_DEBUG, "Connect handler");
         r = cs_rcvconnect (p->cs_link);
         if (r == 1)
         {
@@ -3296,7 +3416,11 @@ void ir_select_read (ClientData clientData)
             return;
         }
         p->state = IR_TCL_R_Idle;
+#if IRTCL_GENERIC_FILES
+        ir_select_remove_write (p->csFile, p);
+#else
         ir_select_remove_write (cs_fileno (p->cs_link), p);
+#endif
         if (r < 0)
         {
             logf (LOG_DEBUG, "cs_rcvconnect error");
@@ -3326,7 +3450,11 @@ void ir_select_read (ClientData clientData)
         if ((r=cs_get (p->cs_link, &p->buf_in, &p->len_in)) <= 0)
         {
             logf (LOG_DEBUG, "cs_get failed, code %d", r);
+#if IRTCL_GENERIC_FILES
+            ir_select_remove (p->csFile, p);
+#else
             ir_select_remove (cs_fileno (p->cs_link), p);
+#endif
             do_disconnect (p, NULL, 2, NULL);
             if (p->failback)
             {
@@ -3338,23 +3466,31 @@ void ir_select_read (ClientData clientData)
             return;
         }        
         if (r == 1)
+       {
+           logf(LOG_DEBUG, "PDU Fraction read");
             return ;
+       }
         /* got complete APDU. Now decode */
+        p->apduLen = r;
+        p->apduOffset = -1;
         odr_setbuf (p->odr_in, p->buf_in, r, 0);
-        logf (LOG_DEBUG, "cs_get ok, got %d", r);
+        logf (LOG_DEBUG, "cs_get ok, total size %d", r);
         if (!z_APDU (p->odr_in, &apdu, 0))
         {
-            logf (LOG_DEBUG, "%s", odr_errmsg (odr_geterror (p->odr_in)));
+            logf (LOG_DEBUG, "cs_get failed: %s",
+               odr_errmsg (odr_geterror (p->odr_in)));
             do_disconnect (p, NULL, 2, NULL);
             if (p->failback)
             {
                 p->failInfo = IR_TCL_FAIL_IN_APDU;
+                p->apduOffset = odr_offset (p->odr_in);
                 IrTcl_eval (p->interp, p->failback);
             }
             /* release ir object now if failback deleted it */
             ir_obj_delete (p);
             return;
         }
+       logf(LOG_DEBUG, "Decoded ok");
         /* handle APDU and invoke callback */
         rq = p->request_queue;
         if (!rq)
@@ -3433,15 +3569,16 @@ void ir_select_read (ClientData clientData)
 /*
  * ir_select_write: handle outgoing packages - not yet written.
  */
-void ir_select_write (ClientData clientData)
+static void ir_select_write (ClientData clientData)
 {
     IrTcl_Obj *p = clientData;
     int r;
     IrTcl_Request *rq;
 
-    logf (LOG_DEBUG, "In write handler");
+    logf (LOG_DEBUG, "Write handler fd=%d", cs_fileno(p->cs_link));
     if (p->state == IR_TCL_R_Connecting)
     {
+       logf(LOG_DEBUG, "Connect handler");
         r = cs_rcvconnect (p->cs_link);
         if (r == 1)
             return;
@@ -3449,7 +3586,11 @@ void ir_select_write (ClientData clientData)
         if (r < 0)
         {
             logf (LOG_DEBUG, "cs_rcvconnect error");
+#if IRTCL_GENERIC_FILES
+            ir_select_remove_write (p->csFile, p);
+#else
             ir_select_remove_write (cs_fileno (p->cs_link), p);
+#endif
             if (p->failback)
             {
                 p->failInfo = IR_TCL_FAIL_CONNECT;
@@ -3458,7 +3599,11 @@ void ir_select_write (ClientData clientData)
             do_disconnect (p, NULL, 2, NULL);
             return;
         }
+#if IRTCL_GENERIC_FILES
+        ir_select_remove_write (p->csFile, p);
+#else
         ir_select_remove_write (cs_fileno (p->cs_link), p);
+#endif
         if (p->callback)
             IrTcl_eval (p->interp, p->callback);
         return;
@@ -3469,7 +3614,7 @@ void ir_select_write (ClientData clientData)
     assert (rq);
     if ((r=cs_put (p->cs_link, rq->buf_out, rq->len_out)) < 0)
     {
-        logf (LOG_DEBUG, "select write fail");
+        logf (LOG_DEBUG, "cs_put write fail");
         if (p->failback)
         {
             p->failInfo = IR_TCL_FAIL_WRITE;
@@ -3481,13 +3626,26 @@ void ir_select_write (ClientData clientData)
     }
     else if (r == 0)            /* remove select bit */
     {
+       logf(LOG_DEBUG, "Write completed");
         p->state = IR_TCL_R_Waiting;
+#if IRTCL_GENERIC_FILES
+        ir_select_remove_write (p->csFile, p);
+#else
         ir_select_remove_write (cs_fileno (p->cs_link), p);
+#endif
         free (rq->buf_out);
         rq->buf_out = NULL;
     }
 }
 
+static void ir_select_notify (ClientData clientData, int r, int w, int e)
+{
+    if (r)
+        ir_select_read (clientData);
+    if (w)
+        ir_select_write (clientData);
+}
+
 /* ------------------------------------------------------- */
 
 /*