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