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