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