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