Simplified select handling. Only one function ir_tcl_select_set has
[ir-tcl-moved-to-github.git] / ir-tcl.c
1 /*
2  * IR toolkit for tcl/tk
3  * (c) Index Data 1995
4  * See the file LICENSE for details.
5  * Sebastian Hammer, Adam Dickmeiss
6  *
7  * $Log: ir-tcl.c,v $
8  * Revision 1.78  1996-02-21 10:16:08  adam
9  * Simplified select handling. Only one function ir_tcl_select_set has
10  * to be externally defined.
11  *
12  * Revision 1.77  1996/02/20  17:52:58  adam
13  * Uses the YAZ oid system to name record syntax object identifiers.
14  *
15  * Revision 1.76  1996/02/20  16:09:51  adam
16  * Bug fix: didn't set element set names stamp correctly on result
17  * set records when element set names were set to the empty string.
18  *
19  * Revision 1.75  1996/02/19  15:41:53  adam
20  * Better log messages.
21  * Minor improvement of connect method.
22  *
23  * Revision 1.74  1996/02/05  17:58:03  adam
24  * Ported ir-tcl to use the beta releases of tcl7.5/tk4.1.
25  *
26  * Revision 1.73  1996/01/29  11:35:19  adam
27  * Bug fix: cs_type member renamed to comstackType to avoid conflict with
28  * cs_type macro defined by YAZ.
29  *
30  * Revision 1.72  1996/01/19  17:45:34  quinn
31  * Added debugging output
32  *
33  * Revision 1.71  1996/01/19  16:22:38  adam
34  * New method: apduDump - returns information about last incoming APDU.
35  *
36  * Revision 1.70  1996/01/10  09:18:34  adam
37  * PDU specific callbacks implemented: initRespnse, searchResponse,
38  *  presentResponse and scanResponse.
39  * Bug fix in the command line shell (tclmain.c) - discovered on OSF/1.
40  *
41  * Revision 1.69  1996/01/04  16:12:12  adam
42  * Setting PDUType renamed to eventType.
43  *
44  * Revision 1.68  1996/01/04  11:05:22  adam
45  * New setting: PDUType - returns type of last PDU returned from the target.
46  * Fixed a bug in configure/Makefile.
47  *
48  * Revision 1.67  1996/01/03  09:00:51  adam
49  * Updated to use new version of Yaz (names changed to avoid C++ conflict).
50  *
51  * Revision 1.66  1995/11/28  17:26:39  adam
52  * Removed Carriage return from ir-tcl.c!
53  * Removed misc. debug logs.
54  *
55  * Revision 1.65  1995/11/28  13:53:00  quinn
56  * Windows port.
57  *
58  * Revision 1.64  1995/11/13  15:39:18  adam
59  * Bug fix: {small,medium}SetElementSetNames weren't set correctly.
60  * Bug fix: idAuthentication weren't set correctly.
61  *
62  * Revision 1.63  1995/11/13  09:55:39  adam
63  * Multiple records at a position in a result-set with differnt
64  * element specs.
65  *
66  * Revision 1.62  1995/10/18  17:20:33  adam
67  * Work on target setup in client.tcl.
68  *
69  * Revision 1.61  1995/10/18  16:42:42  adam
70  * New settings: smallSetElementSetNames and mediumSetElementSetNames.
71  *
72  * Revision 1.60  1995/10/18  15:43:31  adam
73  * In search: mediumSetElementSetNames and smallSetElementSetNames are
74  * set to elementSetNames.
75  *
76  * Revision 1.59  1995/10/17  12:18:58  adam
77  * Bug fix: when target connection closed, the connection was not
78  * properly reestablished.
79  *
80  * Revision 1.58  1995/10/16  17:00:55  adam
81  * New setting: elementSetNames.
82  * Various client improvements. Medium presentation format looks better.
83  *
84  * Revision 1.57  1995/09/21  13:11:51  adam
85  * Support of dynamic loading.
86  * Test script uses load command if necessary.
87  *
88  * Revision 1.56  1995/08/29  15:30:14  adam
89  * Work on GRS records.
90  *
91  * Revision 1.55  1995/08/28  09:43:25  adam
92  * Minor changes. configure only searches for yaz beta 3 and versions after
93  * that.
94  *
95  * Revision 1.54  1995/08/24  12:25:16  adam
96  * Modified to work with yaz 1.0b3.
97  *
98  * Revision 1.53  1995/08/04  12:49:26  adam
99  * Bug fix: reading uninitialized variable p.
100  *
101  * Revision 1.52  1995/08/04  11:32:38  adam
102  * More work on output queue. Memory related routines moved
103  * to mem.c
104  *
105  * Revision 1.51  1995/08/03  13:22:54  adam
106  * Request queue.
107  *
108  * Revision 1.50  1995/07/20  08:09:49  adam
109  * client.tcl: Targets removed from hotTargets list when targets
110  *  are removed/modified.
111  * ir-tcl.c: More work on triggerResourceControl.
112  *
113  * Revision 1.49  1995/06/30  12:39:21  adam
114  * Bug fix: loadFile didn't set record type.
115  * The MARC routines are a little less strict in the interpretation.
116  * Script display.tcl replaces the old marc.tcl.
117  * New interactive script: shell.tcl.
118  *
119  * Revision 1.48  1995/06/27  19:03:50  adam
120  * Bug fix in do_present in ir-tcl.c: p->set_child member weren't set.
121  * nextResultSetPosition used instead of setOffset.
122  *
123  * Revision 1.47  1995/06/25  10:25:04  adam
124  * Working on triggerResourceControl. Description of compile/install
125  * procedure moved to ir-tcl.sgml.
126  *
127  * Revision 1.46  1995/06/22  13:15:06  adam
128  * Feature: SUTRS. Setting getSutrs implemented.
129  * Work on display formats.
130  * Preferred record syntax can be set by the user.
131  *
132  * Revision 1.45  1995/06/20  08:07:30  adam
133  * New setting: failInfo.
134  * Working on better cancel mechanism.
135  *
136  * Revision 1.44  1995/06/19  17:01:20  adam
137  * Minor changes.
138  *
139  * Revision 1.43  1995/06/19  13:06:08  adam
140  * New define: IR_TCL_VERSION.
141  *
142  * Revision 1.42  1995/06/19  08:08:52  adam
143  * client.tcl: hotTargets now contain both database and target name.
144  * ir-tcl.c: setting protocol edited. Errors in callbacks are logged
145  * by logf(LOG_WARN, ...) calls.
146  *
147  * Revision 1.41  1995/06/16  12:28:16  adam
148  * Implemented preferredRecordSyntax.
149  * Minor changes in diagnostic handling.
150  * Record list deleted when connection closes.
151  *
152  * Revision 1.40  1995/06/14  13:37:18  adam
153  * Setting recordType implemented.
154  * Setting implementationVersion implemented.
155  * Settings implementationId / implementationName edited.
156  *
157  * Revision 1.39  1995/06/08  10:26:32  adam
158  * Bug fix in ir_strdup.
159  *
160  * Revision 1.38  1995/06/01  16:36:47  adam
161  * About buttons. Minor bug fixes.
162  *
163  * Revision 1.37  1995/06/01  07:31:20  adam
164  * Rename of many typedefs -> IrTcl_...
165  *
166  * Revision 1.36  1995/05/31  13:09:59  adam
167  * Client searches/presents may be interrupted.
168  * New moving book-logo.
169  *
170  * Revision 1.35  1995/05/31  08:36:33  adam
171  * Bug fix in client.tcl: didn't save options on clientrc.tcl.
172  * New method: referenceId. More work on scan.
173  *
174  * Revision 1.34  1995/05/29  10:33:42  adam
175  * README and rename of startup script.
176  *
177  * Revision 1.33  1995/05/29  09:15:11  quinn
178  * Changed CS_SR to PROTO_SR, etc.
179  *
180  * Revision 1.32  1995/05/29  08:44:16  adam
181  * Work on delete of objects.
182  *
183  * Revision 1.31  1995/05/26  11:44:10  adam
184  * Bugs fixed. More work on MARC utilities and queries. Test
185  * client is up-to-date again.
186  *
187  * Revision 1.30  1995/05/26  08:54:11  adam
188  * New MARC utilities. Uses prefix query.
189  *
190  * Revision 1.29  1995/05/24  14:10:22  adam
191  * Work on idAuthentication, protocolVersion and options.
192  *
193  * Revision 1.28  1995/05/23  15:34:48  adam
194  * Many new settings, userInformationField, smallSetUpperBound, etc.
195  * A number of settings are inherited when ir-set is executed.
196  * This version is incompatible with the graphical test client (client.tcl).
197  *
198  * Revision 1.27  1995/05/11  15:34:47  adam
199  * Scan request changed a bit. This version works with RLG.
200  *
201  * Revision 1.26  1995/04/18  16:11:51  adam
202  * First version of graphical Scan. Some work on query-by-form.
203  *
204  * Revision 1.25  1995/04/17  09:37:17  adam
205  * Further development of scan.
206  *
207  * Revision 1.24  1995/04/11  14:16:42  adam
208  * Further work on scan. Response works. Entries aren't saved yet.
209  *
210  * Revision 1.23  1995/04/10  10:50:27  adam
211  * Result-set name defaults to suffix of ir-set name.
212  * Started working on scan. Not finished at this point.
213  *
214  * Revision 1.22  1995/03/31  10:43:03  adam
215  * More robust when getting bad MARC records.
216  *
217  * Revision 1.21  1995/03/31  08:56:37  adam
218  * New button "Search".
219  *
220  * Revision 1.20  1995/03/29  16:07:09  adam
221  * Bug fix: Didn't use setName in present request.
222  *
223  * Revision 1.19  1995/03/28  12:45:23  adam
224  * New ir method failback: called on disconnect/protocol error.
225  * New ir set/get method: protocol: SR / Z3950.
226  * Simple popup and disconnect when failback is invoked.
227  *
228  * Revision 1.18  1995/03/21  15:50:12  adam
229  * Minor changes.
230  *
231  * Revision 1.17  1995/03/21  13:41:03  adam
232  * Comstack cs_create not used too often. Non-blocking connect.
233  *
234  * Revision 1.16  1995/03/21  08:26:06  adam
235  * New method, setName, to specify the result set name (other than Default).
236  * New method, responseStatus, which returns diagnostic info, if any, after
237  * present response / search response.
238  *
239  * Revision 1.15  1995/03/20  15:24:07  adam
240  * Diagnostic records saved on searchResponse.
241  *
242  * Revision 1.14  1995/03/20  08:53:22  adam
243  * Event loop in tclmain.c rewritten. New method searchStatus.
244  *
245  * Revision 1.13  1995/03/17  18:26:17  adam
246  * Non-blocking i/o used now. Database names popup as cascade items.
247  *
248  * Revision 1.12  1995/03/17  15:45:00  adam
249  * Improved target/database setup.
250  *
251  * Revision 1.11  1995/03/16  17:54:03  adam
252  * Minor changes really.
253  *
254  * Revision 1.10  1995/03/15  16:14:50  adam
255  * Blocking arg in cs_create changed.
256  *
257  * Revision 1.9  1995/03/15  13:59:24  adam
258  * Minor changes.
259  *
260  * Revision 1.8  1995/03/15  08:25:16  adam
261  * New method presentStatus to check for error on present. Misc. cleanup
262  * of IrTcl_RecordList manipulations. Full MARC record presentation in
263  * search.tcl.
264  *
265  * Revision 1.7  1995/03/14  17:32:29  adam
266  * Presentation of full Marc record in popup window.
267  *
268  * Revision 1.6  1995/03/12  19:31:55  adam
269  * Pattern matching implemented when retrieving MARC records. More
270  * diagnostic functions.
271  *
272  * Revision 1.5  1995/03/10  18:00:15  adam
273  * Actual presentation in line-by-line format. RPN query support.
274  *
275  * Revision 1.4  1995/03/09  16:15:08  adam
276  * First presentRequest attempts. Hot-target list.
277  *
278  */
279
280 #include <stdlib.h>
281 #include <stdio.h>
282 #include <unistd.h>
283 #include <time.h>
284 #include <assert.h>
285
286 #define CS_BLOCK 0
287
288 #define IRTCL_GENERIC_FILES 0
289
290 #include "ir-tclp.h"
291
292 typedef struct {
293     int type;
294     char *name;
295     int (*method) (void *obj, Tcl_Interp *interp, int argc, char **argv);
296 } IrTcl_Method;
297
298 typedef struct {
299     void *obj;
300     IrTcl_Method *tab;
301 } IrTcl_Methods;
302
303 static void ir_deleteDiags (IrTcl_Diagnostic **dst_list, int *dst_num);
304 static int do_disconnect (void *obj, Tcl_Interp *interp, 
305                           int argc, char **argv);
306
307 static void ir_select_notify (ClientData clientData, int r, int w, int e);
308
309 void ir_select_add (int fd, void *obj)
310 {
311     ir_tcl_select_set (ir_select_notify, fd, obj, 1, 0, 0);
312 }
313
314 void ir_select_add_write (int fd, void *obj)
315 {
316     ir_tcl_select_set (ir_select_notify, fd, obj, 1, 1, 0);
317 }
318
319 void ir_select_remove (int fd, void *obj)
320 {
321     ir_tcl_select_set (NULL, fd, obj, 0, 0, 0);
322 }
323
324 void ir_select_remove_write (int fd, void *obj)
325 {
326     ir_tcl_select_set (ir_select_notify, fd, obj, 1, 0, 0);
327 }
328
329 static IrTcl_RecordList *new_IR_record (IrTcl_SetObj *setobj, 
330                                         int no, int which, 
331                                         const char *elements)
332 {
333     IrTcl_RecordList *rl;
334
335     if (elements && !*elements)
336         elements = NULL;
337     for (rl = setobj->record_list; rl; rl = rl->next)
338     {
339         if (no == rl->no && (!rl->elements || !elements ||
340                              !strcmp(elements, rl->elements)))
341         {
342             free (rl->elements);
343             switch (rl->which)
344             {
345             case Z_NamePlusRecord_databaseRecord:
346                 free (rl->u.dbrec.buf);
347                 rl->u.dbrec.buf = NULL;
348                 break;
349             case Z_NamePlusRecord_surrogateDiagnostic:
350                 ir_deleteDiags (&rl->u.surrogateDiagnostics.list,
351                                 &rl->u.surrogateDiagnostics.num);
352                 break;
353             }
354             break;
355         }
356     }
357     if (!rl)
358     {
359         rl = ir_tcl_malloc (sizeof(*rl));
360         rl->next = setobj->record_list;
361         rl->no = no;
362         setobj->record_list = rl;
363     }
364     rl->which = which;
365     ir_tcl_strdup (NULL, &rl->elements, elements);
366     return rl;
367 }
368
369 /* 
370  * IrTcl_eval
371  */
372 int IrTcl_eval (Tcl_Interp *interp, const char *command)
373 {
374     char *tmp = ir_tcl_malloc (strlen(command)+1);
375     int r;
376
377     strcpy (tmp, command);
378     r = Tcl_Eval (interp, tmp);
379     if (r == TCL_ERROR)
380         logf (LOG_WARN, "Tcl error in line %d: %s", interp->errorLine, 
381               interp->result);
382     Tcl_FreeResult (interp);
383     free (tmp);
384     return r;
385 }
386
387 /*
388  * IrTcl_getRecordSyntaxStr: Return record syntax name of object id
389  */
390 static char *IrTcl_getRecordSyntaxStr (enum oid_value value)
391 {
392     int *o;
393     struct oident ent, *entp;
394
395     ent.proto = PROTO_Z3950;
396     ent.oclass = CLASS_RECSYN;
397     ent.value = value;
398
399     o = oid_getoidbyent (&ent);
400     entp = oid_getentbyoid (o);
401     
402     if (!entp)
403         return "";
404     return entp->desc;
405 }
406
407 /*
408  * IrTcl_getRecordSyntaxVal: Return record syntax value of string
409  */
410 static enum oid_value IrTcl_getRecordSyntaxVal (const char *name)
411 {
412     return oid_getvalbyname (name);
413 }
414
415 static IrTcl_RecordList *find_IR_record (IrTcl_SetObj *setobj, int no)
416 {
417     IrTcl_RecordList *rl;
418
419     for (rl = setobj->record_list; rl; rl = rl->next)
420         if (no == rl->no && 
421             (!setobj->recordElements || !rl->elements || 
422              !strcmp (setobj->recordElements, rl->elements)))
423             return rl;
424     return NULL;
425 }
426
427 static void delete_IR_records (IrTcl_SetObj *setobj)
428 {
429     IrTcl_RecordList *rl, *rl1;
430
431     for (rl = setobj->record_list; rl; rl = rl1)
432     {
433         switch (rl->which)
434         {
435         case Z_NamePlusRecord_databaseRecord:
436             free (rl->u.dbrec.buf);
437             break;
438         case Z_NamePlusRecord_surrogateDiagnostic:
439             ir_deleteDiags (&rl->u.surrogateDiagnostics.list,
440                             &rl->u.surrogateDiagnostics.num);
441             break;
442         }
443         rl1 = rl->next;
444         free (rl);
445     }
446     setobj->record_list = NULL;
447 }
448
449 /*
450  * get_set_int: Set/get integer value
451  */
452 static int get_set_int (int *val, Tcl_Interp *interp, int argc, char **argv)
453 {
454     char buf[20];
455     
456     if (argc == 3)
457     {
458         if (Tcl_GetInt (interp, argv[2], val)==TCL_ERROR)
459             return TCL_ERROR;
460     }
461     sprintf (buf, "%d", *val);
462     Tcl_AppendResult (interp, buf, NULL);
463     return TCL_OK;
464 }
465
466 /*
467  * ir_method: Search for method in table and invoke method handler
468  */
469 int ir_method (Tcl_Interp *interp, int argc, char **argv, IrTcl_Methods *tab)
470 {
471     IrTcl_Methods *tab_i = tab;
472     IrTcl_Method *t;
473
474     for (tab_i = tab; tab_i->tab; tab_i++)
475         for (t = tab_i->tab; t->name; t++)
476             if (argc <= 0)
477             {
478                 if ((*t->method)(tab_i->obj, interp, argc, argv) == TCL_ERROR)
479                     return TCL_ERROR;
480             }
481             else
482                 if (!strcmp (t->name, argv[1]))
483                     return (*t->method)(tab_i->obj, interp, argc, argv);
484
485     if (argc <= 0)
486         return TCL_OK;
487     Tcl_AppendResult (interp, "Bad method: ", argv[1], 
488                       ". Possible methods:", NULL);
489     for (tab_i = tab; tab_i->tab; tab_i++)
490         for (t = tab_i->tab; t->name; t++)
491             Tcl_AppendResult (interp, " ", t->name, NULL);
492     return TCL_ERROR;
493 }
494
495 /*
496  * ir_method_r: Get status for all readable elements
497  */
498 int ir_method_r (void *obj, Tcl_Interp *interp, int argc, char **argv,
499                  IrTcl_Method *tab)
500 {
501     char *argv_n[3];
502     int argc_n;
503
504     argv_n[0] = argv[0];
505     argc_n = 2;
506     for (; tab->name; tab++)
507         if (tab->type)
508         {
509             argv_n[1] = tab->name;
510             Tcl_AppendResult (interp, "{", NULL);
511             (*tab->method)(obj, interp, argc_n, argv_n);
512             Tcl_AppendResult (interp, "} ", NULL);
513         }
514     return TCL_OK;
515 }
516
517 /*
518  *  ir_named_bits: get/set named bits
519  */
520 int ir_named_bits (struct ir_named_entry *tab, Odr_bitmask *ob,
521                    Tcl_Interp *interp, int argc, char **argv)
522 {
523     struct ir_named_entry *ti;
524     if (argc > 0)
525     {
526         int no;
527         ODR_MASK_ZERO (ob);
528         for (no = 0; no < argc; no++)
529         {
530             for (ti = tab; ti->name; ti++)
531                 if (!strcmp (argv[no], ti->name))
532                 {
533                     ODR_MASK_SET (ob, ti->pos);
534                     break;
535                 }
536             if (!ti->name)
537             {
538                 Tcl_AppendResult (interp, "Bad bit mask: ", argv[no], NULL);
539                 return TCL_ERROR;
540             }
541         }
542         return TCL_OK;
543     }
544     for (ti = tab; ti->name; ti++)
545         if (ODR_MASK_GET (ob, ti->pos))
546             Tcl_AppendElement (interp, ti->name);
547     return TCL_OK;
548 }
549
550 static void set_referenceId (ODR o, Z_ReferenceId **dst, const char *src)
551 {
552     if (!src || !*src)
553         *dst = NULL;
554     else
555     {
556         *dst = odr_malloc (o, sizeof(**dst));
557         (*dst)->size = (*dst)->len = strlen(src);
558         (*dst)->buf = odr_malloc (o, (*dst)->len);
559         memcpy ((*dst)->buf, src, (*dst)->len);
560     }
561 }
562
563 static void get_referenceId (char **dst, Z_ReferenceId *src)
564 {
565     free (*dst);
566     if (!src)
567     {
568         *dst = NULL;
569         return;
570     }
571     *dst = ir_tcl_malloc (src->len+1);
572     memcpy (*dst, src->buf, src->len);
573     (*dst)[src->len] = '\0';
574 }
575
576 /* ------------------------------------------------------- */
577
578 /*
579  * do_init_request: init method on IR object
580  */
581 static int do_init_request (void *obj, Tcl_Interp *interp,
582                             int argc, char **argv)
583 {
584     Z_APDU *apdu;
585     IrTcl_Obj *p = obj;
586     Z_InitRequest *req;
587
588     if (argc <= 0)
589         return TCL_OK;
590     if (!p->cs_link)
591     {
592         interp->result = "init: not connected";
593         return TCL_ERROR;
594     }
595     apdu = zget_APDU (p->odr_out, Z_APDU_initRequest);
596     req = apdu->u.initRequest;
597
598     set_referenceId (p->odr_out, &req->referenceId, p->set_inher.referenceId);
599     req->options = &p->options;
600     req->protocolVersion = &p->protocolVersion;
601     req->preferredMessageSize = &p->preferredMessageSize;
602     req->maximumRecordSize = &p->maximumRecordSize;
603
604     if (p->idAuthenticationGroupId)
605     {
606         Z_IdPass *pass = odr_malloc (p->odr_out, sizeof(*pass));
607         Z_IdAuthentication *auth = odr_malloc (p->odr_out, sizeof(*auth));
608
609         logf (LOG_DEBUG, "using pass authentication");
610
611         auth->which = Z_IdAuthentication_idPass;
612         auth->u.idPass = pass;
613         if (p->idAuthenticationGroupId && *p->idAuthenticationGroupId)
614             pass->groupId = p->idAuthenticationGroupId;
615         else
616             pass->groupId = NULL;
617         if (p->idAuthenticationUserId && *p->idAuthenticationUserId)
618             pass->userId = p->idAuthenticationUserId;
619         else
620             pass->userId = NULL;
621         if (p->idAuthenticationPassword && *p->idAuthenticationPassword)
622             pass->password = p->idAuthenticationPassword;
623         else
624             pass->password = NULL;
625         req->idAuthentication = auth;
626     }
627     else if (!p->idAuthenticationOpen || !*p->idAuthenticationOpen)
628         req->idAuthentication = NULL;
629     else
630     {
631         Z_IdAuthentication *auth = odr_malloc (p->odr_out, sizeof(*auth));
632
633         logf (LOG_DEBUG, "using open authentication");
634         auth->which = Z_IdAuthentication_open;
635         auth->u.open = p->idAuthenticationOpen;
636         req->idAuthentication = auth;
637     }
638     req->implementationId = p->implementationId;
639     req->implementationName = p->implementationName;
640     req->implementationVersion = p->implementationVersion;
641     req->userInformationField = 0;
642
643     return ir_tcl_send_APDU (interp, p, apdu, "init", *argv);
644 }
645
646 /*
647  * do_protocolVersion: Set protocol Version
648  */
649 static int do_protocolVersion (void *obj, Tcl_Interp *interp,
650                                int argc, char **argv)
651 {
652     int version, i;
653     char buf[10];
654     IrTcl_Obj *p = obj;
655
656     if (argc <= 0)
657     {
658         ODR_MASK_ZERO (&p->protocolVersion);
659         ODR_MASK_SET (&p->protocolVersion, 0);
660         ODR_MASK_SET (&p->protocolVersion, 1);
661         return TCL_OK;
662     }
663     if (argc == 3)
664     {
665         if (Tcl_GetInt (interp, argv[2], &version)==TCL_ERROR)
666             return TCL_ERROR;
667         ODR_MASK_ZERO (&p->protocolVersion);
668         for (i = 0; i<version; i++)
669             ODR_MASK_SET (&p->protocolVersion, i);
670     }
671     for (i = 4; --i >= 0; )
672         if (ODR_MASK_GET (&p->protocolVersion, i))
673             break;
674     sprintf (buf, "%d", i+1);
675     interp->result = buf;
676     return TCL_OK;
677 }
678
679 /*
680  * do_options: Set options
681  */
682 static int do_options (void *obj, Tcl_Interp *interp,
683                        int argc, char **argv)
684 {
685     static struct ir_named_entry options_tab[] = {
686     { "search", 0 },
687     { "present", 1 },
688     { "delSet", 2 },
689     { "resourceReport", 3 },
690     { "triggerResourceCtrl", 4},
691     { "resourceCtrl", 5},
692     { "accessCtrl", 6},
693     { "scan", 7},
694     { "sort", 8},
695     { "extendedServices", 10},
696     { "level-1Segmentation", 11},
697     { "level-2Segmentation", 12},
698     { "concurrentOperations", 13},
699     { "namedResultSets", 14},
700     { NULL, 0}
701     };
702     IrTcl_Obj *p = obj;
703
704     if (argc <= 0)
705     {
706         ODR_MASK_ZERO (&p->options);
707         ODR_MASK_SET (&p->options, 0);
708         ODR_MASK_SET (&p->options, 1);
709         ODR_MASK_SET (&p->options, 4);
710         ODR_MASK_SET (&p->options, 7);
711         ODR_MASK_SET (&p->options, 14);
712         return TCL_OK;
713     }
714     return ir_named_bits (options_tab, &p->options, interp, argc-2, argv+2);
715 }
716
717 /*
718  * do_apduInfo: Get APDU information
719  */
720 static int do_apduInfo (void *obj, Tcl_Interp *interp, int argc, char **argv)
721 {
722     char buf[16];
723     FILE *apduf;
724     IrTcl_Obj *p = obj;
725
726     if (argc <= 0)
727         return TCL_OK;
728     sprintf (buf, "%d", p->apduLen);
729     Tcl_AppendElement (interp, buf);
730     sprintf (buf, "%d", p->apduOffset);
731     Tcl_AppendElement (interp, buf);
732     if (!p->buf_in)
733     {
734         Tcl_AppendElement (interp, "");
735         return TCL_OK;
736     }
737     apduf = fopen ("apdu.tmp", "w");
738     if (!apduf)
739     {
740         Tcl_AppendElement (interp, "");
741         return TCL_OK;
742     }
743     odr_dumpBER (apduf, p->buf_in, p->apduLen);
744     fclose (apduf);
745     if (!(apduf = fopen ("apdu.tmp", "r")))
746         Tcl_AppendElement (interp, "");
747     else
748     {
749         int c;
750         
751         Tcl_AppendResult (interp, " {", NULL);
752         while ((c = getc (apduf)) != EOF)
753         {
754             buf[0] = c;
755             buf[1] = '\0';
756             Tcl_AppendResult (interp, buf, NULL);
757         }
758         fclose (apduf);
759         Tcl_AppendResult (interp, "}", NULL);
760     }
761     unlink ("apdu.tmp");
762     return TCL_OK;
763 }
764
765 /*
766  * do_failInfo: Get fail information
767  */
768 static int do_failInfo (void *obj, Tcl_Interp *interp, int argc, char **argv)
769 {
770     char buf[16], *cp;
771     IrTcl_Obj *p = obj;
772
773     if (argc <= 0)
774     {
775         p->failInfo = 0;
776         return TCL_OK;
777     }
778     sprintf (buf, "%d", p->failInfo);
779     switch (p->failInfo)
780     {
781     case 0:
782         cp = "ok";
783         break;
784     case IR_TCL_FAIL_CONNECT:
785         cp = "connect failed";
786         break;
787     case IR_TCL_FAIL_READ:
788         cp = "connection closed";
789         break;
790     case IR_TCL_FAIL_WRITE:
791         cp = "connection closed";
792         break;
793     case IR_TCL_FAIL_IN_APDU:
794         cp = "failed to decode incoming APDU";
795         break;
796     case IR_TCL_FAIL_UNKNOWN_APDU:
797         cp = "unknown APDU";
798         break;
799     default:
800         cp = "";
801     } 
802     Tcl_AppendElement (interp, buf);
803     Tcl_AppendElement (interp, cp);
804     return TCL_OK;
805 }
806
807 /*
808  * do_preferredMessageSize: Set/get preferred message size
809  */
810 static int do_preferredMessageSize (void *obj, Tcl_Interp *interp,
811                                     int argc, char **argv)
812 {
813     IrTcl_Obj *p = obj;
814
815     if (argc <= 0)
816     {
817         p->preferredMessageSize = 30000;
818         return TCL_OK;
819     }
820     return get_set_int (&p->preferredMessageSize, interp, argc, argv);
821 }
822
823 /*
824  * do_maximumRecordSize: Set/get maximum record size
825  */
826 static int do_maximumRecordSize (void *obj, Tcl_Interp *interp,
827                                  int argc, char **argv)
828 {
829     IrTcl_Obj *p = obj;
830
831     if (argc <= 0)
832     {
833         p->maximumRecordSize = 30000;
834         return TCL_OK;
835     }
836     return get_set_int (&p->maximumRecordSize, interp, argc, argv);
837 }
838
839 /*
840  * do_initResult: Get init result
841  */
842 static int do_initResult (void *obj, Tcl_Interp *interp,
843                           int argc, char **argv)
844 {
845     IrTcl_Obj *p = obj;
846    
847     if (argc <= 0)
848         return TCL_OK;
849     return get_set_int (&p->initResult, interp, argc, argv);
850 }
851
852
853 /*
854  * do_implementationName: Set/get Implementation Name.
855  */
856 static int do_implementationName (void *obj, Tcl_Interp *interp,
857                                     int argc, char **argv)
858 {
859     IrTcl_Obj *p = obj;
860
861     if (argc == 0)
862         return ir_tcl_strdup (interp, &p->implementationName,
863                           "Index Data/IrTcl on YAZ");
864     else if (argc == -1)
865         return ir_tcl_strdel (interp, &p->implementationName);
866     if (argc == 3)
867     {
868         free (p->implementationName);
869         if (ir_tcl_strdup (interp, &p->implementationName, argv[2])
870             == TCL_ERROR)
871             return TCL_ERROR;
872     }
873     Tcl_AppendResult (interp, p->implementationName, (char*) NULL);
874     return TCL_OK;
875 }
876
877 /*
878  * do_implementationId: Get Implementation Id.
879  */
880 static int do_implementationId (void *obj, Tcl_Interp *interp,
881                                 int argc, char **argv)
882 {
883     IrTcl_Obj *p = obj;
884
885     if (argc == 0)
886         return ir_tcl_strdup (interp, &p->implementationId, "YAZ (id=81)");
887     else if (argc == -1)
888         return ir_tcl_strdel (interp, &p->implementationId);
889     Tcl_AppendResult (interp, p->implementationId, (char*) NULL);
890     return TCL_OK;
891 }
892
893 /*
894  * do_implementationVersion: get Implementation Version.
895  */
896 static int do_implementationVersion (void *obj, Tcl_Interp *interp,
897                                      int argc, char **argv)
898 {
899     IrTcl_Obj *p = obj;
900
901     if (argc == 0)
902         return ir_tcl_strdup (interp, &p->implementationVersion, 
903                           "YAZ: " YAZ_VERSION " / IrTcl: " IR_TCL_VERSION);
904     else if (argc == -1)
905         return ir_tcl_strdel (interp, &p->implementationVersion);
906     Tcl_AppendResult (interp, p->implementationVersion, (char*) NULL);
907     return TCL_OK;
908 }
909
910 /*
911  * do_targetImplementationName: Get Implementation Name of target.
912  */
913 static int do_targetImplementationName (void *obj, Tcl_Interp *interp,
914                                         int argc, char **argv)
915 {
916     IrTcl_Obj *p = obj;
917     
918     if (argc == 0)
919     {
920         p->targetImplementationName = NULL;
921         return TCL_OK;
922     }
923     else if (argc == -1)
924         return ir_tcl_strdel (interp, &p->targetImplementationName);
925     Tcl_AppendResult (interp, p->targetImplementationName, (char*) NULL);
926     return TCL_OK;
927 }
928
929 /*
930  * do_targetImplementationId: Get Implementation Id of target
931  */
932 static int do_targetImplementationId (void *obj, Tcl_Interp *interp,
933                                       int argc, char **argv)
934 {
935     IrTcl_Obj *p = obj;
936
937     if (argc == 0)
938     {
939         p->targetImplementationId = NULL;
940         return TCL_OK;
941     }
942     else if (argc == -1)
943         return ir_tcl_strdel (interp, &p->targetImplementationId);
944     Tcl_AppendResult (interp, p->targetImplementationId, (char*) NULL);
945     return TCL_OK;
946 }
947
948 /*
949  * do_targetImplementationVersion: Get Implementation Version of target
950  */
951 static int do_targetImplementationVersion (void *obj, Tcl_Interp *interp,
952                                            int argc, char **argv)
953 {
954     IrTcl_Obj *p = obj;
955
956     if (argc == 0)
957     {
958         p->targetImplementationVersion = NULL;
959         return TCL_OK;
960     }
961     else if (argc == -1)
962         return ir_tcl_strdel (interp, &p->targetImplementationVersion);
963     Tcl_AppendResult (interp, p->targetImplementationVersion, (char*) NULL);
964     return TCL_OK;
965 }
966
967 /*
968  * do_idAuthentication: Set/get id Authentication
969  */
970 static int do_idAuthentication (void *obj, Tcl_Interp *interp,
971                                 int argc, char **argv)
972 {
973     IrTcl_Obj *p = obj;
974
975     if (argc >= 3 || argc == -1)
976     {
977         free (p->idAuthenticationOpen);
978         free (p->idAuthenticationGroupId);
979         free (p->idAuthenticationUserId);
980         free (p->idAuthenticationPassword);
981     }
982     if (argc >= 3 || argc <= 0)
983     {
984         p->idAuthenticationOpen = NULL;
985         p->idAuthenticationGroupId = NULL;
986         p->idAuthenticationUserId = NULL;
987         p->idAuthenticationPassword = NULL;
988     }
989     if (argc <= 0)
990         return TCL_OK;
991     if (argc >= 3)
992     {
993         if (argc == 3)
994         {
995             if (argv[2][0] && 
996                 ir_tcl_strdup (interp, &p->idAuthenticationOpen, argv[2])
997                 == TCL_ERROR)
998                 return TCL_ERROR;
999         }
1000         else if (argc == 5)
1001         {
1002             if (argv[2][0] && 
1003                 ir_tcl_strdup (interp, &p->idAuthenticationGroupId, argv[2])
1004                 == TCL_ERROR)
1005                 return TCL_ERROR;
1006             if (argv[3][0] && 
1007                 ir_tcl_strdup (interp, &p->idAuthenticationUserId, argv[3])
1008                 == TCL_ERROR)
1009                 return TCL_ERROR;
1010             if (argv[4][0] &&
1011                 ir_tcl_strdup (interp, &p->idAuthenticationPassword, argv[4])
1012                 == TCL_ERROR)
1013                 return TCL_ERROR;
1014         }
1015     }
1016     if (p->idAuthenticationOpen)
1017         Tcl_AppendElement (interp, p->idAuthenticationOpen);
1018     else if (p->idAuthenticationGroupId)
1019     {
1020         Tcl_AppendElement (interp, p->idAuthenticationGroupId);
1021         Tcl_AppendElement (interp, p->idAuthenticationUserId);
1022         Tcl_AppendElement (interp, p->idAuthenticationPassword);
1023     }
1024     return TCL_OK;
1025 }
1026
1027 /*
1028  * do_connect: connect method on IR object
1029  */
1030 static int do_connect (void *obj, Tcl_Interp *interp,
1031                        int argc, char **argv)
1032 {
1033     void *addr;
1034     IrTcl_Obj *p = obj;
1035     int r;
1036
1037     if (argc <= 0)
1038         return TCL_OK;
1039     if (argc == 3)
1040     {
1041         if (p->hostname)
1042         {
1043             interp->result = "already connected";
1044             return TCL_ERROR;
1045         }
1046         if (!strcmp (p->comstackType, "tcpip"))
1047         {
1048             p->cs_link = cs_create (tcpip_type, CS_BLOCK, p->protocol_type);
1049             addr = tcpip_strtoaddr (argv[2]);
1050             if (!addr)
1051             {
1052                 interp->result = "tcpip_strtoaddr fail";
1053                 return TCL_ERROR;
1054             }
1055             logf (LOG_DEBUG, "tcp/ip connect %s", argv[2]);
1056         }
1057         else if (!strcmp (p->comstackType, "mosi"))
1058         {
1059 #if MOSI
1060             p->cs_link = cs_create (mosi_type, CS_BLOCK, p->protocol_type);
1061             addr = mosi_strtoaddr (argv[2]);
1062             if (!addr)
1063             {
1064                 interp->result = "mosi_strtoaddr fail";
1065                 return TCL_ERROR;
1066             }
1067             logf (LOG_DEBUG, "mosi connect %s", argv[2]);
1068 #else
1069             interp->result = "MOSI support not there";
1070             return TCL_ERROR;
1071 #endif
1072         }
1073         else 
1074         {
1075             Tcl_AppendResult (interp, "Bad comstack type: ", 
1076                               p->comstackType, NULL);
1077             return TCL_ERROR;
1078         }
1079         if (ir_tcl_strdup (interp, &p->hostname, argv[2]) == TCL_ERROR)
1080             return TCL_ERROR;
1081 #if IRTCL_GENERIC_FILES
1082 #ifdef WINDOWS
1083         p->csFile = Tcl_GetFile (cs_fileno(p->cs_link), TCL_WIN_SOCKET);
1084 #else
1085         p->csFile = Tcl_GetFile (cs_fileno(p->cs_link), TCL_UNIX_FD);
1086 #endif
1087 #endif
1088         if ((r=cs_connect (p->cs_link, addr)) < 0)
1089         {
1090             interp->result = "connect fail";
1091             do_disconnect (p, NULL, 2, NULL);
1092             return TCL_ERROR;
1093         }
1094         logf(LOG_DEBUG, "cs_connect() returned %d fd=%d", r,
1095              cs_fileno(p->cs_link));
1096         p->eventType = "connect";
1097 #if IRTCL_GENERIC_FILES
1098         ir_select_add (p->csFile, p);
1099 #else
1100         ir_select_add (cs_fileno (p->cs_link), p);
1101 #endif
1102         if (r == 1)
1103         {
1104 #if IRTCL_GENERIC_FILES
1105             ir_select_add_write (p->csFile, p);
1106 #else
1107             ir_select_add_write (cs_fileno (p->cs_link), p);
1108 #endif
1109             p->state = IR_TCL_R_Connecting;
1110         }
1111         else
1112         {
1113             p->state = IR_TCL_R_Idle;
1114             if (p->callback)
1115                 IrTcl_eval (p->interp, p->callback);
1116         }
1117     }
1118     else
1119         Tcl_AppendResult (interp, p->hostname, NULL);
1120     return TCL_OK;
1121 }
1122
1123 /*
1124  * do_disconnect: disconnect method on IR object
1125  */
1126 static int do_disconnect (void *obj, Tcl_Interp *interp,
1127                           int argc, char **argv)
1128 {
1129     IrTcl_Obj *p = obj;
1130
1131     if (argc == 0)
1132     {
1133         p->state = IR_TCL_R_Idle;
1134         p->eventType = NULL;
1135         p->hostname = NULL;
1136         p->cs_link = NULL;
1137 #if IRTCL_GENERIC_FILES
1138         p->csFile = 0;
1139 #endif
1140         return TCL_OK;
1141     }
1142     if (p->hostname)
1143     {
1144         logf(LOG_DEBUG, "Closing connection to %s", p->hostname);
1145         free (p->hostname);
1146         p->hostname = NULL;
1147 #if IRTCL_GENERIC_FILES
1148         ir_select_remove_write (p->csFile, p);
1149         ir_select_remove (p->csFile, p);
1150 #else
1151         ir_select_remove_write (cs_fileno (p->cs_link), p);
1152         ir_select_remove (cs_fileno (p->cs_link), p);
1153 #endif
1154
1155         odr_reset (p->odr_in);
1156
1157         assert (p->cs_link);
1158         cs_close (p->cs_link);
1159         p->cs_link = NULL;
1160 #if IRTCL_GENERIC_FILES
1161         Tcl_FreeFile (p->csFile);
1162         p->csFile = NULL;
1163 #endif
1164
1165         ODR_MASK_ZERO (&p->options);
1166         ODR_MASK_SET (&p->options, 0);
1167         ODR_MASK_SET (&p->options, 1);
1168         ODR_MASK_SET (&p->options, 4);
1169         ODR_MASK_SET (&p->options, 7);
1170         ODR_MASK_SET (&p->options, 14);
1171
1172         ODR_MASK_ZERO (&p->protocolVersion);
1173         ODR_MASK_SET (&p->protocolVersion, 0);
1174         ODR_MASK_SET (&p->protocolVersion, 1);
1175         ir_tcl_del_q (p);
1176     }
1177     assert (!p->cs_link);
1178     return TCL_OK;
1179 }
1180
1181 /*
1182  * do_comstack: Set/get comstack method on IR object
1183  */
1184 static int do_comstack (void *o, Tcl_Interp *interp,
1185                         int argc, char **argv)
1186 {
1187     IrTcl_Obj *obj = o;
1188
1189     if (argc == 0)
1190         return ir_tcl_strdup (interp, &obj->comstackType, "tcpip");
1191     else if (argc == -1)
1192         return ir_tcl_strdel (interp, &obj->comstackType);
1193     else if (argc == 3)
1194     {
1195         free (obj->comstackType);
1196         if (ir_tcl_strdup (interp, &obj->comstackType, argv[2]) == TCL_ERROR)
1197             return TCL_ERROR;
1198     }
1199     Tcl_AppendElement (interp, obj->comstackType);
1200     return TCL_OK;
1201 }
1202
1203 /*
1204  * do_logLevel: Set log level
1205  */
1206 static int do_logLevel (void *o, Tcl_Interp *interp,
1207                         int argc, char **argv)
1208 {
1209     if (argc <= 2)
1210         return TCL_OK;
1211     if (argc == 3)
1212         log_init (log_mask_str (argv[2]), "", NULL);
1213     else if (argc == 4)
1214         log_init (log_mask_str (argv[2]), argv[3], NULL);
1215     else if (argc == 5)
1216         log_init (log_mask_str (argv[2]), argv[3], argv[4]);
1217     return TCL_OK;
1218 }
1219
1220
1221 /*
1222  * do_eventType: Return type of last event
1223  */
1224 static int do_eventType (void *obj, Tcl_Interp *interp,
1225                          int argc, char **argv)
1226 {
1227     IrTcl_Obj *p = obj;
1228
1229     if (argc <= 0)
1230     {
1231         p->eventType = NULL;
1232         return TCL_OK;
1233     }
1234     Tcl_AppendElement (interp, p->eventType ? p->eventType : "");
1235     return TCL_OK;
1236 }
1237
1238
1239 /*
1240  * do_callback: add callback
1241  */
1242 static int do_callback (void *obj, Tcl_Interp *interp,
1243                         int argc, char **argv)
1244 {
1245     IrTcl_Obj *p = obj;
1246
1247     if (argc == 0)
1248     {
1249         p->callback = NULL;
1250         return TCL_OK;
1251     }
1252     else if (argc == -1)
1253         return ir_tcl_strdel (interp, &p->callback);
1254     if (argc == 3)
1255     {
1256         free (p->callback);
1257         if (argv[2][0])
1258         {
1259             if (ir_tcl_strdup (interp, &p->callback, argv[2]) == TCL_ERROR)
1260                 return TCL_ERROR;
1261         }
1262         else
1263             p->callback = NULL;
1264     }
1265     return TCL_OK;
1266 }
1267
1268 /*
1269  * do_failback: add error handle callback
1270  */
1271 static int do_failback (void *obj, Tcl_Interp *interp,
1272                           int argc, char **argv)
1273 {
1274     IrTcl_Obj *p = obj;
1275
1276     if (argc == 0)
1277     {
1278         p->failback = NULL;
1279         return TCL_OK;
1280     }
1281     else if (argc == -1)
1282         return ir_tcl_strdel (interp, &p->failback);
1283     else if (argc == 3)
1284     {
1285         free (p->failback);
1286         if (argv[2][0])
1287         {
1288             if (ir_tcl_strdup (interp, &p->failback, argv[2]) == TCL_ERROR)
1289                 return TCL_ERROR;
1290         }
1291         else
1292             p->failback = NULL;
1293     }
1294     return TCL_OK;
1295 }
1296
1297 /*
1298  * do_initResponse: add init response handler
1299  */
1300 static int do_initResponse (void *obj, Tcl_Interp *interp,
1301                             int argc, char **argv)
1302 {
1303     IrTcl_Obj *p = obj;
1304
1305     if (argc == 0)
1306     {
1307         p->initResponse = NULL;
1308         return TCL_OK;
1309     }
1310     else if (argc == -1)
1311         return ir_tcl_strdel (interp, &p->initResponse);
1312     if (argc == 3)
1313     {
1314         free (p->initResponse);
1315         if (argv[2][0])
1316         {
1317             if (ir_tcl_strdup (interp, &p->initResponse, argv[2]) == TCL_ERROR)
1318                 return TCL_ERROR;
1319         }
1320         else
1321             p->initResponse = NULL;
1322     }
1323     return TCL_OK;
1324 }
1325 /*
1326  * do_protocol: Set/get protocol method on IR object
1327  */
1328 static int do_protocol (void *o, Tcl_Interp *interp, int argc, char **argv)
1329 {
1330     IrTcl_Obj *p = o;
1331
1332     if (argc <= 0)
1333     {
1334         p->protocol_type = PROTO_Z3950;
1335         return TCL_OK;
1336     }
1337     else if (argc == 3)
1338     {
1339         if (!strcmp (argv[2], "Z39"))
1340             p->protocol_type = PROTO_Z3950;
1341         else if (!strcmp (argv[2], "SR"))
1342             p->protocol_type = PROTO_SR;
1343         else
1344         {
1345             Tcl_AppendResult (interp, "Bad protocol: ", argv[2], NULL);
1346             return TCL_ERROR;
1347         }
1348         return TCL_OK;
1349     }
1350     switch (p->protocol_type)
1351     {
1352     case PROTO_Z3950:
1353         Tcl_AppendElement (interp, "Z39");
1354         break;
1355     case PROTO_SR:
1356         Tcl_AppendElement (interp, "SR");
1357         break;
1358     }
1359     return TCL_OK;
1360 }
1361
1362 /*
1363  * do_triggerResourceControl:
1364  */
1365 static int do_triggerResourceControl (void *obj, Tcl_Interp *interp,
1366                                       int argc, char **argv)
1367 {
1368     IrTcl_Obj *p = obj;
1369     Z_APDU *apdu;
1370     Z_TriggerResourceControlRequest *req;
1371     bool_t is_false = 0;
1372
1373     if (argc <= 0)
1374         return TCL_OK;
1375     if (!p->cs_link)
1376     {
1377         interp->result = "triggerResourceControl: not connected";
1378         return TCL_ERROR;
1379     }
1380     apdu = zget_APDU (p->odr_out, Z_APDU_triggerResourceControlRequest);
1381     req = apdu->u.triggerResourceControlRequest;
1382     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
1383     req->resultSetWanted = &is_false; 
1384     
1385     return ir_tcl_send_APDU (interp, p, apdu, "triggerResourceControl",
1386                              argv[0]);
1387 }
1388
1389 /*
1390  * do_databaseNames: specify database names
1391  */
1392 static int do_databaseNames (void *obj, Tcl_Interp *interp,
1393                              int argc, char **argv)
1394 {
1395     int i;
1396     IrTcl_SetCObj *p = obj;
1397
1398     if (argc == -1)
1399     {
1400         for (i=0; i<p->num_databaseNames; i++)
1401             free (p->databaseNames[i]);
1402         free (p->databaseNames);
1403     }
1404     if (argc <= 0)
1405     {
1406         p->num_databaseNames = 0;
1407         p->databaseNames = NULL;
1408         return TCL_OK;
1409     }
1410     if (argc < 3)
1411     {
1412         for (i=0; i<p->num_databaseNames; i++)
1413             Tcl_AppendElement (interp, p->databaseNames[i]);
1414         return TCL_OK;
1415     }
1416     if (p->databaseNames)
1417     {
1418         for (i=0; i<p->num_databaseNames; i++)
1419             free (p->databaseNames[i]);
1420         free (p->databaseNames);
1421     }
1422     p->num_databaseNames = argc - 2;
1423     p->databaseNames =
1424         ir_tcl_malloc (sizeof(*p->databaseNames) * p->num_databaseNames);
1425     for (i=0; i<p->num_databaseNames; i++)
1426     {
1427         if (ir_tcl_strdup (interp, &p->databaseNames[i], argv[2+i]) 
1428             == TCL_ERROR)
1429             return TCL_ERROR;
1430     }
1431     return TCL_OK;
1432 }
1433
1434 /*
1435  * do_replaceIndicator: Set/get replace Set indicator
1436  */
1437 static int do_replaceIndicator (void *obj, Tcl_Interp *interp,
1438                                 int argc, char **argv)
1439 {
1440     IrTcl_SetCObj *p = obj;
1441
1442     if (argc <= 0)
1443     {
1444         p->replaceIndicator = 1;
1445         return TCL_OK;
1446     }
1447     return get_set_int (&p->replaceIndicator, interp, argc, argv);
1448 }
1449
1450 /*
1451  * do_queryType: Set/Get query method
1452  */
1453 static int do_queryType (void *obj, Tcl_Interp *interp,
1454                        int argc, char **argv)
1455 {
1456     IrTcl_SetCObj *p = obj;
1457
1458     if (argc == 0)
1459         return ir_tcl_strdup (interp, &p->queryType, "rpn");
1460     else if (argc == -1)
1461         return ir_tcl_strdel (interp, &p->queryType);
1462     if (argc == 3)
1463     {
1464         free (p->queryType);
1465         if (ir_tcl_strdup (interp, &p->queryType, argv[2]) == TCL_ERROR)
1466             return TCL_ERROR;
1467     }
1468     Tcl_AppendResult (interp, p->queryType, NULL);
1469     return TCL_OK;
1470 }
1471
1472 /*
1473  * do_userInformationField: Get User information field
1474  */
1475 static int do_userInformationField (void *obj, Tcl_Interp *interp,
1476                                     int argc, char **argv)
1477 {
1478     IrTcl_Obj *p = obj;
1479     
1480     if (argc == 0)
1481     {
1482         p->userInformationField = NULL;
1483         return TCL_OK;
1484     }
1485     else if (argc == -1)
1486         return ir_tcl_strdel (interp, &p->userInformationField);
1487     Tcl_AppendResult (interp, p->userInformationField, NULL);
1488     return TCL_OK;
1489 }
1490
1491 /*
1492  * do_smallSetUpperBound: Set/get small set upper bound
1493  */
1494 static int do_smallSetUpperBound (void *o, Tcl_Interp *interp,
1495                        int argc, char **argv)
1496 {
1497     IrTcl_SetCObj *p = o;
1498
1499     if (argc <= 0)
1500     {
1501         p->smallSetUpperBound = 0;
1502         return TCL_OK;
1503     }
1504     return get_set_int (&p->smallSetUpperBound, interp, argc, argv);
1505 }
1506
1507 /*
1508  * do_largeSetLowerBound: Set/get large set lower bound
1509  */
1510 static int do_largeSetLowerBound (void *o, Tcl_Interp *interp,
1511                                   int argc, char **argv)
1512 {
1513     IrTcl_SetCObj *p = o;
1514
1515     if (argc <= 0)
1516     {
1517         p->largeSetLowerBound = 2;
1518         return TCL_OK;
1519     }
1520     return get_set_int (&p->largeSetLowerBound, interp, argc, argv);
1521 }
1522
1523 /*
1524  * do_mediumSetPresentNumber: Set/get large set lower bound
1525  */
1526 static int do_mediumSetPresentNumber (void *o, Tcl_Interp *interp,
1527                                       int argc, char **argv)
1528 {
1529     IrTcl_SetCObj *p = o;
1530    
1531     if (argc <= 0)
1532     {
1533         p->mediumSetPresentNumber = 0;
1534         return TCL_OK;
1535     }
1536     return get_set_int (&p->mediumSetPresentNumber, interp, argc, argv);
1537 }
1538
1539 /*
1540  * do_referenceId: Set/Get referenceId
1541  */
1542 static int do_referenceId (void *obj, Tcl_Interp *interp,
1543                            int argc, char **argv)
1544 {
1545     IrTcl_SetCObj *p = obj;
1546
1547     if (argc == 0)
1548     {
1549         p->referenceId = NULL;
1550         return TCL_OK;
1551     }
1552     else if (argc == -1)
1553         return ir_tcl_strdel (interp, &p->referenceId);
1554     if (argc == 3)
1555     {
1556         free (p->referenceId);
1557         if (ir_tcl_strdup (interp, &p->referenceId, argv[2]) == TCL_ERROR)
1558             return TCL_ERROR;
1559     }
1560     Tcl_AppendResult (interp, p->referenceId, NULL);
1561     return TCL_OK;
1562 }
1563
1564 /*
1565  * do_preferredRecordSyntax: Set/get preferred record syntax
1566  */
1567 static int do_preferredRecordSyntax (void *obj, Tcl_Interp *interp,
1568                                      int argc, char **argv)
1569 {
1570     IrTcl_SetCObj *p = obj;
1571
1572     if (argc == 0)
1573     {
1574         p->preferredRecordSyntax = NULL;
1575         return TCL_OK;
1576     }
1577     else if (argc == -1)
1578     {
1579         free (p->preferredRecordSyntax);
1580         p->preferredRecordSyntax = NULL;
1581         return TCL_OK;
1582     }
1583     if (argc == 3)
1584     {
1585         free (p->preferredRecordSyntax);
1586         p->preferredRecordSyntax = NULL;
1587         if (argv[2][0] && (p->preferredRecordSyntax = 
1588                            ir_tcl_malloc (sizeof(*p->preferredRecordSyntax))))
1589             *p->preferredRecordSyntax = IrTcl_getRecordSyntaxVal (argv[2]);
1590     }
1591     else if (argc == 2)
1592     {
1593         Tcl_AppendElement (interp, IrTcl_getRecordSyntaxStr
1594                            (*p->preferredRecordSyntax));
1595     }
1596     return TCL_OK;
1597             
1598 }
1599
1600 /*
1601  * do_elementSetNames: Set/Get element Set Names
1602  */
1603 static int do_elementSetNames (void *obj, Tcl_Interp *interp,
1604                                int argc, char **argv)
1605 {
1606     IrTcl_SetCObj *p = obj;
1607
1608     if (argc == 0)
1609     {
1610         p->elementSetNames = NULL;
1611         return TCL_OK;
1612     }
1613     else if (argc == -1)
1614         return ir_tcl_strdel (interp, &p->elementSetNames);
1615     if (argc == 3)
1616     {
1617         free (p->elementSetNames);
1618         if (ir_tcl_strdup (interp, &p->elementSetNames, argv[2]) == TCL_ERROR)
1619             return TCL_ERROR;
1620     }
1621     Tcl_AppendResult (interp, p->elementSetNames, NULL);
1622     return TCL_OK;
1623 }
1624
1625 /*
1626  * do_smallSetElementSetNames: Set/Get small Set Element Set Names
1627  */
1628 static int do_smallSetElementSetNames (void *obj, Tcl_Interp *interp,
1629                                int argc, char **argv)
1630 {
1631     IrTcl_SetCObj *p = obj;
1632
1633     if (argc == 0)
1634     {
1635         p->smallSetElementSetNames = NULL;
1636         return TCL_OK;
1637     }
1638     else if (argc == -1)
1639         return ir_tcl_strdel (interp, &p->smallSetElementSetNames);
1640     if (argc == 3)
1641     {
1642         free (p->smallSetElementSetNames);
1643         if (ir_tcl_strdup (interp, &p->smallSetElementSetNames,
1644                            argv[2]) == TCL_ERROR)
1645             return TCL_ERROR;
1646     }
1647     Tcl_AppendResult (interp, p->smallSetElementSetNames, NULL);
1648     return TCL_OK;
1649 }
1650
1651 /*
1652  * do_mediumSetElementSetNames: Set/Get medium Set Element Set Names
1653  */
1654 static int do_mediumSetElementSetNames (void *obj, Tcl_Interp *interp,
1655                                int argc, char **argv)
1656 {
1657     IrTcl_SetCObj *p = obj;
1658
1659     if (argc == 0)
1660     {
1661         p->mediumSetElementSetNames = NULL;
1662         return TCL_OK;
1663     }
1664     else if (argc == -1)
1665         return ir_tcl_strdel (interp, &p->mediumSetElementSetNames);
1666     if (argc == 3)
1667     {
1668         free (p->mediumSetElementSetNames);
1669         if (ir_tcl_strdup (interp, &p->mediumSetElementSetNames,
1670                            argv[2]) == TCL_ERROR)
1671             return TCL_ERROR;
1672     }
1673     Tcl_AppendResult (interp, p->mediumSetElementSetNames, NULL);
1674     return TCL_OK;
1675 }
1676
1677 static IrTcl_Method ir_method_tab[] = {
1678 { 1, "comstack",                    do_comstack },
1679 { 1, "protocol",                    do_protocol },
1680 { 0, "failback",                    do_failback },
1681 { 0, "failInfo",                    do_failInfo },
1682 { 0, "apduInfo",                    do_apduInfo },
1683 { 0, "logLevel",                    do_logLevel },
1684
1685 { 0, "eventType",                   do_eventType },
1686 { 1, "connect",                     do_connect },
1687 { 0, "protocolVersion",             do_protocolVersion },
1688 { 1, "preferredMessageSize",        do_preferredMessageSize },
1689 { 1, "maximumRecordSize",           do_maximumRecordSize },
1690 { 1, "implementationName",          do_implementationName },
1691 { 1, "implementationId",            do_implementationId },
1692 { 1, "implementationVersion",       do_implementationVersion },
1693 { 0, "targetImplementationName",    do_targetImplementationName },
1694 { 0, "targetImplementationId",      do_targetImplementationId },
1695 { 0, "targetImplementationVersion", do_targetImplementationVersion },
1696 { 0, "userInformationField",        do_userInformationField },
1697 { 1, "idAuthentication",            do_idAuthentication },
1698 { 0, "options",                     do_options },
1699 { 0, "init",                        do_init_request },
1700 { 0, "initResult",                  do_initResult },
1701 { 0, "disconnect",                  do_disconnect },
1702 { 0, "callback",                    do_callback },
1703 { 0, "initResponse",                do_initResponse },
1704 { 0, "triggerResourceControl",      do_triggerResourceControl },
1705 { 0, "initResponse",                do_initResponse },
1706 { 0, NULL, NULL}
1707 };
1708
1709 static IrTcl_Method ir_set_c_method_tab[] = {
1710 { 0, "databaseNames",               do_databaseNames},
1711 { 0, "replaceIndicator",            do_replaceIndicator},
1712 { 0, "queryType",                   do_queryType },
1713 { 0, "preferredRecordSyntax",       do_preferredRecordSyntax },
1714 { 0, "smallSetUpperBound",          do_smallSetUpperBound},
1715 { 0, "largeSetLowerBound",          do_largeSetLowerBound},
1716 { 0, "mediumSetPresentNumber",      do_mediumSetPresentNumber},
1717 { 0, "referenceId",                 do_referenceId },
1718 { 0, "elementSetNames",             do_elementSetNames },
1719 { 0, "smallSetElementSetNames",     do_smallSetElementSetNames },
1720 { 0, "mediumSetElementSetNames",    do_mediumSetElementSetNames },
1721 { 0, NULL, NULL}
1722 };
1723
1724 /* 
1725  * ir_obj_method: IR Object methods
1726  */
1727 static int ir_obj_method (ClientData clientData, Tcl_Interp *interp,
1728                           int argc, char **argv)
1729 {
1730     IrTcl_Methods tab[3];
1731     IrTcl_Obj *p = clientData;
1732
1733     if (argc < 2)
1734         return ir_method_r (clientData, interp, argc, argv, ir_method_tab);
1735
1736     tab[0].tab = ir_method_tab;
1737     tab[0].obj = p;
1738     tab[1].tab = ir_set_c_method_tab;
1739     tab[1].obj = &p->set_inher;
1740     tab[2].tab = NULL;
1741
1742     return ir_method (interp, argc, argv, tab);
1743 }
1744
1745 /* 
1746  * ir_obj_delete: IR Object disposal
1747  */
1748 static void ir_obj_delete (ClientData clientData)
1749 {
1750     IrTcl_Obj *obj = clientData;
1751     IrTcl_Methods tab[3];
1752
1753     --(obj->ref_count);
1754     if (obj->ref_count > 0)
1755         return;
1756     assert (obj->ref_count == 0);
1757
1758     logf (LOG_DEBUG, "ir object delete");
1759     tab[0].tab = ir_method_tab;
1760     tab[0].obj = obj;
1761     tab[1].tab = ir_set_c_method_tab;
1762     tab[1].obj = &obj->set_inher;
1763     tab[2].tab = NULL;
1764
1765     ir_method (NULL, -1, NULL, tab);
1766
1767     ir_tcl_del_q (obj);
1768     odr_destroy (obj->odr_in);
1769     odr_destroy (obj->odr_out);
1770     odr_destroy (obj->odr_pr);
1771     free (obj);
1772 }
1773
1774 /* 
1775  * ir_obj_mk: IR Object creation
1776  */
1777 static int ir_obj_mk (ClientData clientData, Tcl_Interp *interp,
1778                       int argc, char **argv)
1779 {
1780     IrTcl_Methods tab[3];
1781     IrTcl_Obj *obj;
1782 #if CCL2RPN
1783     FILE *inf;
1784 #endif
1785
1786     if (argc != 2)
1787     {
1788         interp->result = "wrong # args";
1789         return TCL_ERROR;
1790     }
1791     obj = ir_tcl_malloc (sizeof(*obj));
1792     obj->ref_count = 1;
1793 #if CCL2RPN
1794     obj->bibset = ccl_qual_mk (); 
1795     if ((inf = fopen ("default.bib", "r")))
1796     {
1797         ccl_qual_file (obj->bibset, inf);
1798         fclose (inf);
1799     }
1800 #endif
1801
1802     logf (LOG_DEBUG, "ir object create %s", argv[1]);
1803     obj->odr_in = odr_createmem (ODR_DECODE);
1804     obj->odr_out = odr_createmem (ODR_ENCODE);
1805     obj->odr_pr = odr_createmem (ODR_PRINT);
1806     obj->state = IR_TCL_R_Idle;
1807     obj->interp = interp;
1808
1809     obj->len_in = 0;
1810     obj->buf_in = NULL;
1811     obj->request_queue = NULL;
1812
1813     tab[0].tab = ir_method_tab;
1814     tab[0].obj = obj;
1815     tab[1].tab = ir_set_c_method_tab;
1816     tab[1].obj = &obj->set_inher;
1817     tab[2].tab = NULL;
1818
1819     if (ir_method (interp, 0, NULL, tab) == TCL_ERROR)
1820         return TCL_ERROR;
1821     Tcl_CreateCommand (interp, argv[1], ir_obj_method,
1822                        (ClientData) obj, ir_obj_delete);
1823     return TCL_OK;
1824 }
1825
1826 /* ------------------------------------------------------- */
1827 /*
1828  * do_search: Do search request
1829  */
1830 static int do_search (void *o, Tcl_Interp *interp, int argc, char **argv)
1831 {
1832     Z_SearchRequest *req;
1833     Z_Query query;
1834     Z_APDU *apdu;
1835     Odr_oct ccl_query;
1836     IrTcl_SetObj *obj = o;
1837     IrTcl_Obj *p;
1838     int r;
1839     oident bib1;
1840
1841     if (argc <= 0)
1842         return TCL_OK;
1843
1844     p = obj->parent;
1845     if (argc != 3)
1846     {
1847         interp->result = "wrong # args";
1848         return TCL_ERROR;
1849     }
1850     if (!obj->set_inher.num_databaseNames)
1851     {
1852         interp->result = "no databaseNames";
1853         return TCL_ERROR;
1854     }
1855     if (!p->cs_link)
1856     {
1857         interp->result = "search: not connected";
1858         return TCL_ERROR;
1859     }
1860     apdu = zget_APDU (p->odr_out, Z_APDU_searchRequest);
1861     req = apdu->u.searchRequest;
1862
1863     obj->start = 1;
1864
1865     bib1.proto = p->protocol_type;
1866     bib1.oclass = CLASS_ATTSET;
1867     bib1.value = VAL_BIB1;
1868
1869     set_referenceId (p->odr_out, &req->referenceId,
1870                      obj->set_inher.referenceId);
1871
1872     req->smallSetUpperBound = &obj->set_inher.smallSetUpperBound;
1873     req->largeSetLowerBound = &obj->set_inher.largeSetLowerBound;
1874     req->mediumSetPresentNumber = &obj->set_inher.mediumSetPresentNumber;
1875     req->replaceIndicator = &obj->set_inher.replaceIndicator;
1876     req->resultSetName = obj->setName ? obj->setName : "Default";
1877     logf (LOG_DEBUG, "Search, resultSetName %s", req->resultSetName);
1878     req->num_databaseNames = obj->set_inher.num_databaseNames;
1879     req->databaseNames = obj->set_inher.databaseNames;
1880     for (r=0; r < obj->set_inher.num_databaseNames; r++)
1881         logf (LOG_DEBUG, " Database %s", obj->set_inher.databaseNames[r]);
1882     if (obj->set_inher.preferredRecordSyntax)
1883     {
1884         struct oident ident;
1885
1886         ident.proto = p->protocol_type;
1887         ident.oclass = CLASS_RECSYN;
1888         ident.value = *obj->set_inher.preferredRecordSyntax;
1889         logf (LOG_DEBUG, "Preferred record syntax is %d", ident.value);
1890         req->preferredRecordSyntax = odr_oiddup (p->odr_out, 
1891                                                  oid_getoidbyent (&ident));
1892     }
1893     else
1894         req->preferredRecordSyntax = 0;
1895
1896     if (obj->set_inher.smallSetElementSetNames &&
1897         *obj->set_inher.smallSetElementSetNames)
1898     {
1899         Z_ElementSetNames *esn = odr_malloc (p->odr_out, sizeof(*esn));
1900         
1901         esn->which = Z_ElementSetNames_generic;
1902         esn->u.generic = obj->set_inher.smallSetElementSetNames;
1903         req->smallSetElementSetNames = esn;
1904     }
1905     else
1906         req->smallSetElementSetNames = NULL; 
1907     
1908     if (obj->set_inher.mediumSetElementSetNames &&
1909         *obj->set_inher.mediumSetElementSetNames)
1910     {
1911         Z_ElementSetNames *esn = odr_malloc (p->odr_out, sizeof(*esn));
1912         
1913         esn->which = Z_ElementSetNames_generic;
1914         esn->u.generic = obj->set_inher.mediumSetElementSetNames;
1915         req->mediumSetElementSetNames = esn;
1916     }
1917     else
1918         req->mediumSetElementSetNames = NULL; 
1919     
1920     req->query = &query;
1921     
1922     if (!strcmp (obj->set_inher.queryType, "rpn"))
1923     {
1924         Z_RPNQuery *RPNquery;
1925
1926         RPNquery = p_query_rpn (p->odr_out, argv[2]);
1927         if (!RPNquery)
1928         {
1929             Tcl_AppendResult (interp, "Syntax error in query", NULL);
1930             return TCL_ERROR;
1931         }
1932         RPNquery->attributeSetId = oid_getoidbyent (&bib1);
1933         query.which = Z_Query_type_1;
1934         query.u.type_1 = RPNquery;
1935         logf (LOG_DEBUG, "RPN");
1936     }
1937 #if CCL2RPN
1938     else if (!strcmp (obj->set_inher.queryType, "cclrpn"))
1939     {
1940         int error;
1941         int pos;
1942         struct ccl_rpn_node *rpn;
1943         Z_RPNQuery *RPNquery;
1944
1945         rpn = ccl_find_str(p->bibset, argv[2], &error, &pos);
1946         if (error)
1947         {
1948             Tcl_AppendResult (interp, "CCL error: ", 
1949                               ccl_err_msg(error), NULL);
1950             return TCL_ERROR;
1951         }
1952         ccl_pr_tree (rpn, stderr);
1953         fprintf (stderr, "\n");
1954         assert((RPNquery = ccl_rpn_query(rpn)));
1955         RPNquery->attributeSetId = oid_getoidbyent (&bib1);
1956         query.which = Z_Query_type_1;
1957         query.u.type_1 = RPNquery;
1958         logf (LOG_DEBUG, "CCLRPN");
1959     }
1960 #endif
1961     else if (!strcmp (obj->set_inher.queryType, "ccl"))
1962     {
1963         query.which = Z_Query_type_2;
1964         query.u.type_2 = &ccl_query;
1965         ccl_query.buf = (unsigned char *) argv[2];
1966         ccl_query.len = strlen (argv[2]);
1967         logf (LOG_DEBUG, "CCL");
1968     }
1969     else
1970     {
1971         interp->result = "unknown query method";
1972         return TCL_ERROR;
1973     }
1974     return ir_tcl_send_APDU (interp, p, apdu, "search", *argv);
1975 }
1976
1977 /*
1978  * do_searchResponse: add search response handler
1979  */
1980 static int do_searchResponse (void *o, Tcl_Interp *interp,
1981                               int argc, char **argv)
1982 {
1983     IrTcl_SetObj *obj = o;
1984
1985     if (argc == 0)
1986     {
1987         obj->searchResponse = NULL;
1988         return TCL_OK;
1989     }
1990     else if (argc == -1)
1991         return ir_tcl_strdel (interp, &obj->searchResponse);
1992     if (argc == 3)
1993     {
1994         free (obj->searchResponse);
1995         if (argv[2][0])
1996         {
1997             if (ir_tcl_strdup (interp, &obj->searchResponse, argv[2])
1998                 == TCL_ERROR)
1999                 return TCL_ERROR;
2000         }
2001         else
2002             obj->searchResponse = NULL;
2003     }
2004     return TCL_OK;
2005 }
2006
2007 /*
2008  * do_presentResponse: add present response handler
2009  */
2010 static int do_presentResponse (void *o, Tcl_Interp *interp,
2011                                int argc, char **argv)
2012 {
2013     IrTcl_SetObj *obj = o;
2014
2015     if (argc == 0)
2016     {
2017         obj->presentResponse = NULL;
2018         return TCL_OK;
2019     }
2020     else if (argc == -1)
2021         return ir_tcl_strdel (interp, &obj->presentResponse);
2022     if (argc == 3)
2023     {
2024         free (obj->presentResponse);
2025         if (argv[2][0])
2026         {
2027             if (ir_tcl_strdup (interp, &obj->presentResponse, argv[2])
2028                 == TCL_ERROR)
2029                 return TCL_ERROR;
2030         }
2031         else
2032             obj->presentResponse = NULL;
2033     }
2034     return TCL_OK;
2035 }
2036
2037 /*
2038  * do_resultCount: Get number of hits
2039  */
2040 static int do_resultCount (void *o, Tcl_Interp *interp,
2041                        int argc, char **argv)
2042 {
2043     IrTcl_SetObj *obj = o;
2044
2045     if (argc <= 0)
2046     {
2047         obj->resultCount = 0;
2048         return TCL_OK;
2049     }
2050     return get_set_int (&obj->resultCount, interp, argc, argv);
2051 }
2052
2053 /*
2054  * do_searchStatus: Get search status (after search response)
2055  */
2056 static int do_searchStatus (void *o, Tcl_Interp *interp,
2057                             int argc, char **argv)
2058 {
2059     IrTcl_SetObj *obj = o;
2060
2061     if (argc <= 0)
2062         return TCL_OK;
2063     return get_set_int (&obj->searchStatus, interp, argc, argv);
2064 }
2065
2066 /*
2067  * do_presentStatus: Get search status (after search/present response)
2068  */
2069 static int do_presentStatus (void *o, Tcl_Interp *interp,
2070                             int argc, char **argv)
2071 {
2072     IrTcl_SetObj *obj = o;
2073
2074     if (argc <= 0)
2075         return TCL_OK;
2076     return get_set_int (&obj->presentStatus, interp, argc, argv);
2077 }
2078
2079 /*
2080  * do_nextResultSetPosition: Get next result set position
2081  *       (after search/present response)
2082  */
2083 static int do_nextResultSetPosition (void *o, Tcl_Interp *interp,
2084                                      int argc, char **argv)
2085 {
2086     IrTcl_SetObj *obj = o;
2087
2088     if (argc <= 0)
2089     {
2090         obj->nextResultSetPosition = 0;
2091         return TCL_OK;
2092     }
2093     return get_set_int (&obj->nextResultSetPosition, interp, argc, argv);
2094 }
2095
2096 /*
2097  * do_setName: Set result Set name
2098  */
2099 static int do_setName (void *o, Tcl_Interp *interp,
2100                        int argc, char **argv)
2101 {
2102     IrTcl_SetObj *obj = o;
2103
2104     if (argc == 0)
2105         return ir_tcl_strdup (interp, &obj->setName, "Default");
2106     else if (argc == -1)
2107         return ir_tcl_strdel (interp, &obj->setName);
2108     if (argc == 3)
2109     {
2110         free (obj->setName);
2111         if (ir_tcl_strdup (interp, &obj->setName, argv[2])
2112             == TCL_ERROR)
2113             return TCL_ERROR;
2114     }
2115     Tcl_AppendElement (interp, obj->setName);
2116     return TCL_OK;
2117 }
2118
2119 /*
2120  * do_numberOfRecordsReturned: Get number of records returned
2121  */
2122 static int do_numberOfRecordsReturned (void *o, Tcl_Interp *interp,
2123                                        int argc, char **argv)
2124 {
2125     IrTcl_SetObj *obj = o;
2126
2127     if (argc <= 0)
2128     {
2129         obj->numberOfRecordsReturned = 0;
2130         return TCL_OK;
2131     }
2132     return get_set_int (&obj->numberOfRecordsReturned, interp, argc, argv);
2133 }
2134
2135 /*
2136  * do_type: Return type (if any) at position.
2137  */
2138 static int do_type (void *o, Tcl_Interp *interp, int argc, char **argv)
2139 {
2140     IrTcl_SetObj *obj = o;
2141     int offset;
2142     IrTcl_RecordList *rl;
2143
2144     if (argc == 0)
2145     {
2146         obj->record_list = NULL;
2147         return TCL_OK;
2148     }
2149     else if (argc == -1)
2150     {
2151         delete_IR_records (obj);
2152         return TCL_OK;
2153     }
2154     if (argc != 3)
2155     {
2156         sprintf (interp->result, "wrong # args");
2157         return TCL_ERROR;
2158     }
2159     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2160         return TCL_ERROR;
2161     rl = find_IR_record (obj, offset);
2162     if (!rl)
2163     {
2164         logf (LOG_DEBUG, "No record at position %d", offset);
2165         return TCL_OK;
2166     }
2167     switch (rl->which)
2168     {
2169     case Z_NamePlusRecord_databaseRecord:
2170         interp->result = "DB";
2171         break;
2172     case Z_NamePlusRecord_surrogateDiagnostic:
2173         interp->result = "SD";
2174         break;
2175     }
2176     return TCL_OK;
2177 }
2178
2179
2180 /*
2181  * do_recordType: Return record type (if any) at position.
2182  */
2183 static int do_recordType (void *o, Tcl_Interp *interp, int argc, char **argv)
2184 {
2185     IrTcl_SetObj *obj = o;
2186     int offset;
2187     IrTcl_RecordList *rl;
2188
2189     if (argc == 0)
2190     {
2191         return TCL_OK;
2192     }
2193     else if (argc == -1)
2194     {
2195         return TCL_OK;
2196     }
2197     if (argc != 3)
2198     {
2199         sprintf (interp->result, "wrong # args");
2200         return TCL_ERROR;
2201     }
2202     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2203         return TCL_ERROR;
2204     rl = find_IR_record (obj, offset);
2205     if (!rl)
2206         return TCL_OK;
2207     if (rl->which != Z_NamePlusRecord_databaseRecord)
2208     {
2209         Tcl_AppendResult (interp, "No DB record at #", argv[2], NULL);
2210         return TCL_ERROR;
2211     }
2212     Tcl_AppendElement (interp, (char*)
2213                        IrTcl_getRecordSyntaxStr (rl->u.dbrec.type));
2214     return TCL_OK;
2215 }
2216
2217 /*
2218  * set record elements (for record extraction)
2219  */
2220 static int do_recordElements (void *o, Tcl_Interp *interp,
2221                               int argc, char **argv)
2222 {
2223     IrTcl_SetObj *obj = o;
2224
2225     if (argc == 0)
2226     {
2227         obj->recordElements = NULL;
2228         return TCL_OK;
2229     }
2230     else if (argc == -1)
2231         return ir_tcl_strdel (NULL, &obj->recordElements);
2232     if (argc > 3)
2233     {
2234         sprintf (interp->result, "wrong # args");
2235         return TCL_ERROR;
2236     }
2237     if (argc == 3)
2238     {
2239         free (obj->recordElements);
2240         return ir_tcl_strdup (NULL, &obj->recordElements, 
2241                               (*argv[2] ? argv[2] : NULL));
2242     }
2243     Tcl_AppendResult (interp, obj->recordElements, NULL);
2244     return TCL_OK;
2245 }
2246
2247 /*
2248  * ir_diagResult 
2249  */
2250 static int ir_diagResult (Tcl_Interp *interp, IrTcl_Diagnostic *list, int num)
2251 {
2252     char buf[20];
2253     int i;
2254     const char *cp;
2255
2256     for (i = 0; i<num; i++)
2257     {
2258         logf (LOG_DEBUG, "Diagnostic, code %d", list[i].condition);
2259         sprintf (buf, "%d", list[i].condition);
2260         Tcl_AppendElement (interp, buf);
2261         cp = diagbib1_str (list[i].condition);
2262         if (cp)
2263             Tcl_AppendElement (interp, (char*) cp);
2264         else
2265             Tcl_AppendElement (interp, "");
2266         if (list[i].addinfo)
2267             Tcl_AppendElement (interp, (char*) list[i].addinfo);
2268         else
2269             Tcl_AppendElement (interp, "");
2270     }
2271     return TCL_OK;
2272 }
2273
2274 /*
2275  * do_diag: Return diagnostic record info
2276  */
2277 static int do_diag (void *o, Tcl_Interp *interp, int argc, char **argv)
2278 {
2279     IrTcl_SetObj *obj = o;
2280     int offset;
2281     IrTcl_RecordList *rl;
2282
2283     if (argc <= 0)
2284         return TCL_OK;
2285     if (argc != 3)
2286     {
2287         sprintf (interp->result, "wrong # args");
2288         return TCL_ERROR;
2289     }
2290     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2291         return TCL_ERROR;
2292     rl = find_IR_record (obj, offset);
2293     if (!rl)
2294     {
2295         Tcl_AppendResult (interp, "No record at #", argv[2], NULL);
2296         return TCL_ERROR;
2297     }
2298     if (rl->which != Z_NamePlusRecord_surrogateDiagnostic)
2299     {
2300         Tcl_AppendResult (interp, "No Diagnostic record at #", argv[2], NULL);
2301         return TCL_ERROR;
2302     }
2303     return ir_diagResult (interp, rl->u.surrogateDiagnostics.list,
2304                           rl->u.surrogateDiagnostics.num);
2305 }
2306
2307 /*
2308  * do_getMarc: Get ISO2709 Record lines/fields
2309  */
2310 static int do_getMarc (void *o, Tcl_Interp *interp, int argc, char **argv)
2311 {
2312     IrTcl_SetObj *obj = o;
2313     int offset;
2314     IrTcl_RecordList *rl;
2315
2316     if (argc <= 0)
2317         return TCL_OK;
2318     if (argc < 7)
2319     {
2320         sprintf (interp->result, "wrong # args");
2321         return TCL_ERROR;
2322     }
2323     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2324         return TCL_ERROR;
2325     rl = find_IR_record (obj, offset);
2326     if (!rl)
2327     {
2328         Tcl_AppendResult (interp, "No record at #", argv[2], NULL);
2329         return TCL_ERROR;
2330     }
2331     if (rl->which != Z_NamePlusRecord_databaseRecord)
2332     {
2333         Tcl_AppendResult (interp, "No DB record at #", argv[2], NULL);
2334         return TCL_ERROR;
2335     }
2336     return ir_tcl_get_marc (interp, rl->u.dbrec.buf, argc, argv);
2337 }
2338
2339 /*
2340  * do_getSutrs: Get SUTRS Record
2341  */
2342 static int do_getSutrs (void *o, Tcl_Interp *interp, int argc, char **argv)
2343 {
2344     IrTcl_SetObj *obj = o;
2345     int offset;
2346     IrTcl_RecordList *rl;
2347
2348     if (argc <= 0)
2349         return TCL_OK;
2350     if (argc != 3)
2351     {
2352         sprintf (interp->result, "wrong # args");
2353         return TCL_ERROR;
2354     }
2355     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2356         return TCL_ERROR;
2357     rl = find_IR_record (obj, offset);
2358     if (!rl)
2359     {
2360         Tcl_AppendResult (interp, "No record at #", argv[2], NULL);
2361         return TCL_ERROR;
2362     }
2363     if (rl->which != Z_NamePlusRecord_databaseRecord)
2364     {
2365         Tcl_AppendResult (interp, "No DB record at #", argv[2], NULL);
2366         return TCL_ERROR;
2367     }
2368     if (rl->u.dbrec.type != VAL_SUTRS)
2369         return TCL_OK;
2370     Tcl_AppendElement (interp, rl->u.dbrec.buf);
2371     return TCL_OK;
2372 }
2373
2374
2375 /*
2376  * do_getGrs: Get a GRS1 Record
2377  */
2378 static int do_getGrs (void *o, Tcl_Interp *interp, int argc, char **argv)
2379 {
2380     IrTcl_SetObj *obj = o;
2381     int offset;
2382     IrTcl_RecordList *rl;
2383
2384     if (argc <= 0)
2385         return TCL_OK;
2386     if (argc < 3)
2387     {
2388         sprintf (interp->result, "wrong # args");
2389         return TCL_ERROR;
2390     }
2391     if (Tcl_GetInt (interp, argv[2], &offset)==TCL_ERROR)
2392         return TCL_ERROR;
2393     rl = find_IR_record (obj, offset);
2394     if (!rl)
2395     {
2396         Tcl_AppendResult (interp, "No record at #", argv[2], NULL);
2397         return TCL_ERROR;
2398     }
2399     if (rl->which != Z_NamePlusRecord_databaseRecord)
2400     {
2401         Tcl_AppendResult (interp, "No DB record at #", argv[2], NULL);
2402         return TCL_ERROR;
2403     }
2404     if (rl->u.dbrec.type != VAL_GRS1)
2405         return TCL_OK;
2406     return ir_tcl_get_grs (interp, rl->u.dbrec.u.grs1, argc, argv);
2407 }
2408
2409
2410 /*
2411  * do_responseStatus: Return response status (present or search)
2412  */
2413 static int do_responseStatus (void *o, Tcl_Interp *interp, 
2414                              int argc, char **argv)
2415 {
2416     IrTcl_SetObj *obj = o;
2417
2418     if (argc == 0)
2419     {
2420         obj->recordFlag = 0;
2421         obj->nonSurrogateDiagnosticNum = 0;
2422         obj->nonSurrogateDiagnosticList = NULL;
2423         return TCL_OK;
2424     }
2425     else if (argc == -1)
2426     {
2427         ir_deleteDiags (&obj->nonSurrogateDiagnosticList,
2428                         &obj->nonSurrogateDiagnosticNum);
2429         return TCL_OK;
2430     }
2431     if (!obj->recordFlag)
2432     {
2433         Tcl_AppendElement (interp, "OK");
2434         return TCL_OK;
2435     }
2436     switch (obj->which)
2437     {
2438     case Z_Records_DBOSD:
2439         Tcl_AppendElement (interp, "DBOSD");
2440         break;
2441     case Z_Records_NSD:
2442         Tcl_AppendElement (interp, "NSD");
2443         return ir_diagResult (interp, obj->nonSurrogateDiagnosticList,
2444                               obj->nonSurrogateDiagnosticNum);
2445     }
2446     return TCL_OK;
2447 }
2448
2449 /*
2450  * do_present: Perform Present Request
2451  */
2452
2453 static int do_present (void *o, Tcl_Interp *interp, int argc, char **argv)
2454 {
2455     IrTcl_SetObj *obj = o;
2456     IrTcl_Obj *p;
2457     Z_APDU *apdu;
2458     Z_PresentRequest *req;
2459     int start;
2460     int number;
2461
2462     if (argc <= 0)
2463         return TCL_OK;
2464     if (argc >= 3)
2465     {
2466         if (Tcl_GetInt (interp, argv[2], &start) == TCL_ERROR)
2467             return TCL_ERROR;
2468     }
2469     else
2470         start = 1;
2471     if (argc >= 4)
2472     {
2473         if (Tcl_GetInt (interp, argv[3], &number) == TCL_ERROR)
2474             return TCL_ERROR;
2475     }
2476     else 
2477         number = 10;
2478     p = obj->parent;
2479     if (!p->cs_link)
2480     {
2481         interp->result = "present: not connected";
2482         return TCL_ERROR;
2483     }
2484
2485     obj->start = start;
2486     obj->number = number;
2487
2488     apdu = zget_APDU (p->odr_out, Z_APDU_presentRequest);
2489     req = apdu->u.presentRequest;
2490
2491     set_referenceId (p->odr_out, &req->referenceId,
2492                      obj->set_inher.referenceId);
2493
2494     req->resultSetId = obj->setName ? obj->setName : "Default";
2495     
2496     req->resultSetStartPoint = &start;
2497     req->numberOfRecordsRequested = &number;
2498     if (obj->set_inher.preferredRecordSyntax)
2499     {
2500         struct oident ident;
2501
2502         ident.proto = p->protocol_type;
2503         ident.oclass = CLASS_RECSYN;
2504         ident.value = *obj->set_inher.preferredRecordSyntax;
2505         logf (LOG_DEBUG, "Preferred record syntax is %d", ident.value);
2506         req->preferredRecordSyntax = odr_oiddup (p->odr_out, 
2507                                                  oid_getoidbyent (&ident));
2508     }
2509     else
2510         req->preferredRecordSyntax = 0;
2511
2512     if (obj->set_inher.elementSetNames && *obj->set_inher.elementSetNames)
2513     {
2514         Z_ElementSetNames *esn = odr_malloc (p->odr_out, sizeof(*esn));
2515         Z_RecordComposition *compo = odr_malloc (p->odr_out, sizeof(*compo));
2516
2517         esn->which = Z_ElementSetNames_generic;
2518         esn->u.generic = obj->set_inher.elementSetNames;
2519
2520         req->recordComposition = compo;
2521         compo->which = Z_RecordComp_simple;
2522         compo->u.simple = esn;
2523     }
2524     else
2525         req->recordComposition = NULL;
2526     return ir_tcl_send_APDU (interp, p, apdu, "present", *argv);
2527 }
2528
2529 /*
2530  * do_loadFile: Load result set from file
2531  */
2532
2533 static int do_loadFile (void *o, Tcl_Interp *interp,
2534                         int argc, char **argv)
2535 {
2536     IrTcl_SetObj *setobj = o;
2537     FILE *inf;
2538     size_t size;
2539     int  no = 1;
2540     char *buf;
2541
2542     if (argc <= 0)
2543         return TCL_OK;
2544     if (argc != 3)
2545     {
2546         interp->result = "wrong # args";
2547         return TCL_ERROR;
2548     }
2549     inf = fopen (argv[2], "r");
2550     if (!inf)
2551     {
2552         Tcl_AppendResult (interp, "Cannot open ", argv[2], NULL);
2553         return TCL_ERROR;
2554     }
2555     while ((buf = ir_tcl_fread_marc (inf, &size)))
2556     {
2557         IrTcl_RecordList *rl;
2558
2559         rl = new_IR_record (setobj, no, Z_NamePlusRecord_databaseRecord, "F");
2560         rl->u.dbrec.type = VAL_USMARC;
2561         rl->u.dbrec.buf = buf;
2562         rl->u.dbrec.size = size;
2563         no++;
2564     }
2565     setobj->numberOfRecordsReturned = no-1;
2566     fclose (inf);
2567     return TCL_OK;
2568 }
2569
2570 static IrTcl_Method ir_set_method_tab[] = {
2571     { 0, "search",                  do_search },
2572     { 0, "searchResponse",          do_searchResponse },
2573     { 0, "presentResponse",         do_presentResponse },
2574     { 0, "searchStatus",            do_searchStatus },
2575     { 0, "presentStatus",           do_presentStatus },
2576     { 0, "nextResultSetPosition",   do_nextResultSetPosition },
2577     { 0, "setName",                 do_setName },
2578     { 0, "resultCount",             do_resultCount },
2579     { 0, "numberOfRecordsReturned", do_numberOfRecordsReturned },
2580     { 0, "present",                 do_present },
2581     { 0, "type",                    do_type },
2582     { 0, "getMarc",                 do_getMarc },
2583     { 0, "getSutrs",                do_getSutrs },
2584     { 0, "getGrs",                  do_getGrs },
2585     { 0, "recordType",              do_recordType },
2586     { 0, "recordElements",          do_recordElements },
2587     { 0, "diag",                    do_diag },
2588     { 0, "responseStatus",          do_responseStatus },
2589     { 0, "loadFile",                do_loadFile },
2590     { 0, NULL, NULL}
2591 };
2592
2593 /* 
2594  * ir_set_obj_method: IR Set Object methods
2595  */
2596 static int ir_set_obj_method (ClientData clientData, Tcl_Interp *interp,
2597                           int argc, char **argv)
2598 {
2599     IrTcl_Methods tabs[3];
2600     IrTcl_SetObj *p = clientData;
2601
2602     if (argc < 2)
2603     {
2604         interp->result = "wrong # args";
2605         return TCL_ERROR;
2606     }
2607     tabs[0].tab = ir_set_method_tab;
2608     tabs[0].obj = p;
2609     tabs[1].tab = ir_set_c_method_tab;
2610     tabs[1].obj = &p->set_inher;
2611     tabs[2].tab = NULL;
2612
2613     return ir_method (interp, argc, argv, tabs);
2614 }
2615
2616 /* 
2617  * ir_set_obj_delete: IR Set Object disposal
2618  */
2619 static void ir_set_obj_delete (ClientData clientData)
2620 {
2621     IrTcl_Methods tabs[3];
2622     IrTcl_SetObj *p = clientData;
2623
2624     logf (LOG_DEBUG, "ir set delete");
2625
2626     tabs[0].tab = ir_set_method_tab;
2627     tabs[0].obj = p;
2628     tabs[1].tab = ir_set_c_method_tab;
2629     tabs[1].obj = &p->set_inher;
2630     tabs[2].tab = NULL;
2631
2632     ir_method (NULL, -1, NULL, tabs);
2633
2634     free (p);
2635 }
2636
2637 /*
2638  * ir_set_obj_mk: IR Set Object creation
2639  */
2640 static int ir_set_obj_mk (ClientData clientData, Tcl_Interp *interp,
2641                           int argc, char **argv)
2642 {
2643     IrTcl_Methods tabs[3];
2644     IrTcl_SetObj *obj;
2645
2646     if (argc < 2 || argc > 3)
2647     {
2648         interp->result = "wrong # args";
2649         return TCL_ERROR;
2650     }
2651     obj = ir_tcl_malloc (sizeof(*obj));
2652     logf (LOG_DEBUG, "ir set create");
2653     if (argc == 3)
2654     {
2655         Tcl_CmdInfo parent_info;
2656         int i;
2657         IrTcl_SetCObj *dst;
2658         IrTcl_SetCObj *src;
2659
2660         if (!Tcl_GetCommandInfo (interp, argv[2], &parent_info))
2661         {
2662             interp->result = "No parent";
2663             return TCL_ERROR;
2664         }
2665         obj->parent = (IrTcl_Obj *) parent_info.clientData;
2666
2667         dst = &obj->set_inher;
2668         src = &obj->parent->set_inher;
2669
2670         if ((dst->num_databaseNames = src->num_databaseNames))
2671             dst->databaseNames =
2672                 ir_tcl_malloc (sizeof (*dst->databaseNames)
2673                                * dst->num_databaseNames);
2674         else
2675             dst->databaseNames = NULL;
2676         for (i = 0; i < dst->num_databaseNames; i++)
2677             if (ir_tcl_strdup (interp, &dst->databaseNames[i],
2678                            src->databaseNames[i]) == TCL_ERROR)
2679                 return TCL_ERROR;
2680         if (ir_tcl_strdup (interp, &dst->queryType, src->queryType)
2681             == TCL_ERROR)
2682             return TCL_ERROR;
2683
2684         if (ir_tcl_strdup (interp, &dst->referenceId, src->referenceId)
2685             == TCL_ERROR)
2686             return TCL_ERROR;
2687
2688         if (ir_tcl_strdup (interp, &dst->elementSetNames, src->elementSetNames)
2689             == TCL_ERROR)
2690             return TCL_ERROR;
2691
2692         if (ir_tcl_strdup (interp, &dst->smallSetElementSetNames,
2693                            src->smallSetElementSetNames)
2694             == TCL_ERROR)
2695             return TCL_ERROR;
2696
2697         if (ir_tcl_strdup (interp, &dst->mediumSetElementSetNames,
2698                            src->mediumSetElementSetNames)
2699             == TCL_ERROR)
2700             return TCL_ERROR;
2701
2702         if (src->preferredRecordSyntax && 
2703             (dst->preferredRecordSyntax 
2704              = ir_tcl_malloc (sizeof(*dst->preferredRecordSyntax))))
2705             *dst->preferredRecordSyntax = *src->preferredRecordSyntax;
2706         else
2707             dst->preferredRecordSyntax = NULL;
2708         dst->replaceIndicator = src->replaceIndicator;
2709         dst->smallSetUpperBound = src->smallSetUpperBound;
2710         dst->largeSetLowerBound = src->largeSetLowerBound;
2711         dst->mediumSetPresentNumber = src->mediumSetPresentNumber;
2712     }   
2713     else
2714         obj->parent = NULL;
2715
2716     tabs[0].tab = ir_set_method_tab;
2717     tabs[0].obj = obj;
2718     tabs[1].tab = NULL;
2719
2720     if (ir_method (interp, 0, NULL, tabs) == TCL_ERROR)
2721         return TCL_ERROR;
2722
2723     Tcl_CreateCommand (interp, argv[1], ir_set_obj_method,
2724                        (ClientData) obj, ir_set_obj_delete);
2725     return TCL_OK;
2726 }
2727
2728 /* ------------------------------------------------------- */
2729
2730 /*
2731  * do_scan: Perform scan 
2732  */
2733 static int do_scan (void *o, Tcl_Interp *interp, int argc, char **argv)
2734 {
2735     Z_ScanRequest *req;
2736     Z_APDU *apdu;
2737     IrTcl_ScanObj *obj = o;
2738     IrTcl_Obj *p = obj->parent;
2739     oident bib1;
2740 #if CCL2RPN
2741     struct ccl_rpn_node *rpn;
2742     int pos;
2743 #endif
2744
2745     if (argc <= 0)
2746         return TCL_OK;
2747     if (argc != 3)
2748     {
2749         interp->result = "wrong # args";
2750         return TCL_ERROR;
2751     }
2752     if (!p->set_inher.num_databaseNames)
2753     {
2754         interp->result = "no databaseNames";
2755         return TCL_ERROR;
2756     }
2757     if (!p->cs_link)
2758     {
2759         interp->result = "scan: not connected";
2760         return TCL_ERROR;
2761     }
2762
2763     bib1.proto = p->protocol_type;
2764     bib1.oclass = CLASS_ATTSET;
2765     bib1.value = VAL_BIB1;
2766
2767     apdu = zget_APDU (p->odr_out, Z_APDU_scanRequest);
2768     req = apdu->u.scanRequest;
2769
2770     set_referenceId (p->odr_out, &req->referenceId, p->set_inher.referenceId);
2771     req->num_databaseNames = p->set_inher.num_databaseNames;
2772     req->databaseNames = p->set_inher.databaseNames;
2773     req->attributeSet = oid_getoidbyent (&bib1);
2774
2775 #if !CCL2RPN
2776     if (!(req->termListAndStartPoint = p_query_scan (p->odr_out, argv[2])))
2777     {
2778         Tcl_AppendResult (interp, "Syntax error in query", NULL);
2779         return TCL_ERROR;
2780     }
2781 #else
2782     rpn = ccl_find_str(p->bibset, argv[2], &r, &pos);
2783     if (r)
2784     {
2785         Tcl_AppendResult (interp, "CCL error: ", ccl_err_msg (r), NULL);
2786         return TCL_ERROR;
2787     }
2788     ccl_pr_tree (rpn, stderr);
2789     fprintf (stderr, "\n");
2790     if (!(req->termListAndStartPoint = ccl_scan_query (rpn)))
2791         return TCL_ERROR;
2792 #endif
2793     req->stepSize = &obj->stepSize;
2794     req->numberOfTermsRequested = &obj->numberOfTermsRequested;
2795     req->preferredPositionInResponse = &obj->preferredPositionInResponse;
2796     logf (LOG_DEBUG, "stepSize=%d", *req->stepSize);
2797     logf (LOG_DEBUG, "numberOfTermsRequested=%d",
2798           *req->numberOfTermsRequested);
2799     logf (LOG_DEBUG, "preferredPositionInResponse=%d",
2800           *req->preferredPositionInResponse);
2801     
2802     return ir_tcl_send_APDU (interp, p, apdu, "scan", *argv);
2803 }
2804
2805 /*
2806  * do_scanResponse: add scan response handler
2807  */
2808 static int do_scanResponse (void *o, Tcl_Interp *interp,
2809                             int argc, char **argv)
2810 {
2811     IrTcl_ScanObj *obj = o;
2812
2813     if (argc == 0)
2814     {
2815         obj->scanResponse = NULL;
2816         return TCL_OK;
2817     }
2818     else if (argc == -1)
2819         return ir_tcl_strdel (interp, &obj->scanResponse);
2820     if (argc == 3)
2821     {
2822         free (obj->scanResponse);
2823         if (argv[2][0])
2824         {
2825             if (ir_tcl_strdup (interp, &obj->scanResponse, argv[2])
2826                 == TCL_ERROR)
2827                 return TCL_ERROR;
2828         }
2829         else
2830             obj->scanResponse = NULL;
2831     }
2832     return TCL_OK;
2833 }
2834
2835 /*
2836  * do_stepSize: Set/get replace Step Size
2837  */
2838 static int do_stepSize (void *obj, Tcl_Interp *interp,
2839                         int argc, char **argv)
2840 {
2841     IrTcl_ScanObj *p = obj;
2842     if (argc <= 0)
2843     {
2844         p->stepSize = 0;
2845         return TCL_OK;
2846     }
2847     return get_set_int (&p->stepSize, interp, argc, argv);
2848 }
2849
2850 /*
2851  * do_numberOfTermsRequested: Set/get Number of Terms requested
2852  */
2853 static int do_numberOfTermsRequested (void *obj, Tcl_Interp *interp,
2854                                       int argc, char **argv)
2855 {
2856     IrTcl_ScanObj *p = obj;
2857
2858     if (argc <= 0)
2859     {
2860         p->numberOfTermsRequested = 20;
2861         return TCL_OK;
2862     }
2863     return get_set_int (&p->numberOfTermsRequested, interp, argc, argv);
2864 }
2865
2866
2867 /*
2868  * do_preferredPositionInResponse: Set/get preferred Position
2869  */
2870 static int do_preferredPositionInResponse (void *obj, Tcl_Interp *interp,
2871                                            int argc, char **argv)
2872 {
2873     IrTcl_ScanObj *p = obj;
2874
2875     if (argc <= 0)
2876     {
2877         p->preferredPositionInResponse = 1;
2878         return TCL_OK;
2879     }
2880     return get_set_int (&p->preferredPositionInResponse, interp, argc, argv);
2881 }
2882
2883 /*
2884  * do_scanStatus: Get scan status
2885  */
2886 static int do_scanStatus (void *obj, Tcl_Interp *interp,
2887                           int argc, char **argv)
2888 {
2889     IrTcl_ScanObj *p = obj;
2890
2891     if (argc <= 0)
2892         return TCL_OK;
2893     return get_set_int (&p->scanStatus, interp, argc, argv);
2894 }
2895
2896 /*
2897  * do_numberOfEntriesReturned: Get number of Entries returned
2898  */
2899 static int do_numberOfEntriesReturned (void *obj, Tcl_Interp *interp,
2900                                        int argc, char **argv)
2901 {
2902     IrTcl_ScanObj *p = obj;
2903
2904     if (argc <= 0)
2905         return TCL_OK;
2906     return get_set_int (&p->numberOfEntriesReturned, interp, argc, argv);
2907 }
2908
2909 /*
2910  * do_positionOfTerm: Get position of Term
2911  */
2912 static int do_positionOfTerm (void *obj, Tcl_Interp *interp,
2913                               int argc, char **argv)
2914 {
2915     IrTcl_ScanObj *p = obj;
2916
2917     if (argc <= 0)
2918         return TCL_OK;
2919     return get_set_int (&p->positionOfTerm, interp, argc, argv);
2920 }
2921
2922 /*
2923  * do_scanLine: get Scan Line (surrogate or normal) after response
2924  */
2925 static int do_scanLine (void *obj, Tcl_Interp *interp, int argc, char **argv)
2926 {
2927     IrTcl_ScanObj *p = obj;
2928     int i;
2929     char numstr[20];
2930
2931     if (argc == 0)
2932     {
2933         p->entries_flag = 0;
2934         p->entries = NULL;
2935         p->nonSurrogateDiagnosticNum = 0;
2936         p->nonSurrogateDiagnosticList = 0;
2937         return TCL_OK;
2938     }
2939     else if (argc == -1)
2940     {
2941         p->entries_flag = 0;
2942         /* release entries */
2943         p->entries = NULL;
2944
2945         ir_deleteDiags (&p->nonSurrogateDiagnosticList, 
2946                         &p->nonSurrogateDiagnosticNum);
2947         return TCL_OK;
2948     }
2949     if (argc != 3)
2950     {
2951         interp->result = "wrong # args";
2952         return TCL_ERROR;
2953     }
2954     if (Tcl_GetInt (interp, argv[2], &i) == TCL_ERROR)
2955         return TCL_ERROR;
2956     if (!p->entries_flag || p->which != Z_ListEntries_entries || !p->entries
2957         || i >= p->num_entries || i < 0)
2958         return TCL_OK;
2959     switch (p->entries[i].which)
2960     {
2961     case Z_Entry_termInfo:
2962         Tcl_AppendElement (interp, "T");
2963         if (p->entries[i].u.term.buf)
2964             Tcl_AppendElement (interp, p->entries[i].u.term.buf);
2965         else
2966             Tcl_AppendElement (interp, "");
2967         sprintf (numstr, "%d", p->entries[i].u.term.globalOccurrences);
2968         Tcl_AppendElement (interp, numstr);
2969         break;
2970     case Z_Entry_surrogateDiagnostic:
2971         Tcl_AppendElement (interp, "SD");
2972         return ir_diagResult (interp, p->entries[i].u.diag.list,
2973                               p->entries[i].u.diag.num);
2974         break;
2975     }
2976     return TCL_OK;
2977 }
2978
2979 static IrTcl_Method ir_scan_method_tab[] = {
2980     { 0, "scan",                    do_scan },
2981     { 0, "scanResponse",            do_scanResponse },
2982     { 0, "stepSize",                do_stepSize },
2983     { 0, "numberOfTermsRequested",  do_numberOfTermsRequested },
2984     { 0, "preferredPositionInResponse", do_preferredPositionInResponse },
2985     { 0, "scanStatus",              do_scanStatus },
2986     { 0, "numberOfEntriesReturned", do_numberOfEntriesReturned },
2987     { 0, "positionOfTerm",          do_positionOfTerm },
2988     { 0, "scanLine",                do_scanLine },
2989     { 0, NULL, NULL}
2990 };
2991
2992 /* 
2993  * ir_scan_obj_method: IR Scan Object methods
2994  */
2995 static int ir_scan_obj_method (ClientData clientData, Tcl_Interp *interp,
2996                                int argc, char **argv)
2997 {
2998     IrTcl_Methods tabs[2];
2999
3000     if (argc < 2)
3001     {
3002         interp->result = "wrong # args";
3003         return TCL_ERROR;
3004     }
3005     tabs[0].tab = ir_scan_method_tab;
3006     tabs[0].obj = clientData;
3007     tabs[1].tab = NULL;
3008
3009     return ir_method (interp, argc, argv, tabs);
3010 }
3011
3012 /* 
3013  * ir_scan_obj_delete: IR Scan Object disposal
3014  */
3015 static void ir_scan_obj_delete (ClientData clientData)
3016 {
3017     IrTcl_Methods tabs[2];
3018     IrTcl_ScanObj *obj = clientData;
3019
3020     tabs[0].tab = ir_scan_method_tab;
3021     tabs[0].obj = obj;
3022     tabs[1].tab = NULL;
3023
3024     ir_method (NULL, -1, NULL, tabs);
3025     free (obj);
3026 }
3027
3028 /* 
3029  * ir_scan_obj_mk: IR Scan Object creation
3030  */
3031 static int ir_scan_obj_mk (ClientData clientData, Tcl_Interp *interp,
3032                            int argc, char **argv)
3033 {
3034     Tcl_CmdInfo parent_info;
3035     IrTcl_ScanObj *obj;
3036     IrTcl_Methods tabs[2];
3037
3038     if (argc != 3)
3039     {
3040         interp->result = "wrong # args";
3041         return TCL_ERROR;
3042     }
3043     if (!Tcl_GetCommandInfo (interp, argv[2], &parent_info))
3044     {
3045         interp->result = "No parent";
3046         return TCL_ERROR;
3047     }
3048     obj = ir_tcl_malloc (sizeof(*obj));
3049     obj->parent = (IrTcl_Obj *) parent_info.clientData;
3050
3051     tabs[0].tab = ir_scan_method_tab;
3052     tabs[0].obj = obj;
3053     tabs[1].tab = NULL;
3054
3055     if (ir_method (interp, 0, NULL, tabs) == TCL_ERROR)
3056         return TCL_ERROR;
3057     Tcl_CreateCommand (interp, argv[1], ir_scan_obj_method,
3058                        (ClientData) obj, ir_scan_obj_delete);
3059     return TCL_OK;
3060 }
3061
3062 /* ------------------------------------------------------- */
3063
3064 static void ir_initResponse (void *obj, Z_InitResponse *initrs)
3065 {
3066     IrTcl_Obj *p = obj;
3067
3068     p->initResult = *initrs->result ? 1 : 0;
3069     if (!*initrs->result)
3070         logf (LOG_DEBUG, "Connection rejected by target");
3071     else
3072         logf (LOG_DEBUG, "Connection accepted by target");
3073
3074     get_referenceId (&p->set_inher.referenceId, initrs->referenceId);
3075
3076     free (p->targetImplementationId);
3077     ir_tcl_strdup (p->interp, &p->targetImplementationId,
3078                initrs->implementationId);
3079     free (p->targetImplementationName);
3080     ir_tcl_strdup (p->interp, &p->targetImplementationName,
3081                initrs->implementationName);
3082     free (p->targetImplementationVersion);
3083     ir_tcl_strdup (p->interp, &p->targetImplementationVersion,
3084                initrs->implementationVersion);
3085
3086     p->maximumRecordSize = *initrs->maximumRecordSize;
3087     p->preferredMessageSize = *initrs->preferredMessageSize;
3088     
3089     memcpy (&p->options, initrs->options, sizeof(initrs->options));
3090     memcpy (&p->protocolVersion, initrs->protocolVersion,
3091             sizeof(initrs->protocolVersion));
3092     free (p->userInformationField);
3093     p->userInformationField = NULL;
3094     if (initrs->userInformationField)
3095     {
3096         int len;
3097
3098         if (initrs->userInformationField->which == ODR_EXTERNAL_octet && 
3099             (p->userInformationField =
3100              ir_tcl_malloc ((len = 
3101                              initrs->userInformationField->
3102                              u.octet_aligned->len) +1)))
3103         {
3104             memcpy (p->userInformationField,
3105                     initrs->userInformationField->u.octet_aligned->buf,
3106                         len);
3107             (p->userInformationField)[len] = '\0';
3108         }
3109     }
3110 }
3111
3112 static void ir_deleteDiags (IrTcl_Diagnostic **dst_list, int *dst_num)
3113 {
3114     int i;
3115     for (i = 0; i<*dst_num; i++)
3116         free (dst_list[i]->addinfo);
3117     free (*dst_list);
3118     *dst_list = NULL;
3119     *dst_num = 0;
3120 }
3121
3122 static void ir_handleDiags (IrTcl_Diagnostic **dst_list, int *dst_num,
3123                     Z_DiagRec **list, int num)
3124 {
3125     int i;
3126     char *addinfo;
3127
3128     *dst_num = num;
3129     *dst_list = ir_tcl_malloc (sizeof(**dst_list) * num);
3130     for (i = 0; i<num; i++)
3131     {
3132         switch (list[i]->which)
3133         {
3134         case Z_DiagRec_defaultFormat:
3135             (*dst_list)[i].condition = *list[i]->u.defaultFormat->condition;
3136             addinfo = list[i]->u.defaultFormat->addinfo;
3137             if (addinfo && 
3138                 ((*dst_list)[i].addinfo = ir_tcl_malloc (strlen(addinfo)+1)))
3139                 strcpy ((*dst_list)[i].addinfo, addinfo);
3140             break;
3141         default:
3142             (*dst_list)[i].addinfo = NULL;
3143             (*dst_list)[i].condition = 0;
3144         }
3145     }
3146 }
3147
3148 static void ir_handleRecords (void *o, Z_Records *zrs, IrTcl_SetObj *setobj,
3149                               const char *elements)
3150 {
3151     IrTcl_Obj *p = o;
3152
3153     int offset;
3154     IrTcl_RecordList *rl;
3155
3156     setobj->which = zrs->which;
3157     setobj->recordFlag = 1;
3158     
3159     ir_deleteDiags (&setobj->nonSurrogateDiagnosticList,
3160                     &setobj->nonSurrogateDiagnosticNum);
3161     if (zrs->which == Z_Records_DBOSD)
3162     {
3163         setobj->numberOfRecordsReturned = 
3164             zrs->u.databaseOrSurDiagnostics->num_records;
3165         logf (LOG_DEBUG, "Got %d records", setobj->numberOfRecordsReturned);
3166         for (offset = 0; offset<setobj->numberOfRecordsReturned; offset++)
3167         {
3168             rl = new_IR_record (setobj, setobj->start + offset,
3169                                 zrs->u.databaseOrSurDiagnostics->
3170                                 records[offset]->which,
3171                                 elements);
3172             if (rl->which == Z_NamePlusRecord_surrogateDiagnostic)
3173             {
3174                 ir_handleDiags (&rl->u.surrogateDiagnostics.list,
3175                                 &rl->u.surrogateDiagnostics.num,
3176                                 &zrs->u.databaseOrSurDiagnostics->
3177                                 records[offset]->u.surrogateDiagnostic,
3178                                 1);
3179             } 
3180             else
3181             {
3182                 Z_DatabaseRecord *zr; 
3183                 Z_External *oe;
3184                 struct oident *ident;
3185                 
3186                 zr = zrs->u.databaseOrSurDiagnostics->records[offset]
3187                     ->u.databaseRecord;
3188                 oe = (Z_External*) zr;
3189                 rl->u.dbrec.size = zr->u.octet_aligned->len;
3190
3191                 if ((ident = oid_getentbyoid (oe->direct_reference)))
3192                     rl->u.dbrec.type = ident->value;
3193                 else
3194                     rl->u.dbrec.type = VAL_USMARC;
3195
3196                 if (oe->which == ODR_EXTERNAL_octet && rl->u.dbrec.size > 0)
3197                 {
3198                     char *buf = (char*) zr->u.octet_aligned->buf;
3199                     if ((rl->u.dbrec.buf = ir_tcl_malloc (rl->u.dbrec.size)))
3200                         memcpy (rl->u.dbrec.buf, buf, rl->u.dbrec.size);
3201                 }
3202                 else if (rl->u.dbrec.type == VAL_SUTRS && 
3203                          oe->which == Z_External_sutrs)
3204                 {
3205                     odr_setbuf (p->odr_in, (char*) oe->u.single_ASN1_type->buf,
3206                                 oe->u.single_ASN1_type->len, 0);
3207                     if ((rl->u.dbrec.buf = ir_tcl_malloc (oe->u.sutrs->len+1)))
3208                     {
3209                         memcpy (rl->u.dbrec.buf, oe->u.sutrs->buf,
3210                                 oe->u.sutrs->len);
3211                         rl->u.dbrec.buf[oe->u.sutrs->len] = '\0';
3212                     }
3213                     rl->u.dbrec.size = oe->u.sutrs->len;
3214                 }
3215                 else if (rl->u.dbrec.type == VAL_GRS1 && 
3216                          oe->which == Z_External_grs1)
3217                 {
3218                     ir_tcl_read_grs (oe->u.grs1, &rl->u.dbrec.u.grs1);
3219                     rl->u.dbrec.buf = NULL;
3220                 }
3221                 else
3222                     rl->u.dbrec.buf = NULL;
3223             }
3224         }
3225     }
3226     else if (zrs->which == Z_Records_multipleNSD)
3227     {
3228         logf (LOG_DEBUG, "multipleNonSurrogateDiagnostic %d",
3229               zrs->u.multipleNonSurDiagnostics->num_diagRecs);
3230         setobj->numberOfRecordsReturned = 0;
3231         ir_handleDiags (&setobj->nonSurrogateDiagnosticList,
3232                         &setobj->nonSurrogateDiagnosticNum,
3233                         zrs->u.multipleNonSurDiagnostics->diagRecs,
3234                         zrs->u.multipleNonSurDiagnostics->num_diagRecs);
3235     }
3236     else
3237     {
3238         logf (LOG_DEBUG, "NonSurrogateDiagnostic");
3239         setobj->numberOfRecordsReturned = 0;
3240         ir_handleDiags (&setobj->nonSurrogateDiagnosticList,
3241                         &setobj->nonSurrogateDiagnosticNum,
3242                         &zrs->u.nonSurrogateDiagnostic,
3243                         1);
3244     }
3245 }
3246
3247 static void ir_searchResponse (void *o, Z_SearchResponse *searchrs,
3248                                IrTcl_SetObj *setobj)
3249 {    
3250     Z_Records *zrs = searchrs->records;
3251
3252     logf (LOG_DEBUG, "Received search response");
3253     if (!setobj)
3254     {
3255         logf (LOG_DEBUG, "Search response, no object!");
3256         return;
3257     }
3258     setobj->searchStatus = searchrs->searchStatus ? 1 : 0;
3259     get_referenceId (&setobj->set_inher.referenceId, searchrs->referenceId);
3260     setobj->resultCount = *searchrs->resultCount;
3261     if (searchrs->presentStatus)
3262         setobj->presentStatus = *searchrs->presentStatus;
3263     if (searchrs->nextResultSetPosition)
3264         setobj->nextResultSetPosition = *searchrs->nextResultSetPosition;
3265
3266     logf (LOG_DEBUG, "Search response %d, %d hits", 
3267           setobj->searchStatus, setobj->resultCount);
3268     if (zrs)
3269     {
3270         const char *es;
3271         if (setobj->resultCount <= setobj->set_inher.smallSetUpperBound)
3272             es = setobj->set_inher.smallSetElementSetNames;
3273         else 
3274             es = setobj->set_inher.mediumSetElementSetNames;
3275         ir_handleRecords (o, zrs, setobj, es);
3276     }
3277     else
3278         setobj->recordFlag = 0;
3279 }
3280
3281
3282 static void ir_presentResponse (void *o, Z_PresentResponse *presrs,
3283                                 IrTcl_SetObj *setobj)
3284 {
3285     Z_Records *zrs = presrs->records;
3286     
3287     logf (LOG_DEBUG, "Received present response");
3288     if (!setobj)
3289     {
3290         logf (LOG_DEBUG, "Present response, no object!");
3291         return;
3292     }
3293     setobj->presentStatus = *presrs->presentStatus;
3294     get_referenceId (&setobj->set_inher.referenceId, presrs->referenceId);
3295     setobj->nextResultSetPosition = *presrs->nextResultSetPosition;
3296     if (zrs)
3297         ir_handleRecords (o, zrs, setobj, setobj->set_inher.elementSetNames);
3298     else
3299     {
3300         setobj->recordFlag = 0;
3301         logf (LOG_DEBUG, "No records!");
3302     }
3303 }
3304
3305 static void ir_scanResponse (void *o, Z_ScanResponse *scanrs,
3306                              IrTcl_ScanObj *scanobj)
3307 {
3308     IrTcl_Obj *p = o;
3309     
3310     logf (LOG_DEBUG, "Received scanResponse");
3311
3312     get_referenceId (&p->set_inher.referenceId, scanrs->referenceId);
3313     scanobj->scanStatus = *scanrs->scanStatus;
3314     logf (LOG_DEBUG, "scanStatus=%d", scanobj->scanStatus);
3315
3316     if (scanrs->stepSize)
3317         scanobj->stepSize = *scanrs->stepSize;
3318     logf (LOG_DEBUG, "stepSize=%d", scanobj->stepSize);
3319
3320     scanobj->numberOfEntriesReturned = *scanrs->numberOfEntriesReturned;
3321     logf (LOG_DEBUG, "numberOfEntriesReturned=%d",
3322           scanobj->numberOfEntriesReturned);
3323
3324     if (scanrs->positionOfTerm)
3325         scanobj->positionOfTerm = *scanrs->positionOfTerm;
3326     else
3327         scanobj->positionOfTerm = -1;
3328     logf (LOG_DEBUG, "positionOfTerm=%d", scanobj->positionOfTerm);
3329
3330     free (scanobj->entries);
3331     scanobj->entries = NULL;
3332
3333     ir_deleteDiags (&scanobj->nonSurrogateDiagnosticList,
3334                     &scanobj->nonSurrogateDiagnosticNum);
3335     if (scanrs->entries)
3336     {
3337         int i;
3338         Z_Entry *ze;
3339
3340         scanobj->entries_flag = 1;
3341         scanobj->which = scanrs->entries->which;
3342         switch (scanobj->which)
3343         {
3344         case Z_ListEntries_entries:
3345             scanobj->num_entries = scanrs->entries->u.entries->num_entries;
3346             scanobj->entries = ir_tcl_malloc (scanobj->num_entries * 
3347                                        sizeof(*scanobj->entries));
3348             for (i=0; i<scanobj->num_entries; i++)
3349             {
3350                 ze = scanrs->entries->u.entries->entries[i];
3351                 scanobj->entries[i].which = ze->which;
3352                 switch (ze->which)
3353                 {
3354                 case Z_Entry_termInfo:
3355                     if (ze->u.termInfo->term->which == Z_Term_general)
3356                     {
3357                         int l = ze->u.termInfo->term->u.general->len;
3358                         scanobj->entries[i].u.term.buf = ir_tcl_malloc (1+l);
3359                         memcpy (scanobj->entries[i].u.term.buf, 
3360                                 ze->u.termInfo->term->u.general->buf,
3361                                 l);
3362                         scanobj->entries[i].u.term.buf[l] = '\0';
3363                     }
3364                     else
3365                         scanobj->entries[i].u.term.buf = NULL;
3366                     if (ze->u.termInfo->globalOccurrences)
3367                         scanobj->entries[i].u.term.globalOccurrences = 
3368                             *ze->u.termInfo->globalOccurrences;
3369                     else
3370                         scanobj->entries[i].u.term.globalOccurrences = 0;
3371                     break;
3372                 case Z_Entry_surrogateDiagnostic:
3373                     ir_handleDiags (&scanobj->entries[i].u.diag.list,
3374                                     &scanobj->entries[i].u.diag.num,
3375                                     &ze->u.surrogateDiagnostic,
3376                                     1);
3377                     break;
3378                 }
3379             }
3380             break;
3381         case Z_ListEntries_nonSurrogateDiagnostics:
3382             ir_handleDiags (&scanobj->nonSurrogateDiagnosticList,
3383                             &scanobj->nonSurrogateDiagnosticNum,
3384                             scanrs->entries->u.nonSurrogateDiagnostics->
3385                             diagRecs,
3386                             scanrs->entries->u.nonSurrogateDiagnostics->
3387                             num_diagRecs);
3388             break;
3389         }
3390     }
3391     else
3392         scanobj->entries_flag = 0;
3393 }
3394
3395 /*
3396  * ir_select_read: handle incoming packages
3397  */
3398 static void ir_select_read (ClientData clientData)
3399 {
3400     IrTcl_Obj *p = clientData;
3401     Z_APDU *apdu;
3402     int r;
3403     IrTcl_Request *rq;
3404     char *object_name;
3405     Tcl_CmdInfo cmd_info;
3406     const char *apdu_call;
3407
3408     logf(LOG_DEBUG, "Read handler fd=%d", cs_fileno(p->cs_link));
3409     if (p->state == IR_TCL_R_Connecting)
3410     {
3411         logf(LOG_DEBUG, "Connect handler");
3412         r = cs_rcvconnect (p->cs_link);
3413         if (r == 1)
3414         {
3415             logf (LOG_WARN, "cs_rcvconnect returned 1");
3416             return;
3417         }
3418         p->state = IR_TCL_R_Idle;
3419 #if IRTCL_GENERIC_FILES
3420         ir_select_remove_write (p->csFile, p);
3421 #else
3422         ir_select_remove_write (cs_fileno (p->cs_link), p);
3423 #endif
3424         if (r < 0)
3425         {
3426             logf (LOG_DEBUG, "cs_rcvconnect error");
3427             if (p->failback)
3428             {
3429                 p->failInfo = IR_TCL_FAIL_CONNECT;
3430                 IrTcl_eval (p->interp, p->failback);
3431             }
3432             do_disconnect (p, NULL, 2, NULL);
3433             return;
3434         }
3435         p->state = IR_TCL_R_Idle;
3436         if (p->callback)
3437             IrTcl_eval (p->interp, p->callback);
3438         if (p->cs_link && p->request_queue && p->state == IR_TCL_R_Idle)
3439             ir_tcl_send_q (p, p->request_queue, "x");
3440         return;
3441     }
3442     do
3443     {
3444         /* signal one more use of ir object - callbacks must not
3445            release the ir memory (p pointer) */
3446         p->state = IR_TCL_R_Reading;
3447         ++(p->ref_count);
3448
3449         /* read incoming APDU */
3450         if ((r=cs_get (p->cs_link, &p->buf_in, &p->len_in)) <= 0)
3451         {
3452             logf (LOG_DEBUG, "cs_get failed, code %d", r);
3453 #if IRTCL_GENERIC_FILES
3454             ir_select_remove (p->csFile, p);
3455 #else
3456             ir_select_remove (cs_fileno (p->cs_link), p);
3457 #endif
3458             do_disconnect (p, NULL, 2, NULL);
3459             if (p->failback)
3460             {
3461                 p->failInfo = IR_TCL_FAIL_READ;
3462                 IrTcl_eval (p->interp, p->failback);
3463             }
3464             /* release ir object now if callback deleted it */
3465             ir_obj_delete (p);
3466             return;
3467         }        
3468         if (r == 1)
3469         {
3470             logf(LOG_DEBUG, "PDU Fraction read");
3471             return ;
3472         }
3473         /* got complete APDU. Now decode */
3474         p->apduLen = r;
3475         p->apduOffset = -1;
3476         odr_setbuf (p->odr_in, p->buf_in, r, 0);
3477         logf (LOG_DEBUG, "cs_get ok, total size %d", r);
3478         if (!z_APDU (p->odr_in, &apdu, 0))
3479         {
3480             logf (LOG_DEBUG, "cs_get failed: %s",
3481                 odr_errmsg (odr_geterror (p->odr_in)));
3482             do_disconnect (p, NULL, 2, NULL);
3483             if (p->failback)
3484             {
3485                 p->failInfo = IR_TCL_FAIL_IN_APDU;
3486                 p->apduOffset = odr_offset (p->odr_in);
3487                 IrTcl_eval (p->interp, p->failback);
3488             }
3489             /* release ir object now if failback deleted it */
3490             ir_obj_delete (p);
3491             return;
3492         }
3493         logf(LOG_DEBUG, "Decoded ok");
3494         /* handle APDU and invoke callback */
3495         rq = p->request_queue;
3496         if (!rq)
3497         {
3498             logf (LOG_FATAL, "Internal error. No queue entry");
3499             exit (1);
3500         }
3501         object_name = rq->object_name;
3502         logf (LOG_DEBUG, "getCommandInfo (%s)", object_name);
3503         apdu_call = NULL;
3504         if (Tcl_GetCommandInfo (p->interp, object_name, &cmd_info))
3505         {
3506             switch(apdu->which)
3507             {
3508             case Z_APDU_initResponse:
3509                 p->eventType = "init";
3510                 ir_initResponse (p, apdu->u.initResponse);
3511                 apdu_call = p->initResponse;
3512                 break;
3513             case Z_APDU_searchResponse:
3514                 p->eventType = "search";
3515                 ir_searchResponse (p, apdu->u.searchResponse,
3516                                    (IrTcl_SetObj *) cmd_info.clientData);
3517                 apdu_call = ((IrTcl_SetObj *) 
3518                              cmd_info.clientData)->searchResponse;
3519                 break;
3520             case Z_APDU_presentResponse:
3521                 p->eventType = "present";
3522                 ir_presentResponse (p, apdu->u.presentResponse,
3523                                     (IrTcl_SetObj *) cmd_info.clientData);
3524                 apdu_call = ((IrTcl_SetObj *) 
3525                              cmd_info.clientData)->presentResponse;
3526                 break;
3527             case Z_APDU_scanResponse:
3528                 p->eventType = "scan";
3529                 ir_scanResponse (p, apdu->u.scanResponse, 
3530                                  (IrTcl_ScanObj *) cmd_info.clientData);
3531                 apdu_call = ((IrTcl_ScanObj *) 
3532                              cmd_info.clientData)->scanResponse;
3533                 break;
3534             default:
3535                 logf (LOG_WARN, "Received unknown APDU type (%d)",
3536                       apdu->which);
3537                 do_disconnect (p, NULL, 2, NULL);
3538                 if (p->failback)
3539                 {
3540                     p->failInfo = IR_TCL_FAIL_UNKNOWN_APDU;
3541                     IrTcl_eval (p->interp, p->failback);
3542                 }
3543                 return;
3544             }
3545         }
3546         p->request_queue = rq->next;
3547         p->state = IR_TCL_R_Idle;
3548        
3549         if (apdu_call)
3550             IrTcl_eval (p->interp, apdu_call);
3551         else if (rq->callback)
3552             IrTcl_eval (p->interp, rq->callback);
3553         free (rq->buf_out);
3554         free (rq->callback);
3555         free (rq->object_name);
3556         free (rq);
3557         odr_reset (p->odr_in);
3558         if (p->ref_count == 1)
3559         {
3560             ir_obj_delete (p);
3561             return;
3562         }
3563         --(p->ref_count);
3564     } while (p->cs_link && cs_more (p->cs_link));
3565     if (p->cs_link && p->request_queue && p->state == IR_TCL_R_Idle)
3566         ir_tcl_send_q (p, p->request_queue, "x");
3567 }
3568
3569 /*
3570  * ir_select_write: handle outgoing packages - not yet written.
3571  */
3572 static void ir_select_write (ClientData clientData)
3573 {
3574     IrTcl_Obj *p = clientData;
3575     int r;
3576     IrTcl_Request *rq;
3577
3578     logf (LOG_DEBUG, "Write handler fd=%d", cs_fileno(p->cs_link));
3579     if (p->state == IR_TCL_R_Connecting)
3580     {
3581         logf(LOG_DEBUG, "Connect handler");
3582         r = cs_rcvconnect (p->cs_link);
3583         if (r == 1)
3584             return;
3585         p->state = IR_TCL_R_Idle;
3586         if (r < 0)
3587         {
3588             logf (LOG_DEBUG, "cs_rcvconnect error");
3589 #if IRTCL_GENERIC_FILES
3590             ir_select_remove_write (p->csFile, p);
3591 #else
3592             ir_select_remove_write (cs_fileno (p->cs_link), p);
3593 #endif
3594             if (p->failback)
3595             {
3596                 p->failInfo = IR_TCL_FAIL_CONNECT;
3597                 IrTcl_eval (p->interp, p->failback);
3598             }
3599             do_disconnect (p, NULL, 2, NULL);
3600             return;
3601         }
3602 #if IRTCL_GENERIC_FILES
3603         ir_select_remove_write (p->csFile, p);
3604 #else
3605         ir_select_remove_write (cs_fileno (p->cs_link), p);
3606 #endif
3607         if (p->callback)
3608             IrTcl_eval (p->interp, p->callback);
3609         return;
3610     }
3611     rq = p->request_queue;
3612     if (!rq || !rq->buf_out)
3613         return;
3614     assert (rq);
3615     if ((r=cs_put (p->cs_link, rq->buf_out, rq->len_out)) < 0)
3616     {
3617         logf (LOG_DEBUG, "cs_put write fail");
3618         if (p->failback)
3619         {
3620             p->failInfo = IR_TCL_FAIL_WRITE;
3621             IrTcl_eval (p->interp, p->failback);
3622         }
3623         free (rq->buf_out);
3624         rq->buf_out = NULL;
3625         do_disconnect (p, NULL, 2, NULL);
3626     }
3627     else if (r == 0)            /* remove select bit */
3628     {
3629         logf(LOG_DEBUG, "Write completed");
3630         p->state = IR_TCL_R_Waiting;
3631 #if IRTCL_GENERIC_FILES
3632         ir_select_remove_write (p->csFile, p);
3633 #else
3634         ir_select_remove_write (cs_fileno (p->cs_link), p);
3635 #endif
3636         free (rq->buf_out);
3637         rq->buf_out = NULL;
3638     }
3639 }
3640
3641 static void ir_select_notify (ClientData clientData, int r, int w, int e)
3642 {
3643     if (r)
3644         ir_select_read (clientData);
3645     if (w)
3646         ir_select_write (clientData);
3647 }
3648
3649 /* ------------------------------------------------------- */
3650
3651 /*
3652  * Irtcl_init: Registration of TCL commands.
3653  */
3654 int Irtcl_Init (Tcl_Interp *interp)
3655 {
3656     Tcl_CreateCommand (interp, "ir", ir_obj_mk, (ClientData) NULL,
3657                        (Tcl_CmdDeleteProc *) NULL);
3658     Tcl_CreateCommand (interp, "ir-set", ir_set_obj_mk,
3659                        (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
3660     Tcl_CreateCommand (interp, "ir-scan", ir_scan_obj_mk,
3661                        (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
3662     return TCL_OK;
3663 }
3664