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