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