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