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