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