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