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