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