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