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