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