a2899cf5a644db801c9b42c08fb7965e99a7fe1d
[yaz-moved-to-github.git] / client / client.c
1 /* 
2  * Copyright (c) 1995-2002, Index Data
3  * See the file LICENSE for details.
4  *
5  * $Id: client.c,v 1.172 2002-10-04 19:05:36 adam Exp $
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <time.h>
11 #include <ctype.h>
12
13 #ifdef WIN32
14 #include <io.h>
15 #define S_ISREG(x) (x & _S_IFREG)
16 #define S_ISDIR(x) (x & _S_IFDIR)
17 #endif
18
19 #include <yaz/yaz-util.h>
20
21 #include <yaz/comstack.h>
22
23 #include <yaz/proto.h>
24 #include <yaz/marcdisp.h>
25 #include <yaz/diagbib1.h>
26 #include <yaz/otherinfo.h>
27 #include <yaz/charneg.h>
28
29 #include <yaz/pquery.h>
30 #include <yaz/sortspec.h>
31
32 #include <yaz/ill.h>
33
34 #include <yaz/yaz-ccl.h>
35
36 #if HAVE_READLINE_READLINE_H
37 #include <readline/readline.h>
38 #include <unistd.h>
39 #endif
40 #if HAVE_READLINE_HISTORY_H
41 #include <readline/history.h>
42 #endif
43
44 #include <sys/stat.h>
45
46
47 #include "admin.h"
48 #include "tabcomplete.h"
49
50 #define C_PROMPT "Z> "
51
52 static ODR out, in, print;              /* encoding and decoding streams */
53 static FILE *apdu_file = 0;
54 static COMSTACK conn = 0;               /* our z-association */
55 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
56 char *databaseNames[128];
57 int num_databaseNames = 0;
58 static Z_External *record_last = 0;
59 static int setnumber = -1;              /* current result set number */
60 static int smallSetUpperBound = 0;
61 static int largeSetLowerBound = 1;
62 static int mediumSetPresentNumber = 0;
63 static Z_ElementSetNames *elementSetNames = 0; 
64 static int setno = 1;                   /* current set offset */
65 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
66 static enum oid_value recordsyntax = VAL_USMARC;
67 static enum oid_value schema = VAL_NONE;
68 static int sent_close = 0;
69 static NMEM session_mem = NULL;         /* memory handle for init-response */
70 static Z_InitResponse *session = 0;     /* session parameters */
71 static char last_scan_line[512] = "0";
72 static char last_scan_query[512] = "0";
73 static char ccl_fields[512] = "default.bib";
74 static char* esPackageName = 0;
75 static char* yazProxy = 0;
76 static int kilobytes = 1024;
77 static char* yazCharset = 0;
78 static char* yazLang = 0;
79
80
81 static char last_cmd[32] = "?";
82 static FILE *marcdump = 0;
83 static char *refid = NULL;
84 static char *last_open_command = NULL;
85 static int auto_reconnect = 0;
86
87 typedef enum {
88     QueryType_Prefix,
89     QueryType_CCL,
90     QueryType_CCL2RPN
91 } QueryType;
92
93 static QueryType queryType = QueryType_Prefix;
94
95 static CCL_bibset bibset;               /* CCL bibset handle */
96
97 #if HAVE_READLINE_COMPLETION_OVER
98
99 #else
100 /* readline doesn't have this var. Define it ourselves. */
101 int rl_attempted_completion_over = 0;
102 #endif
103
104 /* set this one to 1, to avoid decode of unknown MARCs  */
105 #define AVOID_MARC_DECODE 1
106
107 /* nice helper macro as extensive tabbing gives spaces at the en of the args lines */
108 #define REMOVE_TAILING_BLANKS(a) {\
109   char* args_end=(a)+strlen(a)-1; \
110   while(isspace(*args_end)) {*args_end=0;--args_end;}; \
111   }
112
113 #define maxOtherInfosSupported 10
114 struct {
115     int oidval;
116     char* value;
117 } extraOtherInfos[maxOtherInfosSupported];
118         
119
120 void process_cmd_line(char* line);
121 char ** readline_completer(char *text, int start, int end);
122 char *command_generator(const char *text, int state);
123 char** curret_global_list=NULL;
124 int cmd_register_tab(char* arg);
125
126 ODR getODROutputStream()
127 {
128     return out;
129 }
130
131 const char* query_type_as_string(QueryType q) 
132 {
133     switch (q) { 
134     case QueryType_Prefix: return "prefix (RPN sent to server)";
135     case QueryType_CCL: return "CCL (CCL sent to server) ";
136     case QueryType_CCL2RPN: return "CCL -> RPN (RPN sent to server)";
137     default: 
138         return "unknown Query type internal yaz-client error";
139     }
140 }
141
142
143 void do_hex_dump(char* buf,int len) 
144 {
145 #if 0 
146     int i,x;
147     for( i=0; i<len ; i=i+16 ) 
148     {                   
149         printf(" %4.4d ",i);
150         for(x=0 ; i+x<len && x<16; ++x) 
151         {
152             printf("%2.2X ",(unsigned int)((unsigned char)buf[i+x]));
153         }
154         printf("\n");
155     }
156 #endif
157 }
158
159
160
161 void add_otherInfos(Z_APDU *a) 
162 {
163     Z_OtherInformation **oi;
164     int i;
165                 
166     yaz_oi_APDU(a, &oi);
167     for(i=0; i<maxOtherInfosSupported; ++i) 
168     {
169         if(extraOtherInfos[i].oidval != -1) 
170             yaz_oi_set_string_oidval(oi, out, extraOtherInfos[i].oidval, 1, extraOtherInfos[i].value);
171     }   
172 }
173
174 void send_apdu(Z_APDU *a)
175 {
176     char *buf;
177     int len;
178     
179     add_otherInfos(a);
180     
181     if (apdu_file)
182     {
183         z_APDU(print, &a, 0, 0);
184         odr_reset(print);
185     }
186     if (!z_APDU(out, &a, 0, 0))
187     {
188         odr_perror(out, "Encoding APDU");
189         exit(1);
190     }
191     buf = odr_getbuf(out, &len, 0);
192     /* printf ("sending APDU of size %d\n", len); */
193     if (cs_put(conn, buf, len) < 0)
194     {
195         fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
196         exit(1);
197     }
198     
199     do_hex_dump(buf,len);
200     odr_reset(out); /* release the APDU structure  */
201 }
202
203 static void print_stringn(const unsigned char *buf, size_t len)
204 {
205     size_t i;
206     for (i = 0; i<len; i++)
207         if ((buf[i] <= 126 && buf[i] >= 32) || strchr ("\n\r\t\f", buf[i]))
208             printf ("%c", buf[i]);
209         else
210             printf ("\\X%02X", buf[i]);
211 }
212
213 static void print_refid (Z_ReferenceId *id)
214 {
215     if (id)
216     {
217         printf ("Reference Id: ");
218         print_stringn (id->buf, id->len);
219         printf ("\n");
220     }
221 }
222
223 static Z_ReferenceId *set_refid (ODR out)
224 {
225     Z_ReferenceId *id;
226     if (!refid)
227         return 0;
228     id = (Z_ReferenceId *) odr_malloc (out, sizeof(*id));
229     id->size = id->len = strlen(refid);
230     id->buf = (unsigned char *) odr_malloc (out, id->len);
231     memcpy (id->buf, refid, id->len);
232     return id;
233 }   
234
235 /* INIT SERVICE ------------------------------- */
236
237 static void send_initRequest(const char* type_and_host)
238 {
239     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
240     Z_InitRequest *req = apdu->u.initRequest;
241
242     ODR_MASK_SET(req->options, Z_Options_search);
243     ODR_MASK_SET(req->options, Z_Options_present);
244     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
245     ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl);
246     ODR_MASK_SET(req->options, Z_Options_scan);
247     ODR_MASK_SET(req->options, Z_Options_sort);
248     ODR_MASK_SET(req->options, Z_Options_extendedServices);
249     ODR_MASK_SET(req->options, Z_Options_delSet);
250
251     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
252     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
253     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
254
255     *req->maximumRecordSize = 1024*kilobytes;
256     *req->preferredMessageSize = 1024*kilobytes;
257
258     req->idAuthentication = auth;
259
260     req->referenceId = set_refid (out);
261
262     if (yazProxy) 
263         yaz_oi_set_string_oidval(&req->otherInfo, out, VAL_PROXY,
264         1, type_and_host);
265     
266     if (yazCharset || yazLang) {
267         Z_OtherInformation **p;
268         Z_OtherInformationUnit *p0;
269         
270         yaz_oi_APDU(apdu, &p);
271         
272         if ((p0=yaz_oi_update(p, out, NULL, 0, 0))) {
273                 ODR_MASK_SET(req->options, Z_Options_negotiationModel);
274                 
275                 p0->which = Z_OtherInfo_externallyDefinedInfo;
276                 p0->information.externallyDefinedInfo =
277                         yaz_set_proposal_charneg(out,
278                                 (const char**)&yazCharset, (yazCharset)?1:0,
279                                 (const char**)&yazLang, (yazLang)?1:0, 1);
280         }
281     }
282     
283     send_apdu(apdu);
284     printf("Sent initrequest.\n");
285 }
286
287 static int process_initResponse(Z_InitResponse *res)
288 {
289     /* save session parameters for later use */
290     session_mem = odr_extract_mem(in);
291     session = res;
292
293     if (!*res->result)
294         printf("Connection rejected by target.\n");
295     else
296         printf("Connection accepted by target.\n");
297     if (res->implementationId)
298         printf("ID     : %s\n", res->implementationId);
299     if (res->implementationName)
300         printf("Name   : %s\n", res->implementationName);
301     if (res->implementationVersion)
302         printf("Version: %s\n", res->implementationVersion);
303     if (res->userInformationField)
304     {
305         printf("UserInformationfield:\n");
306         if (!z_External(print, (Z_External**)&res-> userInformationField,
307             0, 0))
308         {
309             odr_perror(print, "Printing userinfo\n");
310             odr_reset(print);
311         }
312         if (res->userInformationField->which == Z_External_octet)
313         {
314             printf("Guessing visiblestring:\n");
315             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
316         }
317         odr_reset (print);
318     }
319     printf ("Options:");
320     if (ODR_MASK_GET(res->options, Z_Options_search))
321         printf (" search");
322     if (ODR_MASK_GET(res->options, Z_Options_present))
323         printf (" present");
324     if (ODR_MASK_GET(res->options, Z_Options_delSet))
325         printf (" delSet");
326     if (ODR_MASK_GET(res->options, Z_Options_resourceReport))
327         printf (" resourceReport");
328     if (ODR_MASK_GET(res->options, Z_Options_resourceCtrl))
329         printf (" resourceCtrl");
330     if (ODR_MASK_GET(res->options, Z_Options_accessCtrl))
331         printf (" accessCtrl");
332     if (ODR_MASK_GET(res->options, Z_Options_scan))
333         printf (" scan");
334     if (ODR_MASK_GET(res->options, Z_Options_sort))
335         printf (" sort");
336     if (ODR_MASK_GET(res->options, Z_Options_extendedServices))
337         printf (" extendedServices");
338     if (ODR_MASK_GET(res->options, Z_Options_level_1Segmentation))
339         printf (" level1Segmentation");
340     if (ODR_MASK_GET(res->options, Z_Options_level_2Segmentation))
341         printf (" level2Segmentation");
342     if (ODR_MASK_GET(res->options, Z_Options_concurrentOperations))
343         printf (" concurrentOperations");
344     if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
345     {
346         printf (" namedResultSets");
347         setnumber = 0;
348     }
349     if (ODR_MASK_GET(res->options, Z_Options_encapsulation))
350         printf (" encapsulation");
351     if (ODR_MASK_GET(res->options, Z_Options_resultCount))
352         printf (" resultCount");
353     if (ODR_MASK_GET(res->options, Z_Options_negotiationModel))
354         printf (" negotiationModel");
355     if (ODR_MASK_GET(res->options, Z_Options_duplicateDetection))
356         printf (" duplicateDetection");
357     if (ODR_MASK_GET(res->options, Z_Options_queryType104))
358         printf (" queryType104");
359     printf ("\n");
360     
361     if (ODR_MASK_GET(res->options, Z_Options_negotiationModel)) {
362     
363         Z_CharSetandLanguageNegotiation *p =
364                 yaz_get_charneg_record(res->otherInfo);
365         
366         if (p) {
367             
368             char *charset=NULL, *lang=NULL;
369             int selected;
370             
371             yaz_get_response_charneg(session_mem, p, &charset, &lang,
372                                      &selected);
373             
374             printf("Accepted character set : %s\n", charset);
375             printf("Accepted code language : %s\n", lang ? lang : "none");
376             printf("Accepted records in ...: %d\n", selected );
377         }
378     }
379     fflush (stdout);
380     return 0;
381 }
382
383 static int cmd_base(char *arg)
384 {
385     int i;
386     char *cp;
387
388     if (!*arg)
389     {
390         printf("Usage: base <database> <database> ...\n");
391         return 0;
392     }
393     for (i = 0; i<num_databaseNames; i++)
394         xfree (databaseNames[i]);
395     num_databaseNames = 0;
396     while (1)
397     {
398         char *cp1;
399         if (!(cp = strchr(arg, ' ')))
400             cp = arg + strlen(arg);
401         if (cp - arg < 1)
402             break;
403         databaseNames[num_databaseNames] = (char *)xmalloc (1 + cp - arg);
404         memcpy (databaseNames[num_databaseNames], arg, cp - arg);
405         databaseNames[num_databaseNames][cp - arg] = '\0';
406
407         for (cp1 = databaseNames[num_databaseNames]; *cp1 ; cp1++)
408             if (*cp1 == '+')
409                 *cp1 = ' ';
410         num_databaseNames++;
411
412         if (!*cp)
413             break;
414         arg = cp+1;
415     }
416     return 1;
417 }
418
419 void cmd_open_remember_last_open_command(char* arg, char* new_open_command)
420 {
421         if(last_open_command != arg) 
422         {
423                 if(last_open_command) xfree(last_open_command);
424                 last_open_command = xstrdup(new_open_command);
425         }
426 }
427
428 int cmd_open(char *arg)
429 {
430     void *add;
431     char type_and_host[101], base[101]; 
432     
433     if (conn)
434     {
435         printf("Already connected.\n");
436         
437         cs_close (conn);
438         conn = NULL;
439         if (session_mem)
440         {
441             nmem_destroy (session_mem);
442             session_mem = NULL;
443         }
444     }   
445
446     if (strncmp (arg, "unix:", 5) == 0)
447     {
448         base[0] = '\0';
449         conn = cs_create_host(arg, 1, &add);
450                 cmd_open_remember_last_open_command(arg,arg);
451     }
452     else
453     {
454         base[0] = '\0';
455         if (sscanf (arg, "%100[^/]/%100s", type_and_host, base) < 1)
456             return 0;
457                 
458                 cmd_open_remember_last_open_command(arg,type_and_host);
459         if (yazProxy) 
460             conn = cs_create_host(yazProxy, 1, &add);
461         else 
462             conn = cs_create_host(type_and_host, 1, &add);
463     }
464     if (!conn)
465     {
466         printf ("Couldn't create comstack\n");
467         return 0;
468     }
469     printf("Connecting...");
470     fflush(stdout);
471     if (cs_connect(conn, add) < 0)
472     {
473         printf ("error = %s\n", cs_strerror(conn));
474         if (conn->cerrno == CSYSERR)
475             perror("system");
476         cs_close(conn);
477         conn = 0;
478         return 0;
479     }
480     printf("Ok.\n");
481     
482     send_initRequest(type_and_host);
483     if (*base)
484         cmd_base (base);
485
486     return 2;
487 }
488
489
490 void try_reconnect() 
491 {
492
493         char* open_command;
494         
495         if(!( auto_reconnect && last_open_command) ) return ;
496
497         open_command = (char *) xmalloc (strlen(last_open_command)+6);
498         strcpy (open_command, "open ");
499         
500         strcat (open_command, last_open_command);
501
502         process_cmd_line(open_command);
503         
504         xfree(open_command);                            
505 }
506
507 int cmd_authentication(char *arg)
508 {
509     static Z_IdAuthentication au;
510     static char user[40], group[40], pass[40];
511     static Z_IdPass idPass;
512     int r;
513
514     if (!*arg)
515     {
516         printf("Auth field set to null\n");
517         auth = 0;
518         return 1;
519     }
520     r = sscanf (arg, "%39s %39s %39s", user, group, pass);
521     if (r == 0)
522     {
523         printf("Auth field set to null\n");
524         auth = 0;
525     }
526     if (r == 1)
527     {
528         auth = &au;
529         au.which = Z_IdAuthentication_open;
530         au.u.open = user;
531     }
532     if (r == 2)
533     {
534         auth = &au;
535         au.which = Z_IdAuthentication_idPass;
536         au.u.idPass = &idPass;
537         idPass.groupId = NULL;
538         idPass.userId = user;
539         idPass.password = group;
540     }
541     if (r == 3)
542     {
543         auth = &au;
544         au.which = Z_IdAuthentication_idPass;
545         au.u.idPass = &idPass;
546         idPass.groupId = group;
547         idPass.userId = user;
548         idPass.password = pass;
549     }
550     return 1;
551 }
552
553 /* SEARCH SERVICE ------------------------------ */
554 static void display_record(Z_External *r);
555
556 static void display_variant(Z_Variant *v, int level)
557 {
558     int i;
559
560     for (i = 0; i < v->num_triples; i++)
561     {
562         printf("%*sclass=%d,type=%d", level * 4, "", *v->triples[i]->zclass,
563             *v->triples[i]->type);
564         if (v->triples[i]->which == Z_Triple_internationalString)
565             printf(",value=%s\n", v->triples[i]->value.internationalString);
566         else
567             printf("\n");
568     }
569 }
570
571 static void display_grs1(Z_GenericRecord *r, int level)
572 {
573     int i;
574
575     if (!r)
576     {
577         return;
578     }
579     for (i = 0; i < r->num_elements; i++)
580     {
581         Z_TaggedElement *t;
582
583         printf("%*s", level * 4, "");
584         t = r->elements[i];
585         printf("(");
586         if (t->tagType)
587             printf("%d,", *t->tagType);
588         else
589             printf("?,");
590         if (t->tagValue->which == Z_StringOrNumeric_numeric)
591             printf("%d) ", *t->tagValue->u.numeric);
592         else
593             printf("%s) ", t->tagValue->u.string);
594         if (t->content->which == Z_ElementData_subtree)
595         {
596             if (!t->content->u.subtree)
597                 printf (" (no subtree)\n");
598             else
599             {
600                 printf("\n");
601                 display_grs1(t->content->u.subtree, level+1);
602             }
603         }
604         else if (t->content->which == Z_ElementData_string)
605             printf("%s\n", t->content->u.string);
606         else if (t->content->which == Z_ElementData_numeric)
607             printf("%d\n", *t->content->u.numeric);
608         else if (t->content->which == Z_ElementData_oid)
609         {
610             int *ip = t->content->u.oid;
611             oident *oent;
612             
613             if ((oent = oid_getentbyoid(t->content->u.oid)))
614                 printf("OID: %s\n", oent->desc);
615             else
616             {
617                 printf("{");
618                 while (ip && *ip >= 0)
619                     printf(" %d", *(ip++));
620                 printf(" }\n");
621             }
622         }
623         else if (t->content->which == Z_ElementData_noDataRequested)
624             printf("[No data requested]\n");
625         else if (t->content->which == Z_ElementData_elementEmpty)
626             printf("[Element empty]\n");
627         else if (t->content->which == Z_ElementData_elementNotThere)
628             printf("[Element not there]\n");
629         else if (t->content->which == Z_ElementData_date)
630             printf("Date: %s\n", t->content->u.date);
631         else if (t->content->which == Z_ElementData_ext)
632         {
633             printf ("External\n");
634             display_record (t->content->u.ext);
635         } 
636         else
637             printf("? type = %d\n",t->content->which);
638         if (t->appliedVariant)
639             display_variant(t->appliedVariant, level+1);
640         if (t->metaData && t->metaData->supportedVariants)
641         {
642             int c;
643
644             printf("%*s---- variant list\n", (level+1)*4, "");
645             for (c = 0; c < t->metaData->num_supportedVariants; c++)
646             {
647                 printf("%*svariant #%d\n", (level+1)*4, "", c);
648                 display_variant(t->metaData->supportedVariants[c], level + 2);
649             }
650         }
651     }
652 }
653
654
655 static void print_record(const unsigned char *buf, size_t len)
656 {
657     size_t i = len;
658     print_stringn (buf, len);
659     /* add newline if not already added ... */
660     if (i <= 0 || buf[i-1] != '\n')
661         printf ("\n");
662 }
663
664 static void display_record(Z_External *r)
665 {
666     oident *ent = oid_getentbyoid(r->direct_reference);
667
668     record_last = r;
669     /*
670      * Tell the user what we got.
671      */
672     if (r->direct_reference)
673     {
674         printf("Record type: ");
675         if (ent)
676             printf("%s\n", ent->desc);
677         else if (!odr_oid(print, &r->direct_reference, 0, 0))
678         {
679             odr_perror(print, "print oid");
680             odr_reset(print);
681         }
682     }
683     /* Check if this is a known, ASN.1 type tucked away in an octet string */
684     if (ent && r->which == Z_External_octet)
685     {
686         Z_ext_typeent *type = z_ext_getentbyref(ent->value);
687         void *rr;
688
689         if (type)
690         {
691             /*
692              * Call the given decoder to process the record.
693              */
694             odr_setbuf(in, (char*)r->u.octet_aligned->buf,
695                 r->u.octet_aligned->len, 0);
696             if (!(*type->fun)(in, (char **)&rr, 0, 0))
697             {
698                 odr_perror(in, "Decoding constructed record.");
699                 fprintf(stderr, "[Near %d]\n", odr_offset(in));
700                 fprintf(stderr, "Packet dump:\n---------\n");
701                 odr_dumpBER(stderr, (char*)r->u.octet_aligned->buf,
702                             r->u.octet_aligned->len);
703                 fprintf(stderr, "---------\n");
704                 
705                 /* note just ignores the error ant print the bytes form the octet_aligned later */
706             } else {
707                 /*
708                  * Note: we throw away the original, BER-encoded record here.
709                  * Do something else with it if you want to keep it.
710                  */
711                 r->u.sutrs = (Z_SUTRS *) rr; /* we don't actually check the type here. */
712                 r->which = type->what;
713             }
714         }
715     }
716     if (ent && ent->oclass != CLASS_RECSYN) 
717         return;
718     if (ent && ent->value == VAL_SOIF)
719         print_record((const unsigned char *) r->u.octet_aligned->buf,
720                      r->u.octet_aligned->len);
721     else if (r->which == Z_External_octet && r->u.octet_aligned->len)
722     {
723         const char *octet_buf = (char*)r->u.octet_aligned->buf;
724         if (ent->value == VAL_TEXT_XML || ent->value == VAL_APPLICATION_XML ||
725             ent->value == VAL_HTML)
726         {
727             print_record((const unsigned char *) octet_buf,
728                          r->u.octet_aligned->len);
729         }
730         else if (ent->value == VAL_POSTSCRIPT)
731         {
732             int size = r->u.octet_aligned->len;
733             if (size > 100)
734                 size = 100;
735             print_record((const unsigned char *) octet_buf, size);
736         }
737         else
738         {
739             if ( 
740 #if AVOID_MARC_DECODE
741                 /* primitive check for a marc OID 5.1-29 except 16 */
742                 ent->oidsuffix[0] == 5 && ent->oidsuffix[1] < 30 &&
743                 ent->oidsuffix[1] != 16
744 #else
745                 1
746 #endif
747                 )
748             {
749                 if (marc_display_exl (octet_buf, NULL, 0 /* debug */,
750                                       r->u.octet_aligned->len) <= 0)
751                 {
752                     printf ("bad MARC. Dumping as it is:\n");
753                     print_record((const unsigned char*) octet_buf,
754                                  r->u.octet_aligned->len);
755                 }
756             }
757             else
758             {
759                 print_record((const unsigned char*) octet_buf,
760                              r->u.octet_aligned->len);
761             }
762         }
763         if (marcdump)
764             fwrite (octet_buf, 1, r->u.octet_aligned->len, marcdump);
765     }
766     else if (ent && ent->value == VAL_SUTRS)
767     {
768         if (r->which != Z_External_sutrs)
769         {
770             printf("Expecting single SUTRS type for SUTRS.\n");
771             return;
772         }
773         print_record(r->u.sutrs->buf, r->u.sutrs->len);
774     }
775     else if (ent && ent->value == VAL_GRS1)
776     {
777         if (r->which != Z_External_grs1)
778         {
779             printf("Expecting single GRS type for GRS.\n");
780             return;
781         }
782         display_grs1(r->u.grs1, 0);
783     }
784     else 
785     {
786         printf("Unknown record representation.\n");
787         if (!z_External(print, &r, 0, 0))
788         {
789             odr_perror(print, "Printing external");
790             odr_reset(print);
791         }
792     }
793 }
794
795 static void display_diagrecs(Z_DiagRec **pp, int num)
796 {
797     int i;
798     oident *ent;
799     Z_DefaultDiagFormat *r;
800
801     printf("Diagnostic message(s) from database:\n");
802     for (i = 0; i<num; i++)
803     {
804         Z_DiagRec *p = pp[i];
805         if (p->which != Z_DiagRec_defaultFormat)
806         {
807             printf("Diagnostic record not in default format.\n");
808             return;
809         }
810         else
811             r = p->u.defaultFormat;
812         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
813             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
814             printf("Missing or unknown diagset\n");
815         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
816         switch (r->which)
817         {
818         case Z_DefaultDiagFormat_v2Addinfo:
819             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
820             break;
821         case Z_DefaultDiagFormat_v3Addinfo:
822             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
823             break;
824         }
825     }
826 }
827
828
829 static void display_nameplusrecord(Z_NamePlusRecord *p)
830 {
831     if (p->databaseName)
832         printf("[%s]", p->databaseName);
833     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
834         display_diagrecs(&p->u.surrogateDiagnostic, 1);
835     else if (p->which == Z_NamePlusRecord_databaseRecord)
836         display_record(p->u.databaseRecord);
837 }
838
839 static void display_records(Z_Records *p)
840 {
841     int i;
842
843     if (p->which == Z_Records_NSD)
844     {
845         Z_DiagRec dr, *dr_p = &dr;
846         dr.which = Z_DiagRec_defaultFormat;
847         dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
848         display_diagrecs (&dr_p, 1);
849     }
850     else if (p->which == Z_Records_multipleNSD)
851         display_diagrecs (p->u.multipleNonSurDiagnostics->diagRecs,
852                           p->u.multipleNonSurDiagnostics->num_diagRecs);
853     else 
854     {
855         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
856         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
857             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
858     }
859 }
860
861 static int send_deleteResultSetRequest(char *arg)
862 {
863     char names[8][32];
864     int i;
865
866     Z_APDU *apdu = zget_APDU(out, Z_APDU_deleteResultSetRequest);
867     Z_DeleteResultSetRequest *req = apdu->u.deleteResultSetRequest;
868
869     req->referenceId = set_refid (out);
870
871     req->num_resultSetList =
872         sscanf (arg, "%30s %30s %30s %30s %30s %30s %30s %30s",
873                 names[0], names[1], names[2], names[3],
874                 names[4], names[5], names[6], names[7]);
875
876     req->deleteFunction = (int *)
877         odr_malloc (out, sizeof(*req->deleteFunction));
878     if (req->num_resultSetList > 0)
879     {
880         *req->deleteFunction = Z_DeleteRequest_list;
881         req->resultSetList = (char **)
882             odr_malloc (out, sizeof(*req->resultSetList)*
883                         req->num_resultSetList);
884         for (i = 0; i<req->num_resultSetList; i++)
885             req->resultSetList[i] = names[i];
886     }
887     else
888     {
889         *req->deleteFunction = Z_DeleteRequest_all;
890         req->resultSetList = 0;
891     }
892     
893     send_apdu(apdu);
894     printf("Sent deleteResultSetRequest.\n");
895     return 2;
896 }
897
898 static int send_searchRequest(char *arg)
899 {
900     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
901     Z_SearchRequest *req = apdu->u.searchRequest;
902     Z_Query query;
903     int oid[OID_SIZE];
904     struct ccl_rpn_node *rpn = NULL;
905     int error, pos;
906     char setstring[100];
907     Z_RPNQuery *RPNquery;
908     Odr_oct ccl_query;
909     YAZ_PQF_Parser pqf_parser;
910
911     if (queryType == QueryType_CCL2RPN)
912     {
913         rpn = ccl_find_str(bibset, arg, &error, &pos);
914         if (error)
915         {
916             printf("CCL ERROR: %s\n", ccl_err_msg(error));
917             return 0;
918         }
919     }
920     req->referenceId = set_refid (out);
921     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
922     {
923         static unsigned char big[2100];
924         static Odr_oct bigo;
925
926         /* send a very big referenceid to test transport stack etc. */
927         memset(big, 'A', 2100);
928         bigo.len = bigo.size = 2100;
929         bigo.buf = big;
930         req->referenceId = &bigo;
931     }
932     
933     if (setnumber >= 0)
934     {
935         sprintf(setstring, "%d", ++setnumber);
936         req->resultSetName = setstring;
937     }
938     *req->smallSetUpperBound = smallSetUpperBound;
939     *req->largeSetLowerBound = largeSetLowerBound;
940     *req->mediumSetPresentNumber = mediumSetPresentNumber;
941     if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
942         mediumSetPresentNumber > 0))
943     {
944         oident prefsyn;
945
946         prefsyn.proto = protocol;
947         prefsyn.oclass = CLASS_RECSYN;
948         prefsyn.value = recordsyntax;
949         req->preferredRecordSyntax =
950             odr_oiddup(out, oid_ent_to_oid(&prefsyn, oid));
951         req->smallSetElementSetNames =
952             req->mediumSetElementSetNames = elementSetNames;
953     }
954     req->num_databaseNames = num_databaseNames;
955     req->databaseNames = databaseNames;
956
957     req->query = &query;
958
959     switch (queryType)
960     {
961     case QueryType_Prefix:
962         query.which = Z_Query_type_1;
963         pqf_parser = yaz_pqf_create ();
964         RPNquery = yaz_pqf_parse (pqf_parser, out, arg);
965         if (!RPNquery)
966         {
967             const char *pqf_msg;
968             size_t off;
969             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
970             printf("%*s^\n", off+4, "");
971             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
972             
973             yaz_pqf_destroy (pqf_parser);
974             return 0;
975         }
976         yaz_pqf_destroy (pqf_parser);
977         query.u.type_1 = RPNquery;
978         break;
979     case QueryType_CCL:
980         query.which = Z_Query_type_2;
981         query.u.type_2 = &ccl_query;
982         ccl_query.buf = (unsigned char*) arg;
983         ccl_query.len = strlen(arg);
984         break;
985     case QueryType_CCL2RPN:
986         query.which = Z_Query_type_1;
987         RPNquery = ccl_rpn_query(out, rpn);
988         if (!RPNquery)
989         {
990             printf ("Couldn't convert from CCL to RPN\n");
991             return 0;
992         }
993         query.u.type_1 = RPNquery;
994         ccl_rpn_delete (rpn);
995         break;
996     default:
997         printf ("Unsupported query type\n");
998         return 0;
999     }
1000     send_apdu(apdu);
1001     setno = 1;
1002     printf("Sent searchRequest.\n");
1003     return 2;
1004 }
1005
1006 /* display Query Expression as part of searchResult-1 */
1007 static void display_queryExpression (Z_QueryExpression *qe)
1008 {
1009     if (!qe)
1010         return;
1011     if (qe->which == Z_QueryExpression_term)
1012     {
1013         if (qe->u.term->queryTerm)
1014         {
1015             Z_Term *term = qe->u.term->queryTerm;
1016             switch (term->which)
1017             {
1018             case Z_Term_general:
1019                 printf (" %.*s", term->u.general->len, term->u.general->buf);
1020                 break;
1021             case Z_Term_characterString:
1022                 printf (" %s", term->u.characterString);
1023                 break;
1024             case Z_Term_numeric:
1025                 printf (" %d", *term->u.numeric);
1026                 break;
1027             case Z_Term_null:
1028                 printf (" null");
1029                 break;
1030             }
1031         }
1032     }
1033 }
1034
1035 /* see if we can find USR:SearchResult-1 */
1036 static void display_searchResult (Z_OtherInformation *o)
1037 {
1038     int i;
1039     if (!o)
1040         return ;
1041     for (i = 0; i < o->num_elements; i++)
1042     {
1043         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
1044         {
1045             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
1046             
1047             if (ext->which == Z_External_searchResult1)
1048             {
1049                 int j;
1050                 Z_SearchInfoReport *sr = ext->u.searchResult1;
1051                 printf ("SearchResult-1:");
1052                 for (j = 0; j < sr->num; j++)
1053                 {
1054                     if (!sr->elements[j]->subqueryExpression)
1055                         printf (" %d", j);
1056                     display_queryExpression (
1057                         sr->elements[j]->subqueryExpression);
1058                     display_queryExpression (
1059                         sr->elements[j]->subqueryInterpretation);
1060                     display_queryExpression (
1061                         sr->elements[j]->subqueryRecommendation);
1062                     if (sr->elements[j]->subqueryCount)
1063                         printf ("(%d)", *sr->elements[j]->subqueryCount);
1064                 }
1065                 printf ("\n");
1066             }
1067         }
1068     }
1069 }
1070
1071 static int process_searchResponse(Z_SearchResponse *res)
1072 {
1073     printf ("Received SearchResponse.\n");
1074     print_refid (res->referenceId);
1075     if (*res->searchStatus)
1076         printf("Search was a success.\n");
1077     else
1078         printf("Search was a bloomin' failure.\n");
1079     printf("Number of hits: %d", *res->resultCount);
1080     if (setnumber >= 0)
1081         printf (", setno %d", setnumber);
1082     printf ("\n");
1083     display_searchResult (res->additionalSearchInfo);
1084     printf("records returned: %d\n",
1085            *res->numberOfRecordsReturned);
1086     setno += *res->numberOfRecordsReturned;
1087     if (res->records)
1088         display_records(res->records);
1089     return 0;
1090 }
1091
1092 static void print_level(int iLevel)
1093 {
1094     int i;
1095     for (i = 0; i < iLevel * 4; i++)
1096         printf(" ");
1097 }
1098
1099 static void print_int(int iLevel, const char *pTag, int *pInt)
1100 {
1101     if (pInt != NULL)
1102     {
1103         print_level(iLevel);
1104         printf("%s: %d\n", pTag, *pInt);
1105     }
1106 }
1107
1108 static void print_string(int iLevel, const char *pTag, const char *pString)
1109 {
1110     if (pString != NULL)
1111     {
1112         print_level(iLevel);
1113         printf("%s: %s\n", pTag, pString);
1114     }
1115 }
1116
1117 static void print_oid(int iLevel, const char *pTag, Odr_oid *pOid)
1118 {
1119     if (pOid != NULL)
1120     {
1121         int *pInt = pOid;
1122
1123         print_level(iLevel);
1124         printf("%s:", pTag);
1125         for (; *pInt != -1; pInt++)
1126             printf(" %d", *pInt);
1127         printf("\n");
1128     }
1129 }
1130
1131 static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
1132 {
1133     if (referenceId != NULL)
1134     {
1135         int i;
1136
1137         print_level(iLevel);
1138         printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
1139         for (i = 0; i < referenceId->len; i++)
1140             printf("%c", referenceId->buf[i]);
1141         printf("\n");
1142     }
1143 }
1144
1145 static void print_string_or_numeric(int iLevel, const char *pTag, Z_StringOrNumeric *pStringNumeric)
1146 {
1147     if (pStringNumeric != NULL)
1148     {
1149         switch (pStringNumeric->which)
1150         {
1151         case Z_StringOrNumeric_string:
1152             print_string(iLevel, pTag, pStringNumeric->u.string);
1153             break;
1154             
1155         case Z_StringOrNumeric_numeric:
1156             print_int(iLevel, pTag, pStringNumeric->u.numeric);
1157             break;
1158             
1159         default:
1160             print_level(iLevel);
1161             printf("%s: valid type for Z_StringOrNumeric\n", pTag);
1162             break;
1163         }
1164     }
1165 }
1166
1167 static void print_universe_report_duplicate(
1168     int iLevel,
1169     Z_UniverseReportDuplicate *pUniverseReportDuplicate)
1170 {
1171     if (pUniverseReportDuplicate != NULL)
1172     {
1173         print_level(iLevel);
1174         printf("Universe Report Duplicate: \n");
1175         iLevel++;
1176         print_string_or_numeric(iLevel, "Hit No",
1177                                 pUniverseReportDuplicate->hitno);
1178     }
1179 }
1180
1181 static void print_universe_report_hits(
1182     int iLevel,
1183     Z_UniverseReportHits *pUniverseReportHits)
1184 {
1185     if (pUniverseReportHits != NULL)
1186     {
1187         print_level(iLevel);
1188         printf("Universe Report Hits: \n");
1189         iLevel++;
1190         print_string_or_numeric(iLevel, "Database",
1191                                 pUniverseReportHits->database);
1192         print_string_or_numeric(iLevel, "Hits", pUniverseReportHits->hits);
1193     }
1194 }
1195
1196 static void print_universe_report(int iLevel, Z_UniverseReport *pUniverseReport)
1197 {
1198     if (pUniverseReport != NULL)
1199     {
1200         print_level(iLevel);
1201         printf("Universe Report: \n");
1202         iLevel++;
1203         print_int(iLevel, "Total Hits", pUniverseReport->totalHits);
1204         switch (pUniverseReport->which)
1205         {
1206         case Z_UniverseReport_databaseHits:
1207             print_universe_report_hits(iLevel,
1208                                        pUniverseReport->u.databaseHits);
1209             break;
1210             
1211         case Z_UniverseReport_duplicate:
1212             print_universe_report_duplicate(iLevel,
1213                                             pUniverseReport->u.duplicate);
1214             break;
1215             
1216         default:
1217             print_level(iLevel);
1218             printf("Type: %d\n", pUniverseReport->which);
1219             break;
1220         }
1221     }
1222 }
1223
1224 static void print_external(int iLevel, Z_External *pExternal)
1225 {
1226     if (pExternal != NULL)
1227     {
1228         print_level(iLevel);
1229         printf("External: \n");
1230         iLevel++;
1231         print_oid(iLevel, "Direct Reference", pExternal->direct_reference);
1232         print_int(iLevel, "InDirect Reference", pExternal->indirect_reference);
1233         print_string(iLevel, "Descriptor", pExternal->descriptor);
1234         switch (pExternal->which)
1235         {
1236         case Z_External_universeReport:
1237             print_universe_report(iLevel, pExternal->u.universeReport);
1238             break;
1239             
1240         default:
1241             print_level(iLevel);
1242             printf("Type: %d\n", pExternal->which);
1243             break;
1244         }
1245     }
1246 }
1247
1248 static int process_resourceControlRequest (Z_ResourceControlRequest *req)
1249 {
1250     printf ("Received ResourceControlRequest.\n");
1251     print_referenceId(1, req->referenceId);
1252     print_int(1, "Suspended Flag", req->suspendedFlag);
1253     print_int(1, "Partial Results Available", req->partialResultsAvailable);
1254     print_int(1, "Response Required", req->responseRequired);
1255     print_int(1, "Triggered Request Flag", req->triggeredRequestFlag);
1256     print_external(1, req->resourceReport);
1257     return 0;
1258 }
1259
1260 void process_ESResponse(Z_ExtendedServicesResponse *res)
1261 {
1262     printf("Status: ");
1263     switch (*res->operationStatus)
1264     {
1265     case Z_ExtendedServicesResponse_done:
1266         printf ("done\n");
1267         break;
1268     case Z_ExtendedServicesResponse_accepted:
1269         printf ("accepted\n");
1270         break;
1271     case Z_ExtendedServicesResponse_failure:
1272         printf ("failure\n");
1273         display_diagrecs(res->diagnostics, res->num_diagnostics);
1274         break;
1275     default:
1276         printf ("unknown\n");
1277     }
1278     if ( (*res->operationStatus != Z_ExtendedServicesResponse_failure) &&
1279         (res->num_diagnostics != 0) ) {
1280         display_diagrecs(res->diagnostics, res->num_diagnostics);
1281     }
1282     print_refid (res->referenceId);
1283     if (res->taskPackage && 
1284         res->taskPackage->which == Z_External_extendedService)
1285     {
1286         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
1287         Odr_oct *id = taskPackage->targetReference;
1288         Z_External *ext = taskPackage->taskSpecificParameters;
1289         
1290         if (id)
1291         {
1292             printf ("Target Reference: ");
1293             print_stringn (id->buf, id->len);
1294             printf ("\n");
1295         }
1296         if (ext->which == Z_External_update)
1297         {
1298             Z_IUUpdateTaskPackage *utp = ext->u.update->u.taskPackage;
1299             if (utp && utp->targetPart)
1300             {
1301                 Z_IUTargetPart *targetPart = utp->targetPart;
1302                 int i;
1303
1304                 for (i = 0; i<targetPart->num_taskPackageRecords;  i++)
1305                 {
1306
1307                     Z_IUTaskPackageRecordStructure *tpr =
1308                         targetPart->taskPackageRecords[i];
1309                     printf ("task package record %d\n", i+1);
1310                     if (tpr->which == Z_IUTaskPackageRecordStructure_record)
1311                     {
1312                         display_record (tpr->u.record);
1313                     }
1314                     else
1315                     {
1316                         printf ("other type\n");
1317                     }
1318                 }
1319             }
1320         }
1321     }
1322 }
1323
1324 const char *get_ill_element (void *clientData, const char *element)
1325 {
1326     return 0;
1327 }
1328
1329 static Z_External *create_external_itemRequest()
1330 {
1331     struct ill_get_ctl ctl;
1332     ILL_ItemRequest *req;
1333     Z_External *r = 0;
1334     int item_request_size = 0;
1335     char *item_request_buf = 0;
1336
1337     ctl.odr = out;
1338     ctl.clientData = 0;
1339     ctl.f = get_ill_element;
1340     
1341     req = ill_get_ItemRequest(&ctl, "ill", 0);
1342     if (!req)
1343         printf ("ill_get_ItemRequest failed\n");
1344         
1345     if (!ill_ItemRequest (out, &req, 0, 0))
1346     {
1347         if (apdu_file)
1348         {
1349             ill_ItemRequest(print, &req, 0, 0);
1350             odr_reset(print);
1351         }
1352         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1353         if (item_request_buf)
1354             odr_setbuf (out, item_request_buf, item_request_size, 1);
1355         printf ("Couldn't encode ItemRequest, size %d\n", item_request_size);
1356         return 0;
1357     }
1358     else
1359     {
1360         oident oid;
1361         
1362         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1363         oid.proto = PROTO_GENERAL;
1364         oid.oclass = CLASS_GENERAL;
1365         oid.value = VAL_ISO_ILL_1;
1366         
1367         r = (Z_External *) odr_malloc (out, sizeof(*r));
1368         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1369         r->indirect_reference = 0;
1370         r->descriptor = 0;
1371         r->which = Z_External_single;
1372         
1373         r->u.single_ASN1_type = (Odr_oct *)
1374             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1375         r->u.single_ASN1_type->buf = (unsigned char *)
1376         odr_malloc (out, item_request_size);
1377         r->u.single_ASN1_type->len = item_request_size;
1378         r->u.single_ASN1_type->size = item_request_size;
1379         memcpy (r->u.single_ASN1_type->buf, item_request_buf,
1380                 item_request_size);
1381                 
1382                 do_hex_dump(item_request_buf,item_request_size);
1383     }
1384     return r;
1385 }
1386
1387 static Z_External *create_external_ILL_APDU(int which)
1388 {
1389     struct ill_get_ctl ctl;
1390     ILL_APDU *ill_apdu;
1391     Z_External *r = 0;
1392     int ill_request_size = 0;
1393     char *ill_request_buf = 0;
1394         
1395     ctl.odr = out;
1396     ctl.clientData = 0;
1397     ctl.f = get_ill_element;
1398
1399     ill_apdu = ill_get_APDU(&ctl, "ill", 0);
1400
1401     if (!ill_APDU (out, &ill_apdu, 0, 0))
1402     {
1403         if (apdu_file)
1404         {
1405             printf ("-------------------\n");
1406             ill_APDU(print, &ill_apdu, 0, 0);
1407             odr_reset(print);
1408             printf ("-------------------\n");
1409         }
1410         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1411         if (ill_request_buf)
1412             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
1413         printf ("Couldn't encode ILL-Request, size %d\n", ill_request_size);
1414         return 0;
1415     }
1416     else
1417     {
1418         oident oid;
1419         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1420         
1421         oid.proto = PROTO_GENERAL;
1422         oid.oclass = CLASS_GENERAL;
1423         oid.value = VAL_ISO_ILL_1;
1424         
1425         r = (Z_External *) odr_malloc (out, sizeof(*r));
1426         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1427         r->indirect_reference = 0;
1428         r->descriptor = 0;
1429         r->which = Z_External_single;
1430         
1431         r->u.single_ASN1_type = (Odr_oct *)
1432             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1433         r->u.single_ASN1_type->buf = (unsigned char *)
1434         odr_malloc (out, ill_request_size);
1435         r->u.single_ASN1_type->len = ill_request_size;
1436         r->u.single_ASN1_type->size = ill_request_size;
1437         memcpy (r->u.single_ASN1_type->buf, ill_request_buf, ill_request_size);
1438 /*         printf ("len = %d\n", ill_request_size); */
1439 /*              do_hex_dump(ill_request_buf,ill_request_size); */
1440 /*              printf("--- end of extenal\n"); */
1441
1442     }
1443     return r;
1444 }
1445
1446
1447 static Z_External *create_ItemOrderExternal(const char *type, int itemno)
1448 {
1449     Z_External *r = (Z_External *) odr_malloc(out, sizeof(Z_External));
1450     oident ItemOrderRequest;
1451   
1452     ItemOrderRequest.proto = PROTO_Z3950;
1453     ItemOrderRequest.oclass = CLASS_EXTSERV;
1454     ItemOrderRequest.value = VAL_ITEMORDER;
1455  
1456     r->direct_reference = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest)); 
1457     r->indirect_reference = 0;
1458     r->descriptor = 0;
1459
1460     r->which = Z_External_itemOrder;
1461
1462     r->u.itemOrder = (Z_ItemOrder *) odr_malloc(out,sizeof(Z_ItemOrder));
1463     memset(r->u.itemOrder, 0, sizeof(Z_ItemOrder));
1464     r->u.itemOrder->which=Z_IOItemOrder_esRequest;
1465
1466     r->u.itemOrder->u.esRequest = (Z_IORequest *) 
1467         odr_malloc(out,sizeof(Z_IORequest));
1468     memset(r->u.itemOrder->u.esRequest, 0, sizeof(Z_IORequest));
1469
1470     r->u.itemOrder->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
1471         odr_malloc(out,sizeof(Z_IOOriginPartToKeep));
1472     memset(r->u.itemOrder->u.esRequest->toKeep, 0, sizeof(Z_IOOriginPartToKeep));
1473     r->u.itemOrder->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
1474         odr_malloc(out,sizeof(Z_IOOriginPartNotToKeep));
1475     memset(r->u.itemOrder->u.esRequest->notToKeep, 0, sizeof(Z_IOOriginPartNotToKeep));
1476
1477     r->u.itemOrder->u.esRequest->toKeep->supplDescription = NULL;
1478     r->u.itemOrder->u.esRequest->toKeep->contact = NULL;
1479     r->u.itemOrder->u.esRequest->toKeep->addlBilling = NULL;
1480
1481     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem =
1482         (Z_IOResultSetItem *) odr_malloc(out, sizeof(Z_IOResultSetItem));
1483     memset(r->u.itemOrder->u.esRequest->notToKeep->resultSetItem, 0, sizeof(Z_IOResultSetItem));
1484     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->resultSetId = "1";
1485
1486     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item =
1487         (int *) odr_malloc(out, sizeof(int));
1488     *r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item = itemno;
1489
1490     if (!strcmp (type, "item") || !strcmp(type, "2"))
1491     {
1492         printf ("using item-request\n");
1493         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
1494             create_external_itemRequest();
1495     }
1496     else if (!strcmp(type, "ill") || !strcmp(type, "1"))
1497     {
1498         printf ("using ILL-request\n");
1499         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
1500             create_external_ILL_APDU(ILL_APDU_ILL_Request);
1501     }
1502     else if (!strcmp(type, "xml") || !strcmp(type, "3"))
1503     {
1504     const char *xml_buf =
1505         "<itemorder>\n"
1506         "  <type>request</type>\n"
1507         "  <libraryNo>000200</libraryNo>\n"
1508         "  <borrowerTicketNo> 1212 </borrowerTicketNo>\n"
1509         "</itemorder>";
1510         r->u.itemOrder->u.esRequest->notToKeep->itemRequest =
1511             z_ext_record (out, VAL_TEXT_XML, xml_buf, strlen(xml_buf));
1512     }
1513     else
1514         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 0;
1515
1516     return r;
1517 }
1518
1519 static int send_itemorder(const char *type, int itemno)
1520 {
1521     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest);
1522     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1523     oident ItemOrderRequest;
1524
1525     ItemOrderRequest.proto = PROTO_Z3950;
1526     ItemOrderRequest.oclass = CLASS_EXTSERV;
1527     ItemOrderRequest.value = VAL_ITEMORDER;
1528     req->packageType = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest));
1529     req->packageName = esPackageName;
1530
1531     req->taskSpecificParameters = create_ItemOrderExternal(type, itemno);
1532
1533     send_apdu(apdu);
1534     return 0;
1535 }
1536
1537 static int cmd_update(char *arg)
1538 {
1539     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest );
1540     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1541     Z_External *r;
1542     int oid[OID_SIZE];
1543     Z_IUOriginPartToKeep *toKeep;
1544     Z_IUSuppliedRecords *notToKeep;
1545     oident update_oid;
1546     char action[20], recid[20], fname[80];
1547     int action_no;
1548     Z_External *record_this = 0;
1549
1550     *action = 0;
1551     *recid = 0;
1552     *fname = 0;
1553     sscanf (arg, "%19s %19s %79s", action, recid, fname);
1554
1555     if (!strcmp (action, "insert"))
1556         action_no = Z_IUOriginPartToKeep_recordInsert;
1557     else if (!strcmp (action, "replace"))
1558         action_no = Z_IUOriginPartToKeep_recordReplace;
1559     else if (!strcmp (action, "delete"))
1560         action_no = Z_IUOriginPartToKeep_recordDelete;
1561     else if (!strcmp (action, "update"))
1562         action_no = Z_IUOriginPartToKeep_specialUpdate;
1563     else 
1564     {
1565         printf ("Bad action: %s\n", action);
1566         printf ("Possible values: insert, replace, delete, update\n");
1567         return 0;
1568     }
1569
1570     if (*fname)
1571     {
1572         FILE *inf;
1573         struct stat status;
1574         stat (fname, &status);
1575         if (S_ISREG(status.st_mode) && (inf = fopen(fname, "r")))
1576         {
1577             size_t len = status.st_size;
1578             char *buf = (char *) xmalloc (len);
1579
1580             fread (buf, 1, len, inf);
1581
1582             fclose (inf);
1583             
1584             record_this = z_ext_record (out, VAL_TEXT_XML, buf, len);
1585             
1586             xfree (buf);
1587         }
1588         else
1589         {
1590             printf ("File %s doesn't exist\n", fname);
1591             return 0;
1592         }
1593     }
1594     else
1595     {
1596         if (!record_last)
1597         {
1598             printf ("No last record (update ignored)\n");
1599             return 0;
1600         }
1601         record_this = record_last;
1602     }
1603
1604     update_oid.proto = PROTO_Z3950;
1605     update_oid.oclass = CLASS_EXTSERV;
1606     update_oid.value = VAL_DBUPDATE;
1607     oid_ent_to_oid (&update_oid, oid);
1608     req->packageType = odr_oiddup(out,oid);
1609     req->packageName = esPackageName;
1610     
1611     req->referenceId = set_refid (out);
1612
1613     r = req->taskSpecificParameters = (Z_External *)
1614         odr_malloc (out, sizeof(*r));
1615     r->direct_reference = odr_oiddup(out,oid);
1616     r->indirect_reference = 0;
1617     r->descriptor = 0;
1618     r->which = Z_External_update;
1619     r->u.update = (Z_IUUpdate *) odr_malloc(out, sizeof(*r->u.update));
1620     r->u.update->which = Z_IUUpdate_esRequest;
1621     r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
1622         odr_malloc(out, sizeof(*r->u.update->u.esRequest));
1623     toKeep = r->u.update->u.esRequest->toKeep = (Z_IUOriginPartToKeep *)
1624         odr_malloc(out, sizeof(*r->u.update->u.esRequest->toKeep));
1625     toKeep->databaseName = databaseNames[0];
1626     toKeep->schema = 0;
1627     toKeep->elementSetName = 0;
1628     toKeep->actionQualifier = 0;
1629     toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
1630     *toKeep->action = action_no;
1631
1632     notToKeep = r->u.update->u.esRequest->notToKeep = (Z_IUSuppliedRecords *)
1633         odr_malloc(out, sizeof(*r->u.update->u.esRequest->notToKeep));
1634     notToKeep->num = 1;
1635     notToKeep->elements = (Z_IUSuppliedRecords_elem **)
1636         odr_malloc(out, sizeof(*notToKeep->elements));
1637     notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
1638         odr_malloc(out, sizeof(**notToKeep->elements));
1639     notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
1640     if (*recid)
1641     {
1642         notToKeep->elements[0]->u.opaque = (Odr_oct *)
1643             odr_malloc (out, sizeof(Odr_oct));
1644         notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
1645         notToKeep->elements[0]->u.opaque->size = strlen(recid);
1646         notToKeep->elements[0]->u.opaque->len = strlen(recid);
1647     }
1648     else
1649         notToKeep->elements[0]->u.opaque = 0;
1650     notToKeep->elements[0]->supplementalId = 0;
1651     notToKeep->elements[0]->correlationInfo = 0;
1652     notToKeep->elements[0]->record = record_this;
1653     
1654     send_apdu(apdu);
1655
1656     return 2;
1657 }
1658
1659 static int cmd_itemorder(char *arg)
1660 {
1661     char type[12];
1662     int itemno;
1663     
1664     if (sscanf (arg, "%10s %d", type, &itemno) != 2)
1665         return 0;
1666
1667     printf("Item order request\n");
1668     fflush(stdout);
1669     send_itemorder(type, itemno);
1670     return 2;
1671 }
1672
1673 static int cmd_find(char *arg)
1674 {
1675     if (!*arg)
1676     {
1677         printf("Find what?\n");
1678         return 0;
1679     }
1680     if (!conn)
1681     {
1682                 try_reconnect(); 
1683
1684                 if (!conn) {                                    
1685                         printf("Not connected yet\n");
1686                         return 0;
1687                 };
1688     }
1689     if (!send_searchRequest(arg))
1690         return 0;
1691     return 2;
1692 }
1693
1694 static int cmd_delete(char *arg)
1695 {
1696     if (!conn)
1697     {
1698         printf("Not connected yet\n");
1699         return 0;
1700     }
1701     if (!send_deleteResultSetRequest(arg))
1702         return 0;
1703     return 2;
1704 }
1705
1706 static int cmd_ssub(char *arg)
1707 {
1708     if (!(smallSetUpperBound = atoi(arg)))
1709         return 0;
1710     return 1;
1711 }
1712
1713 static int cmd_lslb(char *arg)
1714 {
1715     if (!(largeSetLowerBound = atoi(arg)))
1716         return 0;
1717     return 1;
1718 }
1719
1720 static int cmd_mspn(char *arg)
1721 {
1722     if (!(mediumSetPresentNumber = atoi(arg)))
1723         return 0;
1724     return 1;
1725 }
1726
1727 static int cmd_status(char *arg)
1728 {
1729     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
1730     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
1731     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
1732     return 1;
1733 }
1734
1735 static int cmd_setnames(char *arg)
1736 {
1737     if (*arg == '1')         /* enable ? */
1738         setnumber = 0;
1739     else if (*arg == '0')    /* disable ? */
1740         setnumber = -1;
1741     else if (setnumber < 0)  /* no args, toggle .. */
1742         setnumber = 0;
1743     else
1744         setnumber = -1;
1745    
1746     if (setnumber >= 0)
1747         printf("Set numbering enabled.\n");
1748     else
1749         printf("Set numbering disabled.\n");
1750     return 1;
1751 }
1752
1753 /* PRESENT SERVICE ----------------------------- */
1754
1755 static int send_presentRequest(char *arg)
1756 {
1757     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
1758     Z_PresentRequest *req = apdu->u.presentRequest;
1759     Z_RecordComposition compo;
1760     oident prefsyn;
1761     int nos = 1;
1762     int oid[OID_SIZE];
1763     char *p;
1764     char setstring[100];
1765
1766     req->referenceId = set_refid (out);
1767     if ((p = strchr(arg, '+')))
1768     {
1769         nos = atoi(p + 1);
1770         *p = 0;
1771     }
1772     if (*arg)
1773         setno = atoi(arg);
1774     if (p && (p=strchr(p+1, '+')))
1775     {
1776         strcpy (setstring, p+1);
1777         req->resultSetId = setstring;
1778     }
1779     else if (setnumber >= 0)
1780     {
1781         sprintf(setstring, "%d", setnumber);
1782         req->resultSetId = setstring;
1783     }
1784     req->resultSetStartPoint = &setno;
1785     req->numberOfRecordsRequested = &nos;
1786     prefsyn.proto = protocol;
1787     prefsyn.oclass = CLASS_RECSYN;
1788     prefsyn.value = recordsyntax;
1789     req->preferredRecordSyntax =
1790         odr_oiddup (out, oid_ent_to_oid(&prefsyn, oid));
1791
1792     if (schema != VAL_NONE)
1793     {
1794         oident prefschema;
1795
1796         prefschema.proto = protocol;
1797         prefschema.oclass = CLASS_SCHEMA;
1798         prefschema.value = schema;
1799
1800         req->recordComposition = &compo;
1801         compo.which = Z_RecordComp_complex;
1802         compo.u.complex = (Z_CompSpec *)
1803             odr_malloc(out, sizeof(*compo.u.complex));
1804         compo.u.complex->selectAlternativeSyntax = (bool_t *) 
1805             odr_malloc(out, sizeof(bool_t));
1806         *compo.u.complex->selectAlternativeSyntax = 0;
1807
1808         compo.u.complex->generic = (Z_Specification *)
1809             odr_malloc(out, sizeof(*compo.u.complex->generic));
1810         compo.u.complex->generic->schema = (Odr_oid *)
1811             odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1812         if (!compo.u.complex->generic->schema)
1813         {
1814             /* OID wasn't a schema! Try record syntax instead. */
1815             prefschema.oclass = CLASS_RECSYN;
1816             compo.u.complex->generic->schema = (Odr_oid *)
1817                 odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1818         }
1819         if (!elementSetNames)
1820             compo.u.complex->generic->elementSpec = 0;
1821         else
1822         {
1823             compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
1824                 odr_malloc(out, sizeof(Z_ElementSpec));
1825             compo.u.complex->generic->elementSpec->which =
1826                 Z_ElementSpec_elementSetName;
1827             compo.u.complex->generic->elementSpec->u.elementSetName =
1828                 elementSetNames->u.generic;
1829         }
1830         compo.u.complex->num_dbSpecific = 0;
1831         compo.u.complex->dbSpecific = 0;
1832         compo.u.complex->num_recordSyntax = 0;
1833         compo.u.complex->recordSyntax = 0;
1834     }
1835     else if (elementSetNames)
1836     {
1837         req->recordComposition = &compo;
1838         compo.which = Z_RecordComp_simple;
1839         compo.u.simple = elementSetNames;
1840     }
1841     send_apdu(apdu);
1842     printf("Sent presentRequest (%d+%d).\n", setno, nos);
1843     return 2;
1844 }
1845     
1846 static void close_session (void)
1847 {
1848     cs_close (conn);
1849     conn = 0;
1850     if (session_mem)
1851     {
1852         nmem_destroy (session_mem);
1853         session_mem = NULL;
1854     }
1855     sent_close = 0;
1856 }
1857
1858 void process_close(Z_Close *req)
1859 {
1860     Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
1861     Z_Close *res = apdu->u.close;
1862
1863     static char *reasons[] =
1864     {
1865         "finished",
1866         "shutdown",
1867         "system problem",
1868         "cost limit reached",
1869         "resources",
1870         "security violation",
1871         "protocolError",
1872         "lack of activity",
1873         "peer abort",
1874         "unspecified"
1875     };
1876
1877     printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
1878         req->diagnosticInformation ? req->diagnosticInformation : "NULL");
1879     if (sent_close)
1880         close_session ();
1881     else
1882     {
1883         *res->closeReason = Z_Close_finished;
1884         send_apdu(apdu);
1885         printf("Sent response.\n");
1886         sent_close = 1;
1887     }
1888 }
1889
1890 static int cmd_show(char *arg)
1891 {
1892     if (!conn)
1893     {
1894         printf("Not connected yet\n");
1895         return 0;
1896     }
1897     if (!send_presentRequest(arg))
1898         return 0;
1899     return 2;
1900 }
1901
1902 int cmd_quit(char *arg)
1903 {
1904     printf("See you later, alligator.\n");
1905     xmalloc_trav ("");
1906     exit(0);
1907     return 0;
1908 }
1909
1910 int cmd_cancel(char *arg)
1911 {
1912     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
1913     Z_TriggerResourceControlRequest *req =
1914         apdu->u.triggerResourceControlRequest;
1915     bool_t rfalse = 0;
1916     
1917     if (!conn)
1918     {
1919         printf("Session not initialized yet\n");
1920         return 0;
1921     }
1922     if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
1923     {
1924         printf("Target doesn't support cancel (trigger resource ctrl)\n");
1925         return 0;
1926     }
1927     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
1928     req->resultSetWanted = &rfalse;
1929
1930     send_apdu(apdu);
1931     printf("Sent cancel request\n");
1932     return 2;
1933 }
1934
1935 int send_scanrequest(const char *query, int pp, int num, const char *term)
1936 {
1937     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
1938     Z_ScanRequest *req = apdu->u.scanRequest;
1939     int oid[OID_SIZE];
1940     
1941     if (queryType == QueryType_CCL2RPN)
1942     {
1943         oident bib1;
1944         int error, pos;
1945         struct ccl_rpn_node *rpn;
1946
1947         rpn = ccl_find_str (bibset,  query, &error, &pos);
1948         if (error)
1949         {
1950             printf("CCL ERROR: %s\n", ccl_err_msg(error));
1951             return -1;
1952         }
1953         bib1.proto = PROTO_Z3950;
1954         bib1.oclass = CLASS_ATTSET;
1955         bib1.value = VAL_BIB1;
1956         req->attributeSet = oid_ent_to_oid (&bib1, oid);
1957         if (!(req->termListAndStartPoint = ccl_scan_query (out, rpn)))
1958         {
1959             printf("Couldn't convert CCL to Scan term\n");
1960             return -1;
1961         }
1962         ccl_rpn_delete (rpn);
1963     }
1964     else
1965     {
1966         YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
1967
1968         if (!(req->termListAndStartPoint =
1969               yaz_pqf_scan(pqf_parser, out, &req->attributeSet, query)))
1970         {
1971             const char *pqf_msg;
1972             size_t off;
1973             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1974             printf("%*s^\n", off+7, "");
1975             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
1976             yaz_pqf_destroy (pqf_parser);
1977             return -1;
1978         }
1979         yaz_pqf_destroy (pqf_parser);
1980     }
1981     if (term && *term)
1982     {
1983         if (req->termListAndStartPoint->term &&
1984             req->termListAndStartPoint->term->which == Z_Term_general &&
1985             req->termListAndStartPoint->term->u.general)
1986         {
1987             req->termListAndStartPoint->term->u.general->buf =
1988                 (unsigned char *) odr_strdup(out, term);
1989             req->termListAndStartPoint->term->u.general->len =
1990                 req->termListAndStartPoint->term->u.general->size =
1991                 strlen(term);
1992         }
1993     }
1994     req->referenceId = set_refid (out);
1995     req->num_databaseNames = num_databaseNames;
1996     req->databaseNames = databaseNames;
1997     req->numberOfTermsRequested = &num;
1998     req->preferredPositionInResponse = &pp;
1999     send_apdu(apdu);
2000     return 2;
2001 }
2002
2003 int send_sortrequest(char *arg, int newset)
2004 {
2005     Z_APDU *apdu = zget_APDU(out, Z_APDU_sortRequest);
2006     Z_SortRequest *req = apdu->u.sortRequest;
2007     Z_SortKeySpecList *sksl = (Z_SortKeySpecList *)
2008         odr_malloc (out, sizeof(*sksl));
2009     char setstring[32];
2010
2011     if (setnumber >= 0)
2012         sprintf (setstring, "%d", setnumber);
2013     else
2014         sprintf (setstring, "default");
2015
2016     req->referenceId = set_refid (out);
2017
2018     req->num_inputResultSetNames = 1;
2019     req->inputResultSetNames = (Z_InternationalString **)
2020         odr_malloc (out, sizeof(*req->inputResultSetNames));
2021     req->inputResultSetNames[0] = odr_strdup (out, setstring);
2022
2023     if (newset && setnumber >= 0)
2024         sprintf (setstring, "%d", ++setnumber);
2025
2026     req->sortedResultSetName = odr_strdup (out, setstring);
2027
2028     req->sortSequence = yaz_sort_spec (out, arg);
2029     if (!req->sortSequence)
2030     {
2031         printf ("Missing sort specifications\n");
2032         return -1;
2033     }
2034     send_apdu(apdu);
2035     return 2;
2036 }
2037
2038 void display_term(Z_TermInfo *t)
2039 {
2040     if (t->term->which == Z_Term_general)
2041     {
2042         printf("%.*s", t->term->u.general->len, t->term->u.general->buf);
2043         sprintf(last_scan_line, "%.*s", t->term->u.general->len,
2044             t->term->u.general->buf);
2045     }
2046     else
2047         printf("Term (not general)");
2048     if (t->globalOccurrences)
2049         printf (" (%d)\n", *t->globalOccurrences);
2050     else
2051         printf ("\n");
2052 }
2053
2054 void process_scanResponse(Z_ScanResponse *res)
2055 {
2056     int i;
2057     Z_Entry **entries = NULL;
2058     int num_entries = 0;
2059    
2060     printf("Received ScanResponse\n"); 
2061     print_refid (res->referenceId);
2062     printf("%d entries", *res->numberOfEntriesReturned);
2063     if (res->positionOfTerm)
2064         printf (", position=%d", *res->positionOfTerm); 
2065     printf ("\n");
2066     if (*res->scanStatus != Z_Scan_success)
2067         printf("Scan returned code %d\n", *res->scanStatus);
2068     if (!res->entries)
2069         return;
2070     if ((entries = res->entries->entries))
2071         num_entries = res->entries->num_entries;
2072     for (i = 0; i < num_entries; i++)
2073     {
2074         int pos_term = res->positionOfTerm ? *res->positionOfTerm : -1;
2075         if (entries[i]->which == Z_Entry_termInfo)
2076         {
2077             printf("%c ", i + 1 == pos_term ? '*' : ' ');
2078             display_term(entries[i]->u.termInfo);
2079         }
2080         else
2081             display_diagrecs(&entries[i]->u.surrogateDiagnostic, 1);
2082     }
2083     if (res->entries->nonsurrogateDiagnostics)
2084         display_diagrecs (res->entries->nonsurrogateDiagnostics,
2085                           res->entries->num_nonsurrogateDiagnostics);
2086 }
2087
2088 void process_sortResponse(Z_SortResponse *res)
2089 {
2090     printf("Received SortResponse: status=");
2091     switch (*res->sortStatus)
2092     {
2093     case Z_SortStatus_success:
2094         printf ("success"); break;
2095     case Z_SortStatus_partial_1:
2096         printf ("partial"); break;
2097     case Z_SortStatus_failure:
2098         printf ("failure"); break;
2099     default:
2100         printf ("unknown (%d)", *res->sortStatus);
2101     }
2102     printf ("\n");
2103     print_refid (res->referenceId);
2104     if (res->diagnostics)
2105         display_diagrecs(res->diagnostics,
2106                          res->num_diagnostics);
2107 }
2108
2109 void process_deleteResultSetResponse (Z_DeleteResultSetResponse *res)
2110 {
2111     printf("Got deleteResultSetResponse status=%d\n",
2112            *res->deleteOperationStatus);
2113     if (res->deleteListStatuses)
2114     {
2115         int i;
2116         for (i = 0; i < res->deleteListStatuses->num; i++)
2117         {
2118             printf ("%s status=%d\n", res->deleteListStatuses->elements[i]->id,
2119                     *res->deleteListStatuses->elements[i]->status);
2120         }
2121     }
2122 }
2123
2124 int cmd_sort_generic(char *arg, int newset)
2125 {
2126     if (!conn)
2127     {
2128         printf("Session not initialized yet\n");
2129         return 0;
2130     }
2131     if (!ODR_MASK_GET(session->options, Z_Options_sort))
2132     {
2133         printf("Target doesn't support sort\n");
2134         return 0;
2135     }
2136     if (*arg)
2137     {
2138         if (send_sortrequest(arg, newset) < 0)
2139             return 0;
2140         return 2;
2141     }
2142     return 0;
2143 }
2144
2145 int cmd_sort(char *arg)
2146 {
2147     return cmd_sort_generic (arg, 0);
2148 }
2149
2150 int cmd_sort_newset (char *arg)
2151 {
2152     return cmd_sort_generic (arg, 1);
2153 }
2154
2155 int cmd_scan(char *arg)
2156 {
2157     if (!conn)
2158     {
2159                 try_reconnect();
2160                 
2161                 if (!conn) {                                                            
2162                         printf("Session not initialized yet\n");
2163                         return 0;
2164                 };
2165     }
2166     if (!ODR_MASK_GET(session->options, Z_Options_scan))
2167     {
2168         printf("Target doesn't support scan\n");
2169         return 0;
2170     }
2171     if (*arg)
2172     {
2173         strcpy (last_scan_query, arg);
2174         if (send_scanrequest(arg, 1, 20, 0) < 0)
2175             return 0;
2176     }
2177     else
2178     {
2179         if (send_scanrequest(last_scan_query, 1, 20, last_scan_line) < 0)
2180             return 0;
2181     }
2182     return 2;
2183 }
2184
2185 int cmd_schema(char *arg)
2186 {
2187     if (!arg || !*arg)
2188     {
2189         schema = VAL_NONE;
2190         return 1;
2191     }
2192     schema = oid_getvalbyname (arg);
2193     if (schema == VAL_NONE)
2194     {
2195         printf ("unknown schema\n");
2196         return 0;
2197     }
2198     return 1;
2199 }
2200
2201 int cmd_format(char *arg)
2202 {
2203     if (!arg || !*arg)
2204     {
2205         printf("Usage: format <recordsyntax>\n");
2206         return 0;
2207     }
2208     recordsyntax = oid_getvalbyname (arg);
2209     if (recordsyntax == VAL_NONE)
2210     {
2211         printf ("unknown record syntax\n");
2212         return 0;
2213     }
2214     return 1;
2215 }
2216
2217 int cmd_elements(char *arg)
2218 {
2219     static Z_ElementSetNames esn;
2220     static char what[100];
2221
2222     if (!arg || !*arg)
2223     {
2224         elementSetNames = 0;
2225         return 1;
2226     }
2227     strcpy(what, arg);
2228     esn.which = Z_ElementSetNames_generic;
2229     esn.u.generic = what;
2230     elementSetNames = &esn;
2231     return 1;
2232 }
2233
2234 int cmd_attributeset(char *arg)
2235 {
2236     char what[100];
2237
2238     if (!arg || !*arg)
2239     {
2240         printf("Usage: attributeset <setname>\n");
2241         return 0;
2242     }
2243     sscanf(arg, "%s", what);
2244     if (p_query_attset (what))
2245     {
2246         printf("Unknown attribute set name\n");
2247         return 0;
2248     }
2249     return 1;
2250 }
2251
2252 int cmd_querytype (char *arg)
2253 {
2254     if (!strcmp (arg, "ccl"))
2255         queryType = QueryType_CCL;
2256     else if (!strcmp (arg, "prefix") || !strcmp(arg, "rpn"))
2257         queryType = QueryType_Prefix;
2258     else if (!strcmp (arg, "ccl2rpn") || !strcmp (arg, "cclrpn"))
2259         queryType = QueryType_CCL2RPN;
2260     else
2261     {
2262         printf ("Querytype must be one of:\n");
2263         printf (" prefix         - Prefix query\n");
2264         printf (" ccl            - CCL query\n");
2265         printf (" ccl2rpn        - CCL query converted to RPN\n");
2266         return 0;
2267     }
2268     return 1;
2269 }
2270
2271 int cmd_refid (char *arg)
2272 {
2273     xfree (refid);
2274     refid = NULL;
2275     if (*arg)
2276     {
2277         refid = (char *) xmalloc (strlen(arg)+1);
2278         strcpy (refid, arg);
2279     }
2280     return 1;
2281 }
2282
2283 int cmd_close(char *arg)
2284 {
2285     Z_APDU *apdu;
2286     Z_Close *req;
2287     if (!conn)
2288         return 0;
2289
2290     apdu = zget_APDU(out, Z_APDU_close);
2291     req = apdu->u.close;
2292     *req->closeReason = Z_Close_finished;
2293     send_apdu(apdu);
2294     printf("Sent close request.\n");
2295     sent_close = 1;
2296     return 2;
2297 }
2298
2299 int cmd_packagename(char* arg)
2300 {
2301     xfree (esPackageName);
2302     esPackageName = NULL;
2303     if (*arg)
2304     {
2305         esPackageName = (char *) xmalloc (strlen(arg)+1);
2306         strcpy (esPackageName, arg);
2307     }
2308     return 1;
2309 }
2310
2311 int cmd_proxy(char* arg)
2312 {
2313     if (*arg == '\0') {
2314                 xfree (yazProxy);
2315                 yazProxy = NULL;
2316         
2317     }
2318     xfree (yazProxy);
2319     yazProxy = NULL;
2320     if (*arg)
2321     {
2322         yazProxy = (char *) xmalloc (strlen(arg)+1);
2323         strcpy (yazProxy, arg);
2324     } 
2325     return 1;
2326 }
2327
2328 int cmd_charset(char* arg)
2329 {
2330     char l1[30], l2[30];
2331
2332     *l1 = *l2 = 0;
2333     if (sscanf(arg, "%29s %29s", l1, l2) < 1)
2334     {
2335         printf("Current character set is `%s'\n", (yazCharset) ? yazCharset:NULL);
2336         return 1;
2337     }
2338     xfree (yazCharset);
2339     yazCharset = NULL;
2340     if (*l1)
2341         yazCharset = xstrdup(l1);
2342     if (*l2)
2343     {
2344         odr_set_charset (out, l1, l2);
2345         odr_set_charset (in, l2, l1);
2346     }
2347     return 1;
2348 }
2349
2350 int cmd_lang(char* arg)
2351 {
2352     if (*arg == '\0') {
2353         printf("Current language is `%s'\n", (yazLang)?yazLang:NULL);
2354         return 1;
2355     }
2356     xfree (yazLang);
2357     yazLang = NULL;
2358     if (*arg)
2359     {
2360         yazLang = (char *) xmalloc (strlen(arg)+1);
2361         strcpy (yazLang, arg);
2362     } 
2363     return 1;
2364 }
2365
2366 int cmd_source(char* arg) 
2367 {
2368     /* first should open the file and read one line at a time.. */
2369     FILE* includeFile;
2370     char line[1024], *cp;
2371
2372     {
2373         char* args_end=(arg)+strlen(arg)-1; 
2374         while(isspace(*args_end)) 
2375         {*args_end=0;
2376         --args_end;}; 
2377     }
2378
2379     REMOVE_TAILING_BLANKS(arg);
2380     
2381     if(strlen(arg)<1) {
2382         fprintf(stderr,"Error in source command use a filename\n");
2383         return -1;
2384     }
2385     
2386     includeFile = fopen (arg, "r");
2387     
2388     if(!includeFile) {
2389         fprintf(stderr,"Unable to open file %s for reading\n",arg);
2390         return -1;
2391     }
2392     
2393     while(!feof(includeFile)) {
2394         memset(line,0,sizeof(line));
2395         fgets(line,sizeof(line),includeFile);
2396         
2397         if(strlen(line) < 2) continue;
2398         if(line[0] == '#') continue;
2399         
2400         if ((cp = strrchr (line, '\n')))
2401             *cp = '\0';
2402         
2403         process_cmd_line(line);
2404     }
2405     
2406     if(fclose(includeFile)<0) {
2407         perror("unable to close include file");
2408         exit(1);
2409     }
2410     return 1;
2411 }
2412
2413 int cmd_subshell(char* args)
2414 {
2415     if(strlen(args)) 
2416         system(args);
2417     else 
2418         system(getenv("SHELL"));
2419     
2420     printf("\n");
2421     return 1;
2422 }
2423
2424 int cmd_set_apdufile(char* arg)
2425 {
2426     REMOVE_TAILING_BLANKS(arg);
2427   
2428     if(apdu_file && apdu_file != stderr) { /* don't close stdout*/
2429         perror("unable to close apdu log file");      
2430     }
2431     apdu_file=NULL;
2432   
2433     if(strlen(arg)<1) {
2434         return 1;
2435     }
2436   
2437     if(!strcmp(arg,"-")) 
2438         apdu_file=stderr;      
2439     else 
2440         apdu_file=fopen(arg, "a");
2441   
2442     if(!apdu_file) {
2443         perror("unable to open apdu log file no apdu log loaded");
2444     } else {
2445         odr_setprint(print, apdu_file); 
2446     }
2447   
2448     return 1;
2449 }
2450
2451 int cmd_set_cclfile(char* arg)
2452 {  
2453     FILE *inf;
2454
2455     REMOVE_TAILING_BLANKS(arg);
2456
2457     bibset = ccl_qual_mk (); 
2458     inf = fopen (arg, "r");
2459     if (inf)
2460     {
2461         ccl_qual_file (bibset, inf);
2462         fclose (inf);
2463     }
2464     strcpy(ccl_fields,arg);
2465     return 0;
2466 }
2467
2468
2469 int cmd_set_auto_reconnect(char* arg)
2470 {  
2471     REMOVE_TAILING_BLANKS(arg);
2472     
2473     if(strlen(arg)==0) {
2474         auto_reconnect = ! auto_reconnect;
2475     } else if(strcmp(arg,"on")==0) {
2476         auto_reconnect = 1;
2477     } else if(strcmp(arg,"off")==0) {
2478         auto_reconnect = 0;             
2479     } else {
2480         printf("Error use on or off\n");
2481         return 1;
2482     }
2483     
2484     if (auto_reconnect)
2485         printf("Set auto reconnect enabled.\n");
2486     else
2487         printf("Set auto reconnect disabled.\n");
2488     
2489     return 0;
2490 }
2491
2492 int cmd_set_marcdump(char* arg)
2493 {
2494     if(marcdump && marcdump != stderr) { /* don't close stdout*/
2495         perror("unable to close apdu log file");      
2496     }
2497     marcdump=NULL;
2498     
2499     if(strlen(arg)<1) {
2500         return 1;
2501     }
2502     
2503     if(!strcmp(arg,"-")) 
2504         marcdump=stderr;      
2505     else 
2506         marcdump=fopen(arg, "a");
2507     
2508     if(!marcdump) {
2509         perror("unable to open apdu marcdump file no marcdump done\n");
2510     }
2511     
2512     return 1;
2513 }
2514
2515 int cmd_set_proxy(char* arg)
2516 {
2517     if(yazProxy) free(yazProxy);
2518     yazProxy=NULL;
2519     
2520     if(strlen(arg) > 1) {
2521         yazProxy=strdup(arg);
2522     }
2523     return 1;
2524 }
2525
2526 /* 
2527    this command takes 3 arge {name class oid} 
2528 */
2529 int cmd_register_oid(char* args) {
2530     static struct {
2531         char* className;
2532         oid_class oclass;
2533     } oid_classes[] = {
2534         {"appctx",CLASS_APPCTX},
2535         {"absyn",CLASS_ABSYN},
2536         {"attset",CLASS_ATTSET},
2537         {"transyn",CLASS_TRANSYN},
2538         {"diagset",CLASS_DIAGSET},
2539         {"recsyn",CLASS_RECSYN},
2540         {"resform",CLASS_RESFORM},
2541         {"accform",CLASS_ACCFORM},
2542         {"extserv",CLASS_EXTSERV},
2543         {"userinfo",CLASS_USERINFO},
2544         {"elemspec",CLASS_ELEMSPEC},
2545         {"varset",CLASS_VARSET},
2546         {"schema",CLASS_SCHEMA},
2547         {"tagset",CLASS_TAGSET},
2548         {"general",CLASS_GENERAL},
2549         {0,(enum oid_class) 0}
2550     };
2551     char oname_str[101], oclass_str[101], oid_str[101];  
2552     char* name;
2553     int i;
2554     oid_class oidclass = CLASS_GENERAL;
2555     int val = 0, oid[OID_SIZE];
2556     struct oident * new_oident=NULL;
2557     
2558     if (sscanf (args, "%100[^ ] %100[^ ] %100s",
2559                 oname_str,oclass_str, oid_str) < 1) {
2560         printf("Error in regristrate command \n");
2561         return 0;
2562     }
2563     
2564     for (i = 0; oid_classes[i].className; i++) {
2565         if (!strcmp(oid_classes[i].className, oclass_str))
2566         {
2567             oidclass=oid_classes[i].oclass;
2568             break;
2569         }
2570     }
2571     
2572     if(!(oid_classes[i].className)) {
2573         printf("Unknonwn oid class %s\n",oclass_str);
2574         return 0;
2575     }
2576     
2577     i = 0;
2578     name = oid_str;
2579     val = 0;
2580     
2581     while (isdigit (*name))
2582     {
2583         val = val*10 + (*name - '0');
2584         name++;
2585         if (*name == '.')
2586         {
2587             if (i < OID_SIZE-1)
2588                 oid[i++] = val;
2589             val = 0;
2590             name++;
2591         }
2592     }
2593     oid[i] = val;
2594     oid[i+1] = -1;
2595     
2596     new_oident=oid_addent (oid,PROTO_GENERAL,oidclass,oname_str,VAL_DYNAMIC);  
2597     if(strcmp(new_oident->desc,oname_str)) {
2598         fprintf(stderr,"oid is already named as %s, regristration faild\n",
2599                 new_oident->desc);
2600     }
2601     return 1;  
2602 }
2603
2604 int cmd_push_command(char* arg) 
2605 {
2606 #if HAVE_READLINE_HISTORY_H
2607     if(strlen(arg)>1) 
2608         add_history(arg);
2609 #else 
2610     fprintf(stderr,"Not compiled with the readline/history module\n");
2611 #endif
2612     return 1;
2613 }
2614
2615 void source_rcfile() 
2616 {
2617     /*  Look for a $HOME/.yazclientrc and source it if it exists */
2618     struct stat statbuf;
2619     char buffer[1000];
2620     char* homedir=getenv("HOME");
2621     
2622     if(!homedir) return;
2623     
2624     sprintf(buffer,"%s/.yazclientrc",homedir);
2625     
2626     if(stat(buffer,&statbuf)==0) {
2627         cmd_source(buffer);
2628     }
2629     
2630     if(stat(".yazclientrc",&statbuf)==0) {
2631         cmd_source(".yazclientrc");
2632     }
2633 }
2634
2635
2636 static void initialize(void)
2637 {
2638     FILE *inf;
2639     int i;
2640     
2641     if (!(out = odr_createmem(ODR_ENCODE)) ||
2642         !(in = odr_createmem(ODR_DECODE)) ||
2643         !(print = odr_createmem(ODR_PRINT)))
2644     {
2645         fprintf(stderr, "failed to allocate ODR streams\n");
2646         exit(1);
2647     }
2648     oid_init();
2649     
2650     setvbuf(stdout, 0, _IONBF, 0);
2651     if (apdu_file)
2652         odr_setprint(print, apdu_file);
2653
2654     bibset = ccl_qual_mk (); 
2655     inf = fopen (ccl_fields, "r");
2656     if (inf)
2657     {
2658         ccl_qual_file (bibset, inf);
2659         fclose (inf);
2660     }
2661     cmd_base("Default");
2662
2663 #if HAVE_READLINE_READLINE_H
2664     rl_attempted_completion_function = (CPPFunction*)readline_completer;
2665 #endif
2666     
2667     
2668     for(i=0; i<maxOtherInfosSupported; ++i) {
2669         extraOtherInfos[i].oidval = -1;
2670     }
2671     
2672     source_rcfile();
2673 }
2674
2675
2676 #if HAVE_GETTIMEOFDAY
2677 struct timeval tv_start, tv_end;
2678 #endif
2679
2680 void wait_and_handle_responce() 
2681 {
2682     
2683     int res;
2684     char *netbuffer= 0;
2685     int netbufferlen = 0;
2686     Z_APDU *apdu;
2687     
2688     
2689     if (conn)
2690     {
2691         do
2692         {
2693             if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
2694             {
2695                 printf("Target closed connection\n");
2696                 close_session ();
2697                 break;
2698             }
2699             if (!res)
2700             {
2701                 printf("Target closed connection.\n");
2702                 close_session ();
2703                 break;
2704             }
2705             odr_reset(in); /* release APDU from last round */
2706             record_last = 0;
2707             odr_setbuf(in, netbuffer, res, 0);
2708             if (!z_APDU(in, &apdu, 0, 0))
2709             {
2710                 odr_perror(in, "Decoding incoming APDU");
2711                 fprintf(stderr, "[Near %d]\n", odr_offset(in));
2712                 fprintf(stderr, "Packet dump:\n---------\n");
2713                 odr_dumpBER(stderr, netbuffer, res);
2714                 fprintf(stderr, "---------\n");
2715                 if (apdu_file)
2716                     z_APDU(print, &apdu, 0, 0);
2717                 close_session ();
2718                 break;
2719             }
2720             if (apdu_file && !z_APDU(print, &apdu, 0, 0))
2721             {
2722                 odr_perror(print, "Failed to print incoming APDU");
2723                 odr_reset(print);
2724                 continue;
2725             }
2726             switch(apdu->which)
2727             {
2728             case Z_APDU_initResponse:
2729                 process_initResponse(apdu->u.initResponse);
2730                 break;
2731             case Z_APDU_searchResponse:
2732                 process_searchResponse(apdu->u.searchResponse);
2733                 break;
2734             case Z_APDU_scanResponse:
2735                 process_scanResponse(apdu->u.scanResponse);
2736                 break;
2737             case Z_APDU_presentResponse:
2738                 print_refid (apdu->u.presentResponse->referenceId);
2739                 setno +=
2740                     *apdu->u.presentResponse->numberOfRecordsReturned;
2741                 if (apdu->u.presentResponse->records)
2742                     display_records(apdu->u.presentResponse->records);
2743                 else
2744                     printf("No records.\n");
2745                 printf ("nextResultSetPosition = %d\n",
2746                         *apdu->u.presentResponse->nextResultSetPosition);
2747                 break;
2748             case Z_APDU_sortResponse:
2749                 process_sortResponse(apdu->u.sortResponse);
2750                 break;
2751             case Z_APDU_extendedServicesResponse:
2752                 printf("Got extended services response\n");
2753                 process_ESResponse(apdu->u.extendedServicesResponse);
2754                 break;
2755             case Z_APDU_close:
2756                 printf("Target has closed the association.\n");
2757                 process_close(apdu->u.close);
2758                 break;
2759             case Z_APDU_resourceControlRequest:
2760                 process_resourceControlRequest
2761                     (apdu->u.resourceControlRequest);
2762                 break;
2763             case Z_APDU_deleteResultSetResponse:
2764                 process_deleteResultSetResponse(apdu->u.
2765                                                 deleteResultSetResponse);
2766                 break;
2767             default:
2768                 printf("Received unknown APDU type (%d).\n", 
2769                        apdu->which);
2770                 close_session ();
2771             }
2772         }
2773         while (conn && cs_more(conn));
2774 #if HAVE_GETTIMEOFDAY
2775         gettimeofday (&tv_end, 0);
2776 #if 0
2777         printf ("S/U S/U=%ld/%ld %ld/%ld",
2778                 (long) tv_start.tv_sec,
2779                 (long) tv_start.tv_usec,
2780                 (long) tv_end.tv_sec,
2781                 (long) tv_end.tv_usec);
2782 #endif
2783         printf ("Elapsed: %.6f\n",
2784                 (double) tv_end.tv_usec / 1e6 + tv_end.tv_sec -
2785                 ((double) tv_start.tv_usec / 1e6 + tv_start.tv_sec));
2786 #endif
2787     }
2788     xfree (netbuffer);
2789 }
2790
2791
2792 int cmd_cclparse(char* arg) 
2793 {
2794     int error, pos;
2795     struct ccl_rpn_node *rpn=NULL;
2796     
2797     
2798     rpn = ccl_find_str (bibset, arg, &error, &pos);
2799     
2800     if (error) {
2801         printf ("%*s^ - ", 3+strlen(last_cmd)+1+pos, " ");
2802         printf ("%s\n", ccl_err_msg (error));
2803     }
2804     else
2805     {
2806         if (rpn)
2807         {       
2808             ccl_pr_tree(rpn, stdout); 
2809         }
2810     }
2811     if (rpn)
2812         ccl_rpn_delete(rpn);
2813     
2814     printf ("\n");
2815     
2816     return 0;
2817 }
2818
2819
2820 int cmd_set_otherinfo(char* args)
2821 {
2822     char oid[101], otherinfoString[101];
2823     int otherinfoNo;
2824     int sscan_res;
2825     int oidval;
2826     
2827     sscan_res = sscanf (args, "%d %100[^ ] %100s", &otherinfoNo, oid, otherinfoString);
2828     if(sscan_res==1) {
2829         /* reset this otherinfo */
2830         if(otherinfoNo>=maxOtherInfosSupported) {
2831             printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2832         }
2833         extraOtherInfos[otherinfoNo].oidval = -1;
2834         if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value);                        
2835         return 0;
2836     }
2837     if (sscan_res<3) {
2838         printf("Error in set_otherinfo command \n");
2839         return 0;
2840     }
2841     
2842     if(otherinfoNo>=maxOtherInfosSupported) {
2843         printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2844     }
2845     
2846     
2847     oidval = oid_getvalbyname (oid);
2848     if(oidval == -1 ) {
2849         printf("Error in set_otherinfo command unknown oid %s \n",oid);
2850         return 0;
2851     }
2852     extraOtherInfos[otherinfoNo].oidval = oidval;
2853     if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value);
2854     extraOtherInfos[otherinfoNo].value = strdup(otherinfoString);
2855     
2856     return 0;
2857 }
2858
2859 int cmd_list_otherinfo(char* args)
2860 {
2861     int i;         
2862     
2863     if(strlen(args)>0) {
2864         i = atoi(args);
2865         if( i >= maxOtherInfosSupported ) {
2866             printf("Error otherinfo index to large (%d>%d)\n",i,maxOtherInfosSupported);
2867             return 0;
2868         }
2869
2870         if(extraOtherInfos[i].oidval != -1) 
2871             printf("  otherinfo %d %s %s\n",
2872                    i,
2873                    yaz_z3950_oid_value_to_str(
2874                        (enum oid_value) extraOtherInfos[i].oidval,
2875                        CLASS_RECSYN),
2876                    extraOtherInfos[i].value);
2877         
2878     } else {            
2879         for(i=0; i<maxOtherInfosSupported; ++i) {
2880             if(extraOtherInfos[i].oidval != -1) 
2881                 printf("  otherinfo %d %s %s\n",
2882                        i,
2883                        yaz_z3950_oid_value_to_str(
2884                            (enum oid_value) extraOtherInfos[i].oidval,
2885                            CLASS_RECSYN),
2886                        extraOtherInfos[i].value);
2887         }
2888         
2889     }
2890     return 0;
2891 }
2892
2893
2894 int cmd_list_all(char* args) {
2895     int i;
2896     
2897     /* connection options */
2898     if(conn) {
2899         printf("Connected to         : %s\n",last_open_command);
2900     } else {
2901         if(last_open_command) 
2902             printf("Not connected to     : %s\n",last_open_command);
2903         else 
2904             printf("Not connected        : \n");
2905         
2906     }
2907     if(yazProxy) printf("using proxy          : %s\n",yazProxy);                
2908     
2909     printf("auto_reconnect       : %s\n",auto_reconnect?"on":"off");
2910     
2911     if (!auth) {
2912         printf("Authentication       : none\n");
2913     } else {
2914         switch(auth->which) {
2915         case Z_IdAuthentication_idPass:
2916             printf("Authentication       : IdPass\n"); 
2917             printf("    Login User       : %s\n",auth->u.idPass->userId?auth->u.idPass->userId:"");
2918             printf("    Login Group      : %s\n",auth->u.idPass->groupId?auth->u.idPass->groupId:"");
2919             printf("    Password         : %s\n",auth->u.idPass->password?auth->u.idPass->password:"");
2920             break;
2921         case Z_IdAuthentication_open:
2922             printf("Authentication       : psOpen\n");                  
2923             printf("    Open string      : %s\n",auth->u.open); 
2924             break;
2925         default:
2926             printf("Authentication       : Unknown\n");
2927         }
2928     }
2929     if ( yazCharset ) printf("Character set        : `%s'\n", (yazCharset) ? yazCharset:NULL);
2930     
2931     /* bases */
2932     printf("Bases                : ");
2933     for (i = 0; i<num_databaseNames; i++) printf("%s ",databaseNames[i]);
2934     printf("\n");
2935     
2936     /* Query options */
2937     printf("CCL file             : %s\n",ccl_fields);
2938     printf("Query type           : %s\n",query_type_as_string(queryType));
2939     
2940     printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
2941     
2942     /* piggy back options */
2943     printf("ssub/lslb/mspn       : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber);
2944     
2945     /* print present related options */
2946     printf("Format               : %s\n",yaz_z3950_oid_value_to_str(recordsyntax,CLASS_RECSYN));
2947     printf("Schema               : %s\n",yaz_z3950_oid_value_to_str(schema,CLASS_SCHEMA));
2948     printf("Elements             : %s\n",elementSetNames?elementSetNames->u.generic:"");
2949     
2950     /* loging options */
2951     printf("APDU log             : %s\n",apdu_file?"on":"off");
2952     printf("Record log           : %s\n",marcdump?"on":"off");
2953     
2954     /* other infos */
2955     printf("Other Info: \n");
2956     cmd_list_otherinfo("");
2957     
2958     return 0;
2959 }
2960
2961 int cmd_clear_otherinfo(char* args) 
2962 {
2963     if(strlen(args)>0) {
2964         int otherinfoNo;
2965         otherinfoNo = atoi(args);
2966         if( otherinfoNo >= maxOtherInfosSupported ) {
2967             printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2968             return 0;
2969         }
2970         
2971         if(extraOtherInfos[otherinfoNo].oidval != -1) {                 
2972             /* only clear if set. */
2973             extraOtherInfos[otherinfoNo].oidval=-1;
2974             free(extraOtherInfos[otherinfoNo].value);
2975         }
2976     } else {
2977         int i;
2978         
2979         for(i=0; i<maxOtherInfosSupported; ++i) {
2980             if (extraOtherInfos[i].oidval!=-1 ) {                               
2981                 extraOtherInfos[i].oidval=-1;
2982                 free(extraOtherInfos[i].value);
2983             }
2984         }
2985     }
2986     return 0;
2987 }
2988
2989 static int cmd_help (char *line);
2990
2991 typedef char *(*completerFunctionType)(const char *text, int state);
2992
2993 static struct {
2994     char *cmd;
2995     int (*fun)(char *arg);
2996     char *ad;
2997         completerFunctionType rl_completerfunction;
2998     int complete_filenames;
2999     char **local_tabcompletes;
3000 } cmd[] = {
3001     {"open", cmd_open, "('tcp'|'ssl')':<host>[':'<port>][/<db>]",NULL,0,NULL},
3002     {"quit", cmd_quit, "",NULL,0,NULL},
3003     {"find", cmd_find, "<query>",NULL,0,NULL},
3004     {"delete", cmd_delete, "<setname>",NULL,0,NULL},
3005     {"base", cmd_base, "<base-name>",NULL,0,NULL},
3006     {"show", cmd_show, "<rec#>['+'<#recs>['+'<setname>]]",NULL,0,NULL},
3007     {"scan", cmd_scan, "<term>",NULL,0,NULL},
3008     {"sort", cmd_sort, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
3009     {"sort+", cmd_sort_newset, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
3010     {"authentication", cmd_authentication, "<acctstring>",NULL,0,NULL},
3011     {"lslb", cmd_lslb, "<largeSetLowerBound>",NULL,0,NULL},
3012     {"ssub", cmd_ssub, "<smallSetUpperBound>",NULL,0,NULL},
3013     {"mspn", cmd_mspn, "<mediumSetPresentNumber>",NULL,0,NULL},
3014     {"status", cmd_status, "",NULL,0,NULL},
3015     {"setnames", cmd_setnames, "",NULL,0,NULL},
3016     {"cancel", cmd_cancel, "",NULL,0,NULL},
3017     {"format", cmd_format, "<recordsyntax>",complete_format,0,NULL},
3018     {"schema", cmd_schema, "<schema>",complete_schema,0,NULL},
3019     {"elements", cmd_elements, "<elementSetName>",NULL,0,NULL},
3020     {"close", cmd_close, "",NULL,0,NULL},
3021     {"attributeset", cmd_attributeset, "<attrset>",complete_attributeset,0,NULL},
3022     {"querytype", cmd_querytype, "<type>",complete_querytype,0,NULL},
3023     {"refid", cmd_refid, "<id>",NULL,0,NULL},
3024     {"itemorder", cmd_itemorder, "ill|item <itemno>",NULL,0,NULL},
3025     {"update", cmd_update, "<action> <recid> [<file>]",NULL,0,NULL},
3026     {"packagename", cmd_packagename, "<packagename>",NULL,0,NULL},
3027     {"proxy", cmd_proxy, "[('tcp'|'ssl')]<host>[':'<port>]",NULL,0,NULL},
3028     {"charset", cmd_charset, "<charset_name>",NULL,0,NULL},
3029     {"lang", cmd_lang, "<language_code>",NULL,0,NULL},
3030     {".", cmd_source, "<filename>",NULL,1,NULL},
3031     {"!", cmd_subshell, "Subshell command",NULL,1,NULL},
3032     {"set_apdufile", cmd_set_apdufile, "<filename>",NULL,1,NULL},
3033     {"set_marcdump", cmd_set_marcdump," <filename>",NULL,1,NULL},
3034     {"set_cclfile", cmd_set_cclfile," <filename>",NULL,1,NULL},
3035     {"set_auto_reconnect", cmd_set_auto_reconnect," on|off",complete_auto_reconnect,1,NULL},
3036         {"set_otherinfo", cmd_set_otherinfo,"<otherinfoinddex> <oid> <string>",NULL,0,NULL},
3037     {"register_oid", cmd_register_oid,"<name> <class> <oid>",NULL,0,NULL},
3038     {"push_command", cmd_push_command,"<command>",command_generator,0,NULL},
3039         {"register_tab", cmd_register_tab,"<commandname> <tab>",command_generator,0,NULL},
3040         {"cclparse", cmd_cclparse,"<ccl find command>",NULL,0,NULL},
3041         {"list_otherinfo",cmd_list_otherinfo,"[otherinfoinddex]",NULL,0,NULL},
3042         {"list_all",cmd_list_all,"",NULL,0,NULL},
3043         {"clear_otherinfo",cmd_clear_otherinfo,"",NULL,0,NULL},
3044     /* Server Admin Functions */
3045     {"adm-reindex", cmd_adm_reindex, "<database-name>",NULL,0,NULL},
3046     {"adm-truncate", cmd_adm_truncate, "('database'|'index')<object-name>",NULL,0,NULL},
3047     {"adm-create", cmd_adm_create, "",NULL,0,NULL},
3048     {"adm-drop", cmd_adm_drop, "('database'|'index')<object-name>",NULL,0,NULL},
3049     {"adm-import", cmd_adm_import, "<record-type> <dir> <pattern>",NULL,0,NULL},
3050     {"adm-refresh", cmd_adm_refresh, "",NULL,0,NULL},
3051     {"adm-commit", cmd_adm_commit, "",NULL,0,NULL},
3052     {"adm-shutdown", cmd_adm_shutdown, "",NULL,0,NULL},
3053     {"adm-startup", cmd_adm_startup, "",NULL,0,NULL},
3054     {"help", cmd_help, "", NULL,0,NULL},
3055     {0,0,0,0,0,0}
3056 };
3057
3058 static int cmd_help (char *line)
3059 {
3060     int i;
3061     char topic[21];
3062     
3063     *topic = 0;
3064     sscanf (line, "%20s", topic);
3065
3066     if (*topic == 0)
3067         printf("Commands:\n");
3068     for (i = 0; cmd[i].cmd; i++)
3069         if (*topic == 0 || strcmp (topic, cmd[i].cmd) == 0)
3070             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
3071     if (strcmp (topic, "find") == 0)
3072     {
3073         printf ("RPN:\n");
3074         printf (" \"term\"                        Simple Term\n");
3075         printf (" @attr [attset] type=value op  Attribute\n");
3076         printf (" @and opl opr                  And\n");
3077         printf (" @or opl opr                   Or\n");
3078         printf (" @not opl opr                  And-Not\n");
3079         printf (" @set set                      Result set\n");
3080         printf ("\n");
3081         printf ("Bib-1 attribute types\n");
3082         printf ("1=Use:         ");
3083         printf ("4=Title 7=ISBN 8=ISSN 30=Date 62=Abstract 1003=Author 1016=Any\n");
3084         printf ("2=Relation:    ");
3085         printf ("1<   2<=  3=  4>=  5>  6!=  102=Relevance\n");
3086         printf ("3=Position:    ");
3087         printf ("1=First in Field  2=First in subfield  3=Any position\n");
3088         printf ("4=Structure:   ");
3089         printf ("1=Phrase  2=Word  3=Key  4=Year  5=Date  6=WordList\n");
3090         printf ("5=Truncation:  ");
3091         printf ("1=Right  2=Left  3=L&R  100=No  101=#  102=Re-1  103=Re-2\n");
3092         printf ("6=Completeness:");
3093         printf ("1=Incomplete subfield  2=Complete subfield  3=Complete field\n");
3094     }
3095     return 1;
3096 }
3097
3098 int cmd_register_tab(char* arg) {
3099         
3100     char command[101], tabargument[101];
3101     int i;
3102     int num_of_tabs;
3103     char** tabslist;
3104     
3105     if (sscanf (arg, "%100s %100s", command, tabargument) < 1) {
3106         return 0;
3107     }
3108     
3109     /* locate the amdn in the list */
3110     for (i = 0; cmd[i].cmd; i++) {
3111         if (!strncmp(cmd[i].cmd, command, strlen(command))) {
3112             break;
3113         }
3114     }
3115     
3116     if(!cmd[i].cmd) { 
3117         fprintf(stderr,"Unknown command %s\n",command);
3118         return 1;
3119     }
3120     
3121         
3122     if(!cmd[i].local_tabcompletes)
3123         cmd[i].local_tabcompletes = (char **) calloc(1,sizeof(char**));
3124     
3125     num_of_tabs=0;              
3126     
3127     tabslist = cmd[i].local_tabcompletes;
3128     for(;tabslist && *tabslist;tabslist++) {
3129         num_of_tabs++;
3130     }
3131     
3132     cmd[i].local_tabcompletes =  (char **)
3133         realloc(cmd[i].local_tabcompletes,(num_of_tabs+2)*sizeof(char**));
3134     tabslist=cmd[i].local_tabcompletes;
3135     tabslist[num_of_tabs]=strdup(tabargument);
3136     tabslist[num_of_tabs+1]=NULL;
3137     return 1;
3138 }
3139
3140
3141 void process_cmd_line(char* line)
3142 {  
3143     int i,res;
3144     char word[32], arg[1024];
3145     
3146 #if HAVE_GETTIMEOFDAY
3147     gettimeofday (&tv_start, 0);
3148 #endif
3149     
3150     if ((res = sscanf(line, "%31s %1023[^;]", word, arg)) <= 0)
3151     {
3152         strcpy(word, last_cmd);
3153         *arg = '\0';
3154     }
3155     else if (res == 1)
3156         *arg = 0;
3157     strcpy(last_cmd, word);
3158     
3159     /* removed tailing spaces from the arg command */
3160     { 
3161         char* p;
3162         char* lastnonspace=NULL;
3163         p = arg;
3164         
3165         for(;*p; ++p) {
3166             if(!isspace(*p)) {
3167                 lastnonspace = p;
3168             }
3169         }
3170         if(lastnonspace) 
3171             *(++lastnonspace) = 0;
3172     }
3173     
3174     
3175     for (i = 0; cmd[i].cmd; i++)
3176         if (!strncmp(cmd[i].cmd, word, strlen(word)))
3177         {
3178             res = (*cmd[i].fun)(arg);
3179             break;
3180         }
3181     
3182     if (!cmd[i].cmd) /* dump our help-screen */
3183     {
3184         printf("Unknown command: %s.\n", word);
3185         printf("use help for list of commands\n");
3186         /* cmd_help (""); */
3187         res = 1;
3188     }
3189     
3190     if(apdu_file) fflush(apdu_file);
3191     
3192     if (res >= 2)
3193         wait_and_handle_responce();
3194     
3195     if(apdu_file)
3196         fflush(apdu_file);
3197     if(marcdump)
3198         fflush(marcdump);
3199 }
3200
3201
3202 char *command_generator(const char *text, int state) 
3203 {
3204     static int idx; 
3205     if (state==0) {
3206         idx = 0;
3207     }
3208     for( ; cmd[idx].cmd; ++idx) {
3209         if (!strncmp(cmd[idx].cmd,text,strlen(text))) {
3210             ++idx;  /* skip this entry on the next run */
3211             return strdup(cmd[idx-1].cmd);
3212         }
3213     }
3214     return NULL;
3215 }
3216
3217
3218 /* 
3219    This function only known how to complete on the first word
3220 */
3221 char ** readline_completer(char *text, int start, int end) {
3222 #if HAVE_READLINE_READLINE_H
3223
3224         completerFunctionType completerToUse;
3225         
3226     if(start == 0) {
3227 #if HAVE_READLINE_RL_COMPLETION_MATCHES
3228         char** res=rl_completion_matches(text,
3229                                       command_generator); 
3230 #else
3231         char** res=completion_matches(text,
3232                                       (CPFunction*)command_generator); 
3233 #endif
3234         rl_attempted_completion_over = 1;
3235         return res;
3236     } else {
3237         char arg[1024],word[32];
3238         int i=0 ,res;
3239         if ((res = sscanf(rl_line_buffer, "%31s %1023[^;]", word, arg)) <= 0) {     
3240             rl_attempted_completion_over = 1;
3241             return NULL;
3242         }
3243         
3244         for (i = 0; cmd[i].cmd; i++) {
3245             if (!strncmp(cmd[i].cmd, word, strlen(word))) {
3246                 break;
3247             }
3248         }
3249         
3250         if(!cmd[i].cmd) return NULL;
3251         
3252         
3253         curret_global_list = cmd[i].local_tabcompletes;
3254         
3255         completerToUse = cmd[i].rl_completerfunction;
3256         if(completerToUse==NULL)  /* if no pr. command completer is defined use the default completer */
3257             completerToUse = default_completer;
3258         
3259         if(completerToUse) {
3260 #ifdef HAVE_READLINE_RL_COMPLETION_MATCHES
3261             char** res=
3262                 rl_completion_matches(text,
3263                                       completerToUse);
3264 #else
3265             char** res=
3266                 completion_matches(text,
3267                                    (CPFunction*)completerToUse);
3268 #endif
3269             if(!cmd[i].complete_filenames) 
3270                 rl_attempted_completion_over = 1;
3271             return res;
3272         } else {
3273             if(!cmd[i].complete_filenames) 
3274                 rl_attempted_completion_over = 1;
3275             return 0;
3276         }
3277     }
3278 #else 
3279     return 0;
3280 #endif 
3281 }
3282
3283
3284 static void client(void)
3285 {
3286     char line[1024];
3287
3288     line[1023] = '\0';
3289
3290 #if HAVE_GETTIMEOFDAY
3291     gettimeofday (&tv_start, 0);
3292 #endif
3293
3294     while (1)
3295     {
3296         char *line_in = NULL;
3297 #if HAVE_READLINE_READLINE_H
3298         if (isatty(0))
3299         {
3300             line_in=readline(C_PROMPT);
3301             if (!line_in)
3302                 break;
3303 #if HAVE_READLINE_HISTORY_H
3304             if (*line_in)
3305                 add_history(line_in);
3306 #endif
3307             strncpy(line, line_in, 1023);
3308             free (line_in);
3309         }
3310 #endif 
3311         if (!line_in)
3312         {
3313             char *end_p;
3314             printf (C_PROMPT);
3315             fflush(stdout);
3316             if (!fgets(line, 1023, stdin))
3317                 break;
3318             if ((end_p = strchr (line, '\n')))
3319                 *end_p = '\0';
3320         }
3321         process_cmd_line(line);
3322     }
3323 }
3324
3325 int main(int argc, char **argv)
3326 {
3327     char *prog = *argv;
3328     char *open_command = 0;
3329     char *auth_command = 0;
3330     char *arg;
3331     int ret;
3332     
3333     while ((ret = options("k:c:a:m:v:p:u:", argv, argc, &arg)) != -2)
3334     {
3335         switch (ret)
3336         {
3337         case 0:
3338             if (!open_command)
3339             {
3340                 open_command = (char *) xmalloc (strlen(arg)+6);
3341                 strcpy (open_command, "open ");
3342                 strcat (open_command, arg);
3343             }
3344             break;
3345         case 'k':
3346             kilobytes = atoi(arg);
3347             break;
3348         case 'm':
3349             if (!(marcdump = fopen (arg, "a")))
3350             {
3351                 perror (arg);
3352                 exit (1);
3353             }
3354             break;
3355                 case 'c':
3356                         strncpy (ccl_fields, arg, sizeof(ccl_fields)-1);
3357                         ccl_fields[sizeof(ccl_fields)-1] = '\0';
3358                         break;
3359         case 'a':
3360             if (!strcmp(arg, "-"))
3361                 apdu_file=stderr;
3362             else
3363                 apdu_file=fopen(arg, "a");
3364             break;
3365                 case 'p':
3366                         yazProxy=strdup(arg);
3367                         break;
3368         case 'u':
3369             if (!auth_command)
3370             {
3371                 auth_command = (char *) xmalloc (strlen(arg)+6);
3372                 strcpy (auth_command, "auth ");
3373                 strcat (auth_command, arg);
3374             }
3375             break;
3376         case 'v':
3377             yaz_log_init (yaz_log_mask_str(arg), "", NULL);
3378             break;
3379         default:
3380             fprintf (stderr, "Usage: %s [-m <marclog>] [ -a <apdulog>] "
3381                      "[-c cclfields]\n      [-p <proxy-addr>] [-u <auth>] "
3382                      "[-k size] [<server-addr>]\n",
3383                      prog);
3384             exit (1);
3385         }      
3386     }
3387     initialize();
3388     if (auth_command)
3389     {
3390 #ifdef HAVE_GETTIMEOFDAY
3391         gettimeofday (&tv_start, 0);
3392 #endif
3393         process_cmd_line (auth_command);
3394 #if HAVE_READLINE_HISTORY_H
3395         add_history(auth_command);
3396 #endif
3397         xfree(auth_command);
3398     }
3399     if (open_command)
3400     {
3401 #ifdef HAVE_GETTIMEOFDAY
3402         gettimeofday (&tv_start, 0);
3403 #endif
3404         process_cmd_line (open_command);
3405 #if HAVE_READLINE_HISTORY_H
3406         add_history(open_command);
3407 #endif
3408         xfree(open_command);
3409     }
3410     client ();
3411     exit (0);
3412 }
3413
3414 /*
3415  * Local variables:
3416  * tab-width: 8
3417  * c-basic-offset: 4
3418  * End:
3419  * vim600: sw=4 ts=8 fdm=marker
3420  * vim<600: sw=4 ts=8
3421  */