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