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