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