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