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