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