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