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