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