Fixed bug #668: save command line history for yaz-client. The history
[yaz-moved-to-github.git] / client / client.c
1 /* 
2  * Copyright (C) 1995-2007, Index Data ApS
3  * See the file LICENSE for details.
4  *
5  * $Id: client.c,v 1.325 2007-01-24 11:50:18 adam Exp $
6  */
7 /** \file client.c
8  *  \brief yaz-client program
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <assert.h>
14 #include <time.h>
15 #include <ctype.h>
16 #if HAVE_SYS_TYPES_H
17 #include <sys/types.h>
18 #endif
19 #if HAVE_LOCALE_H
20 #include <locale.h>
21 #endif
22 #if HAVE_LANGINFO_H
23 #include <langinfo.h>
24 #endif
25 #if HAVE_UNISTD_H
26 #include <unistd.h>
27 #endif
28 #if HAVE_SYS_STAT_H
29 #include <sys/stat.h>
30 #endif
31 #if HAVE_SYS_TIME_H
32 #include <sys/time.h>
33 #endif
34
35 #if HAVE_OPENSSL_SSL_H
36 #include <openssl/bio.h>
37 #include <openssl/crypto.h>
38 #include <openssl/x509.h>
39 #include <openssl/pem.h>
40 #include <openssl/ssl.h>
41 #include <openssl/err.h>
42 #endif
43
44 #ifdef WIN32
45 #include <sys/stat.h>
46 #include <io.h>
47 #include <windows.h>
48 #define S_ISREG(x) (x & _S_IFREG)
49 #define S_ISDIR(x) (x & _S_IFDIR)
50 #endif
51
52 #include <yaz/yaz-util.h>
53
54 #include <yaz/comstack.h>
55
56 #include <yaz/proto.h>
57 #include <yaz/marcdisp.h>
58 #include <yaz/diagbib1.h>
59 #include <yaz/otherinfo.h>
60 #include <yaz/charneg.h>
61
62 #include <yaz/pquery.h>
63 #include <yaz/sortspec.h>
64
65 #include <yaz/ill.h>
66 #include <yaz/srw.h>
67 #include <yaz/yaz-ccl.h>
68 #include <yaz/cql.h>
69 #include <yaz/log.h>
70
71 #if HAVE_READLINE_READLINE_H
72 #include <readline/readline.h>
73 #endif
74 #if HAVE_READLINE_HISTORY_H
75 #include <readline/history.h>
76 #endif
77
78
79 #include "admin.h"
80 #include "tabcomplete.h"
81 #include "fhistory.h"
82
83 #define C_PROMPT "Z> "
84
85 static file_history_t file_history = 0;
86
87 static char *sru_method = "soap";
88 static char *codeset = 0;               /* character set for output */
89 static int hex_dump = 0;
90 static char *dump_file_prefix = 0;
91 static ODR out, in, print;              /* encoding and decoding streams */
92 #if YAZ_HAVE_XML2
93 static ODR srw_sr_odr_out = 0;
94 static Z_SRW_PDU *srw_sr = 0;
95 #endif
96 static FILE *apdu_file = 0;
97 static FILE *ber_file = 0;
98 static COMSTACK conn = 0;               /* our z-association */
99 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
100 char *databaseNames[128];
101 int num_databaseNames = 0;
102 static Z_External *record_last = 0;
103 static int setnumber = -1;              /* current result set number */
104 static int smallSetUpperBound = 0;
105 static int largeSetLowerBound = 1;
106 static int mediumSetPresentNumber = 0;
107 static Z_ElementSetNames *elementSetNames = 0; 
108 static int setno = 1;                   /* current set offset */
109 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
110 #define RECORDSYNTAX_MAX 20
111 static enum oid_value recordsyntax_list[RECORDSYNTAX_MAX]  = { VAL_USMARC };
112 static int recordsyntax_size = 1;
113
114 static char *record_schema = 0;
115 static int sent_close = 0;
116 static NMEM session_mem = NULL;         /* memory handle for init-response */
117 static Z_InitResponse *session_initResponse = 0;   /* session parameters */
118 static char last_scan_line[512] = "0";
119 static char last_scan_query[512] = "0";
120 static char ccl_fields[512] = "default.bib";
121 /* ### How can I set this path to use wherever YAZ is installed? */
122 static char cql_fields[512] = "/usr/local/share/yaz/etc/pqf.properties";
123 static char *esPackageName = 0;
124 static char *yazProxy = 0;
125 static int kilobytes = 1024;
126 static char *negotiationCharset = 0;
127 static int  negotiationCharsetRecords = 1;
128 static int  negotiationCharsetVersion = 3;
129 static char *outputCharset = 0;
130 static char *marcCharset = 0;
131 static char* yazLang = 0;
132
133 static char last_cmd[32] = "?";
134 static FILE *marc_file = 0;
135 static char *refid = NULL;
136 static char *last_open_command = NULL;
137 static int auto_reconnect = 0;
138 static int auto_wait = 1;
139 static Odr_bitmask z3950_options;
140 static int z3950_version = 3;
141 static int scan_stepSize = 0;
142 static int scan_position = 1;
143 static int scan_size = 20;
144 static char cur_host[200];
145 static int last_hit_count = 0;
146
147 typedef enum {
148     QueryType_Prefix,
149     QueryType_CCL,
150     QueryType_CCL2RPN,
151     QueryType_CQL,
152     QueryType_CQL2RPN
153 } QueryType;
154
155 static QueryType queryType = QueryType_Prefix;
156
157 static CCL_bibset bibset;               /* CCL bibset handle */
158 static cql_transform_t cqltrans;        /* CQL context-set handle */
159
160 #if HAVE_READLINE_COMPLETION_OVER
161
162 #else
163 /* readline doesn't have this var. Define it ourselves. */
164 int rl_attempted_completion_over = 0;
165 #endif
166
167 /* set this one to 1, to avoid decode of unknown MARCs  */
168 #define AVOID_MARC_DECODE 1
169
170 #define maxOtherInfosSupported 10
171 struct {
172     int oidval;
173     char* value;
174 } extraOtherInfos[maxOtherInfosSupported];
175         
176
177 void process_cmd_line(char* line);
178 #if HAVE_READLINE_READLINE_H
179 char **readline_completer(char *text, int start, int end);
180 #endif
181 static char *command_generator(const char *text, int state);
182 int cmd_register_tab(const char* arg);
183
184 static void close_session (void);
185
186 ODR getODROutputStream(void)
187 {
188     return out;
189 }
190
191 const char* query_type_as_string(QueryType q) 
192 {
193     switch (q) { 
194     case QueryType_Prefix: return "prefix (RPN sent to server)";
195     case QueryType_CCL: return "CCL (CCL sent to server) ";
196     case QueryType_CCL2RPN: return "CCL -> RPN (RPN sent to server)";
197     case QueryType_CQL: return "CQL (CQL sent to server)";
198     case QueryType_CQL2RPN: return "CQL -> RPN (RPN sent to server)";
199     default: 
200         return "unknown Query type internal yaz-client error";
201     }
202 }
203
204 static void do_hex_dump(const char* buf, int len) 
205 {
206     if (hex_dump)
207     {
208         int i,x;
209         for( i=0; i<len ; i=i+16 ) 
210         {                       
211             printf(" %4.4d ",i);
212             for(x=0 ; i+x<len && x<16; ++x) 
213             {
214                 printf("%2.2X ",(unsigned int)((unsigned char)buf[i+x]));
215             }
216             printf("\n");
217         }
218     }
219     if (dump_file_prefix)
220     {
221         static int no = 0;
222         if (++no < 1000 && strlen(dump_file_prefix) < 500)
223         {
224             char fname[1024];
225             FILE *of;
226             sprintf (fname, "%s.%03d.raw", dump_file_prefix, no);
227             of = fopen(fname, "wb");
228             
229             fwrite (buf, 1, len, of);
230             
231             fclose(of);
232         }
233     }
234 }
235
236 void add_otherInfos(Z_APDU *a) 
237 {
238     Z_OtherInformation **oi;
239     int i;
240                 
241     yaz_oi_APDU(a, &oi);
242     for(i=0; i<maxOtherInfosSupported; ++i) 
243     {
244         if(extraOtherInfos[i].oidval != -1) 
245             yaz_oi_set_string_oidval(oi, out, extraOtherInfos[i].oidval,
246                                      1, extraOtherInfos[i].value);
247     }   
248 }
249
250 int send_apdu(Z_APDU *a)
251 {
252     char *buf;
253     int len;
254     
255     add_otherInfos(a);
256     
257     if (apdu_file)
258     {
259         z_APDU(print, &a, 0, 0);
260         odr_reset(print);
261     }
262     if (!z_APDU(out, &a, 0, 0))
263     {
264         odr_perror(out, "Encoding APDU");
265         close_session();
266         return 0;
267     }
268     buf = odr_getbuf(out, &len, 0);
269     if (ber_file)
270         odr_dumpBER(ber_file, buf, len);
271     /* printf ("sending APDU of size %d\n", len); */
272     do_hex_dump(buf, len);
273     if (cs_put(conn, buf, len) < 0)
274     {
275         fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
276         close_session();
277         return 0;
278     }
279     odr_reset(out); /* release the APDU structure  */
280     return 1;
281 }
282
283 static void print_stringn(const unsigned char *buf, size_t len)
284 {
285     size_t i;
286     for (i = 0; i<len; i++)
287         if ((buf[i] <= 126 && buf[i] >= 32) || strchr ("\n\r\t\f", buf[i]))
288             printf ("%c", buf[i]);
289         else
290             printf ("\\X%02X", buf[i]);
291 }
292
293 static void print_refid (Z_ReferenceId *id)
294 {
295     if (id)
296     {
297         printf ("Reference Id: ");
298         print_stringn (id->buf, id->len);
299         printf ("\n");
300     }
301 }
302
303 static Z_ReferenceId *set_refid (ODR out)
304 {
305     Z_ReferenceId *id;
306     if (!refid)
307         return 0;
308     id = (Z_ReferenceId *) odr_malloc (out, sizeof(*id));
309     id->size = id->len = strlen(refid);
310     id->buf = (unsigned char *) odr_malloc (out, id->len);
311     memcpy (id->buf, refid, id->len);
312     return id;
313 }   
314
315 /* INIT SERVICE ------------------------------- */
316
317 static void send_initRequest(const char* type_and_host)
318 {
319     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
320     Z_InitRequest *req = apdu->u.initRequest;
321     int i;
322
323     req->options = &z3950_options;
324
325     ODR_MASK_ZERO(req->protocolVersion);
326     for (i = 0; i<z3950_version; i++)
327         ODR_MASK_SET(req->protocolVersion, i);
328
329     *req->maximumRecordSize = 1024*kilobytes;
330     *req->preferredMessageSize = 1024*kilobytes;
331
332     req->idAuthentication = auth;
333
334     req->referenceId = set_refid (out);
335
336     if (yazProxy && type_and_host) 
337         yaz_oi_set_string_oidval(&req->otherInfo, out, VAL_PROXY,
338         1, type_and_host);
339     
340     if (negotiationCharset || yazLang) {
341         Z_OtherInformation **p;
342         Z_OtherInformationUnit *p0;
343         
344         yaz_oi_APDU(apdu, &p);
345         
346         if ((p0=yaz_oi_update(p, out, NULL, 0, 0))) {
347             ODR_MASK_SET(req->options, Z_Options_negotiationModel);
348             
349             p0->which = Z_OtherInfo_externallyDefinedInfo;
350             p0->information.externallyDefinedInfo =
351                 yaz_set_proposal_charneg_list(out, ",", 
352                                               negotiationCharset, 
353                                               yazLang,
354                                               negotiationCharsetRecords);
355         }
356     }
357     
358     if (send_apdu(apdu))
359         printf("Sent initrequest.\n");
360 }
361
362
363 /* These two are used only from process_initResponse() */
364 static void render_initUserInfo(Z_OtherInformation *ui1);
365 static void render_diag(Z_DiagnosticFormat *diag);
366
367 static void pr_opt(const char *opt, void *clientData)
368 {
369     printf (" %s", opt);
370 }
371
372 static int process_initResponse(Z_InitResponse *res)
373 {
374     int ver = 0;
375     /* save session parameters for later use */
376     session_mem = odr_extract_mem(in);
377     session_initResponse = res;
378
379     for (ver = 0; ver < 8; ver++)
380         if (!ODR_MASK_GET(res->protocolVersion, ver))
381             break;
382
383     if (!*res->result)
384         printf("Connection rejected by v%d target.\n", ver);
385     else
386         printf("Connection accepted by v%d target.\n", ver);
387     if (res->implementationId)
388         printf("ID     : %s\n", res->implementationId);
389     if (res->implementationName)
390         printf("Name   : %s\n", res->implementationName);
391     if (res->implementationVersion)
392         printf("Version: %s\n", res->implementationVersion);
393     if (res->userInformationField)
394     {
395         Z_External *uif = res->userInformationField;
396         if (uif->which == Z_External_userInfo1) {
397             render_initUserInfo(uif->u.userInfo1);
398         } else {
399             printf("UserInformationfield:\n");
400             if (!z_External(print, (Z_External**)&uif, 0, 0))
401             {
402                 odr_perror(print, "Printing userinfo\n");
403                 odr_reset(print);
404             }
405             if (uif->which == Z_External_octet) {
406                 printf("Guessing visiblestring:\n");
407                 printf("'%.*s'\n", uif->u.octet_aligned->len,
408                        uif->u.octet_aligned->buf);
409             }
410             else if (uif->which == Z_External_single) 
411             {
412                 Odr_any *sat = uif->u.single_ASN1_type;
413                 oident *oid = oid_getentbyoid(uif->direct_reference);
414                 if (oid->value == VAL_OCLCUI) {
415                     Z_OCLC_UserInformation *oclc_ui;
416                     ODR decode = odr_createmem(ODR_DECODE);
417                     odr_setbuf(decode, (char *) sat->buf, sat->len, 0);
418                     if (!z_OCLC_UserInformation(decode, &oclc_ui, 0, 0))
419                         printf ("Bad OCLC UserInformation:\n");
420                     else
421                         printf ("OCLC UserInformation:\n");
422                     if (!z_OCLC_UserInformation(print, &oclc_ui, 0, 0))
423                         printf ("Bad OCLC UserInformation spec\n");
424                     odr_destroy(decode);
425                 }
426                 else
427                 {
428                     /* Peek at any private Init-diagnostic APDUs */
429                     printf("### NAUGHTY: External is '%.*s'\n",
430                            sat->len, sat->buf);
431                 }
432             }
433             odr_reset (print);
434         }
435     }
436     printf ("Options:");
437     yaz_init_opt_decode(res->options, pr_opt, 0);
438     printf ("\n");
439
440     if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
441         setnumber = 0;
442     
443     if (ODR_MASK_GET(res->options, Z_Options_negotiationModel)) {
444     
445         Z_CharSetandLanguageNegotiation *p =
446                 yaz_get_charneg_record(res->otherInfo);
447         
448         if (p) {
449             
450             char *charset=NULL, *lang=NULL;
451             int selected;
452             
453             yaz_get_response_charneg(session_mem, p, &charset, &lang,
454                                      &selected);
455
456             if (outputCharset && negotiationCharset) {
457                     odr_set_charset (out, charset, outputCharset);
458                     odr_set_charset (in, outputCharset, charset);
459             }
460             else {
461                     odr_set_charset (out, 0, 0);
462                     odr_set_charset (in, 0, 0);
463             }
464             
465             printf("Accepted character set : %s\n", charset);
466             printf("Accepted code language : %s\n", lang ? lang : "none");
467             printf("Accepted records in ...: %d\n", selected );
468         }
469     }
470     fflush (stdout);
471     return 0;
472 }
473
474
475 static void render_initUserInfo(Z_OtherInformation *ui1) {
476     int i;
477     printf("Init response contains %d otherInfo unit%s:\n",
478            ui1->num_elements, ui1->num_elements == 1 ? "" : "s");
479
480     for (i = 0; i < ui1->num_elements; i++) {
481         Z_OtherInformationUnit *unit = ui1->list[i];
482         printf("  %d: otherInfo unit contains ", i+1);
483         if (unit->which == Z_OtherInfo_externallyDefinedInfo &&
484             unit->information.externallyDefinedInfo &&
485             unit->information.externallyDefinedInfo->which ==
486             Z_External_diag1) {
487             render_diag(unit->information.externallyDefinedInfo->u.diag1);
488         } 
489         else if (unit->which != Z_OtherInfo_externallyDefinedInfo)
490         {
491             printf("unsupported otherInfo unit->which = %d\n", unit->which);
492         }
493         else 
494         {
495             printf("unsupported otherInfo unit external %d\n",
496                    unit->information.externallyDefinedInfo ? 
497                    unit->information.externallyDefinedInfo->which : -2);
498         }
499     }
500 }
501
502
503 /* ### should this share code with display_diagrecs()? */
504 static void render_diag(Z_DiagnosticFormat *diag) {
505     int i;
506
507     printf("%d diagnostic%s:\n", diag->num, diag->num == 1 ? "" : "s");
508     for (i = 0; i < diag->num; i++) {
509         Z_DiagnosticFormat_s *ds = diag->elements[i];
510         printf("    %d: ", i+1);
511         switch (ds->which) {
512         case Z_DiagnosticFormat_s_defaultDiagRec: {
513             Z_DefaultDiagFormat *dd = ds->u.defaultDiagRec;
514             /* ### should check `dd->diagnosticSetId' */
515             printf("code=%d (%s)", *dd->condition,
516                    diagbib1_str(*dd->condition));
517             /* Both types of addinfo are the same, so use type-pun */
518             if (dd->u.v2Addinfo != 0)
519                 printf(",\n\taddinfo='%s'", dd->u.v2Addinfo);
520             break;
521         }
522         case Z_DiagnosticFormat_s_explicitDiagnostic:
523             printf("Explicit diagnostic (not supported)");
524             break;
525         default:
526             printf("Unrecognised diagnostic type %d", ds->which);
527             break;
528         }
529
530         if (ds->message != 0)
531             printf(", message='%s'", ds->message);
532         printf("\n");
533     }
534 }
535
536
537 static int set_base(const char *arg)
538 {
539     int i;
540     const char *cp;
541
542     for (i = 0; i<num_databaseNames; i++)
543         xfree (databaseNames[i]);
544     num_databaseNames = 0;
545     while (1)
546     {
547         char *cp1;
548         if (!(cp = strchr(arg, ' ')))
549             cp = arg + strlen(arg);
550         if (cp - arg < 1)
551             break;
552         databaseNames[num_databaseNames] = (char *)xmalloc (1 + cp - arg);
553         memcpy (databaseNames[num_databaseNames], arg, cp - arg);
554         databaseNames[num_databaseNames][cp - arg] = '\0';
555
556         for (cp1 = databaseNames[num_databaseNames]; *cp1 ; cp1++)
557             if (*cp1 == '+')
558                 *cp1 = ' ';
559         num_databaseNames++;
560
561         if (!*cp)
562             break;
563         arg = cp+1;
564     }
565     if (num_databaseNames == 0)
566     {
567         num_databaseNames = 1;
568         databaseNames[0] = xstrdup("");
569     }
570     return 1;
571 }
572
573 static int parse_cmd_doc(const char **arg, ODR out, char **buf,
574                          int *len, int opt)
575 {
576     const char *sep;
577     while (**arg && strchr(" \t\n\r\f", **arg))
578         (*arg)++;
579     if ((*arg)[0] == '\"' && (sep=strchr(*arg+1, '"')))
580     {
581         (*arg)++;
582         *len = sep - *arg;
583         *buf = odr_strdupn(out, *arg, *len);
584         (*arg) = sep+1;
585         return 1;
586     }
587     else if ((*arg)[0] && (*arg)[0] != '\"')
588     {
589         long fsize;
590         FILE *inf;
591         const char *fname = *arg;
592         
593         while (**arg != '\0' && **arg != ' ')
594             (*arg)++;
595             
596         inf = fopen(fname, "rb");
597         if (!inf)
598         {
599             printf("Couldn't open %s\n", fname);
600             return 0;
601         }
602         if (fseek(inf, 0L, SEEK_END) == -1)
603         {
604             printf("Couldn't seek in %s\n", fname);
605             fclose(inf);
606             return 0;
607         }
608         fsize = ftell(inf);
609         if (fseek(inf, 0L, SEEK_SET) == -1)
610         {
611             printf("Couldn't seek in %s\n", fname);
612             fclose(inf);
613             return 0;
614         }
615         *len = fsize;
616         *buf = odr_malloc(out, fsize);
617         if (fread(*buf, 1, fsize, inf) != fsize)
618         {
619             printf("Unable to read %s\n", fname);
620             fclose(inf);
621             return 0;
622         }
623         fclose(inf);
624         return 1;
625     }
626     else if (**arg == '\0')
627     {
628         if (opt)
629         {
630             *len = 0;
631             *buf = 0;
632             return 1;
633         }
634         printf("Missing doc argument\n");
635     }
636     else
637         printf("Bad doc argument %s\n", *arg);
638     return 0;
639 }
640
641 static int cmd_base(const char *arg)
642 {
643     if (!*arg)
644     {
645         printf("Usage: base <database> <database> ...\n");
646         return 0;
647     }
648     return set_base(arg);
649 }
650
651 void cmd_open_remember_last_open_command(const char* arg, char* new_open_command)
652 {
653     if(last_open_command != arg) 
654     {
655         if(last_open_command) xfree(last_open_command);
656         last_open_command = xstrdup(new_open_command);
657     }
658 }
659
660 int session_connect(const char *arg)
661 {
662     void *add;
663     char type_and_host[101];
664     const char *basep = 0;
665 #if HAVE_OPENSSL_SSL_H
666     SSL *ssl;
667 #endif
668     if (conn)
669     {
670         cs_close (conn);
671         conn = 0;
672     }   
673     if (session_mem)
674     {
675         nmem_destroy (session_mem);
676         session_mem = NULL;
677         session_initResponse = 0;
678     }
679     cs_get_host_args(arg, &basep);
680
681     strncpy(type_and_host, arg, sizeof(type_and_host)-1);
682     type_and_host[sizeof(type_and_host)-1] = '\0';
683
684     cmd_open_remember_last_open_command(arg, type_and_host);
685
686     if (yazProxy)
687         conn = cs_create_host(yazProxy, 1, &add);
688     else
689         conn = cs_create_host(arg, 1, &add);
690     if (!conn)
691     {
692         printf ("Could not resolve address %s\n", arg);
693         return 0;
694     }
695 #if YAZ_HAVE_XML2
696     if (conn->protocol == PROTO_HTTP)
697         queryType = QueryType_CQL;
698 #else
699     if (conn->protocol == PROTO_HTTP)
700     {
701         printf ("SRW/HTTP not enabled in this YAZ\n");
702         cs_close(conn);
703         conn = 0;
704         return 0;
705     }
706 #endif
707     protocol = conn->protocol;
708     if (conn->protocol == PROTO_HTTP)
709         set_base("");
710     else
711         set_base("Default");
712     printf("Connecting...");
713     fflush(stdout);
714     if (cs_connect(conn, add) < 0)
715     {
716         printf ("error = %s\n", cs_strerror(conn));
717         cs_close(conn);
718         conn = 0;
719         return 0;
720     }
721     printf("OK.\n");
722 #if HAVE_OPENSSL_SSL_H
723     if ((ssl = (SSL *) cs_get_ssl(conn)))
724     {
725         X509 *server_cert = SSL_get_peer_certificate (ssl);
726
727         if (server_cert)
728         {
729             char *pem_buf;
730             int pem_len;
731             BIO *bio = BIO_new(BIO_s_mem());
732
733             /* get PEM buffer in memory */
734             PEM_write_bio_X509(bio, server_cert);
735             pem_len = BIO_get_mem_data(bio, &pem_buf);
736             fwrite(pem_buf, pem_len, 1, stdout);
737         
738             /* print all info on screen .. */
739             X509_print_fp(stdout, server_cert);
740             BIO_free(bio);
741
742             X509_free (server_cert);
743         }
744     }
745 #endif
746     if (basep && *basep)
747         set_base (basep);
748     if (protocol == PROTO_Z3950)
749     {
750         send_initRequest(type_and_host);
751         return 2;
752     }
753     return 0;
754 }
755
756 int cmd_open(const char *arg)
757 {
758     if (arg)
759     {
760         strncpy (cur_host, arg, sizeof(cur_host)-1);
761         cur_host[sizeof(cur_host)-1] = 0;
762     }
763     return session_connect(cur_host);
764 }
765
766 void try_reconnect(void)
767 {
768     char* open_command;
769         
770     if(!( auto_reconnect && last_open_command) ) return ;
771
772     open_command = (char *) xmalloc (strlen(last_open_command)+6);
773     strcpy (open_command, "open ");
774         
775     strcat (open_command, last_open_command);
776
777     process_cmd_line(open_command);
778         
779     xfree(open_command);                                
780 }
781
782 int cmd_authentication(const char *arg)
783 {
784     static Z_IdAuthentication au;
785     static char user[40], group[40], pass[40];
786     static Z_IdPass idPass;
787     int r;
788
789     if (!*arg)
790     {
791         printf("Auth field set to null\n");
792         auth = 0;
793         return 1;
794     }
795     r = sscanf (arg, "%39s %39s %39s", user, group, pass);
796     if (r == 0)
797     {
798         printf("Authentication set to null\n");
799         auth = 0;
800     }
801     if (r == 1)
802     {
803         auth = &au;
804         if (!strcmp(user, "-")) {
805             au.which = Z_IdAuthentication_anonymous;
806             printf("Authentication set to Anonymous\n");
807         } else {
808             au.which = Z_IdAuthentication_open;
809             au.u.open = user;
810             printf("Authentication set to Open (%s)\n", user);
811         }
812     }
813     if (r == 2)
814     {
815         auth = &au;
816         au.which = Z_IdAuthentication_idPass;
817         au.u.idPass = &idPass;
818         idPass.groupId = NULL;
819         idPass.userId = user;
820         idPass.password = group;
821         printf("Authentication set to User (%s), Pass (%s)\n", user, group);
822     }
823     if (r == 3)
824     {
825         auth = &au;
826         au.which = Z_IdAuthentication_idPass;
827         au.u.idPass = &idPass;
828         idPass.groupId = group;
829         idPass.userId = user;
830         idPass.password = pass;
831         printf("Authentication set to User (%s), Group (%s), Pass (%s)\n",
832                user, group, pass);
833     }
834     return 1;
835 }
836
837 /* SEARCH SERVICE ------------------------------ */
838 static void display_record(Z_External *r);
839
840 static void print_record(const unsigned char *buf, size_t len)
841 {
842     size_t i = len;
843     print_stringn (buf, len);
844     /* add newline if not already added ... */
845     if (i <= 0 || buf[i-1] != '\n')
846         printf ("\n");
847 }
848
849 static void display_record(Z_External *r)
850 {
851     oident *ent = oid_getentbyoid(r->direct_reference);
852
853     record_last = r;
854     /*
855      * Tell the user what we got.
856      */
857     if (r->direct_reference)
858     {
859         printf("Record type: ");
860         if (ent)
861             printf("%s\n", ent->desc);
862         else if (!odr_oid(print, &r->direct_reference, 0, 0))
863         {
864             odr_perror(print, "print oid");
865             odr_reset(print);
866         }
867     }
868     /* Check if this is a known, ASN.1 type tucked away in an octet string */
869     if (ent && r->which == Z_External_octet)
870     {
871         Z_ext_typeent *type = z_ext_getentbyref(ent->value);
872         char *rr;
873
874         if (type)
875         {
876             /*
877              * Call the given decoder to process the record.
878              */
879             odr_setbuf(in, (char*)r->u.octet_aligned->buf,
880                 r->u.octet_aligned->len, 0);
881             if (!(*type->fun)(in, &rr, 0, 0))
882             {
883                 odr_perror(in, "Decoding constructed record.");
884                 fprintf(stdout, "[Near %ld]\n", (long) odr_offset(in));
885                 fprintf(stdout, "Packet dump:\n---------\n");
886                 odr_dumpBER(stdout, (char*)r->u.octet_aligned->buf,
887                             r->u.octet_aligned->len);
888                 fprintf(stdout, "---------\n");
889                 
890                 /* note just ignores the error ant print the bytes form the octet_aligned later */
891             } else {
892                 /*
893                  * Note: we throw away the original, BER-encoded record here.
894                  * Do something else with it if you want to keep it.
895                  */
896                 r->u.sutrs = (Z_SUTRS *) rr; /* we don't actually check the type here. */
897                 r->which = type->what;
898             }
899         }
900     }
901     if (ent && ent->value == VAL_SOIF)
902     {
903         print_record((const unsigned char *) r->u.octet_aligned->buf,
904                      r->u.octet_aligned->len);
905         if (marc_file)
906             fwrite (r->u.octet_aligned->buf, 1, r->u.octet_aligned->len, marc_file);
907     }
908     else if (r->which == Z_External_octet)
909     {
910         const char *octet_buf = (char*)r->u.octet_aligned->buf;
911         if (ent->oclass == CLASS_RECSYN && 
912                 (ent->value == VAL_TEXT_XML || 
913                  ent->value == VAL_APPLICATION_XML ||
914                  ent->value == VAL_HTML))
915         {
916             print_record((const unsigned char *) octet_buf,
917                          r->u.octet_aligned->len);
918         }
919         else if (ent->value == VAL_POSTSCRIPT)
920         {
921             int size = r->u.octet_aligned->len;
922             if (size > 100)
923                 size = 100;
924             print_record((const unsigned char *) octet_buf, size);
925         }
926         else
927         {
928             if ( 
929 #if AVOID_MARC_DECODE
930                 /* primitive check for a marc OID 5.1-29 except 16 */
931                 ent->oidsuffix[0] == 5 && ent->oidsuffix[1] < 30 &&
932                 ent->oidsuffix[1] != 16
933 #else
934                 1
935 #endif
936                 )
937             {
938                 char *result;
939                 int rlen;
940                 yaz_iconv_t cd = 0;
941                 yaz_marc_t mt = yaz_marc_create();
942                     
943                 if (yaz_marc_decode_buf(mt, octet_buf,r->u.octet_aligned->len,
944                                         &result, &rlen)> 0)
945                 {
946                     char *from = 0;
947                     if (marcCharset && !strcmp(marcCharset, "auto"))
948                     {
949                         if (ent->value == VAL_USMARC)
950                         {
951                             if (octet_buf[9] == 'a')
952                                 from = "UTF-8";
953                             else
954                                 from = "MARC-8";
955                         }
956                         else
957                             from = "ISO-8859-1";
958                     }
959                     else if (marcCharset)
960                         from = marcCharset;
961                     if (outputCharset && from)
962                     {   
963                         cd = yaz_iconv_open(outputCharset, from);
964                         printf ("convert from %s to %s", from, 
965                                 outputCharset);
966                         if (!cd)
967                             printf (" unsupported\n");
968                         else
969                             printf ("\n");
970                     }
971                     if (!cd)
972                         fwrite (result, 1, rlen, stdout);
973                     else
974                     {
975                         char outbuf[6];
976                         size_t inbytesleft = rlen;
977                         const char *inp = result;
978                         
979                         while (inbytesleft)
980                         {
981                             size_t outbytesleft = sizeof(outbuf);
982                             char *outp = outbuf;
983                             size_t r;
984
985                             r = yaz_iconv (cd, (char**) &inp,
986                                            &inbytesleft, 
987                                            &outp, &outbytesleft);
988                             if (r == (size_t) (-1))
989                             {
990                                 int e = yaz_iconv_error(cd);
991                                 if (e != YAZ_ICONV_E2BIG)
992                                     break;
993                             }
994                             fwrite (outbuf, outp - outbuf, 1, stdout);
995                         }
996                     }
997                 }
998                 else
999                 {
1000                     printf ("bad MARC. Dumping as it is:\n");
1001                     print_record((const unsigned char*) octet_buf,
1002                                   r->u.octet_aligned->len);
1003                 }       
1004                 yaz_marc_destroy(mt);
1005                 if (cd)
1006                     yaz_iconv_close(cd);
1007             }
1008             else
1009             {
1010                 print_record((const unsigned char*) octet_buf,
1011                              r->u.octet_aligned->len);
1012             }
1013         }
1014         if (marc_file)
1015             fwrite (octet_buf, 1, r->u.octet_aligned->len, marc_file);
1016     }
1017     else if (ent && ent->value == VAL_SUTRS)
1018     {
1019         if (r->which != Z_External_sutrs)
1020         {
1021             printf("Expecting single SUTRS type for SUTRS.\n");
1022             return;
1023         }
1024         print_record(r->u.sutrs->buf, r->u.sutrs->len);
1025         if (marc_file)
1026             fwrite (r->u.sutrs->buf, 1, r->u.sutrs->len, marc_file);
1027     }
1028     else if (ent && ent->value == VAL_GRS1)
1029     {
1030         WRBUF w;
1031         if (r->which != Z_External_grs1)
1032         {
1033             printf("Expecting single GRS type for GRS.\n");
1034             return;
1035         }
1036         w = wrbuf_alloc();
1037         yaz_display_grs1(w, r->u.grs1, 0);
1038         puts (wrbuf_buf(w));
1039         wrbuf_free(w, 1);
1040     }
1041     else if (ent && ent->value == VAL_OPAC)
1042     {
1043         int i;
1044         if (r->u.opac->bibliographicRecord)
1045             display_record(r->u.opac->bibliographicRecord);
1046         for (i = 0; i<r->u.opac->num_holdingsData; i++)
1047         {
1048             Z_HoldingsRecord *h = r->u.opac->holdingsData[i];
1049             if (h->which == Z_HoldingsRecord_marcHoldingsRecord)
1050             {
1051                 printf ("MARC holdings %d\n", i);
1052                 display_record(h->u.marcHoldingsRecord);
1053             }
1054             else if (h->which == Z_HoldingsRecord_holdingsAndCirc)
1055             {
1056                 int j;
1057
1058                 Z_HoldingsAndCircData *data = h->u.holdingsAndCirc;
1059
1060                 printf ("Data holdings %d\n", i);
1061                 if (data->typeOfRecord)
1062                     printf ("typeOfRecord: %s\n", data->typeOfRecord);
1063                 if (data->encodingLevel)
1064                     printf ("encodingLevel: %s\n", data->encodingLevel);
1065                 if (data->receiptAcqStatus)
1066                     printf ("receiptAcqStatus: %s\n", data->receiptAcqStatus);
1067                 if (data->generalRetention)
1068                     printf ("generalRetention: %s\n", data->generalRetention);
1069                 if (data->completeness)
1070                     printf ("completeness: %s\n", data->completeness);
1071                 if (data->dateOfReport)
1072                     printf ("dateOfReport: %s\n", data->dateOfReport);
1073                 if (data->nucCode)
1074                     printf ("nucCode: %s\n", data->nucCode);
1075                 if (data->localLocation)
1076                     printf ("localLocation: %s\n", data->localLocation);
1077                 if (data->shelvingLocation)
1078                     printf ("shelvingLocation: %s\n", data->shelvingLocation);
1079                 if (data->callNumber)
1080                     printf ("callNumber: %s\n", data->callNumber);
1081                 if (data->shelvingData)
1082                     printf ("shelvingData: %s\n", data->shelvingData);
1083                 if (data->copyNumber)
1084                     printf ("copyNumber: %s\n", data->copyNumber);
1085                 if (data->publicNote)
1086                     printf ("publicNote: %s\n", data->publicNote);
1087                 if (data->reproductionNote)
1088                     printf ("reproductionNote: %s\n", data->reproductionNote);
1089                 if (data->termsUseRepro)
1090                     printf ("termsUseRepro: %s\n", data->termsUseRepro);
1091                 if (data->enumAndChron)
1092                     printf ("enumAndChron: %s\n", data->enumAndChron);
1093                 for (j = 0; j<data->num_volumes; j++)
1094                 {
1095                     printf ("volume %d\n", j);
1096                     if (data->volumes[j]->enumeration)
1097                         printf (" enumeration: %s\n",
1098                                 data->volumes[j]->enumeration);
1099                     if (data->volumes[j]->chronology)
1100                         printf (" chronology: %s\n",
1101                                 data->volumes[j]->chronology);
1102                     if (data->volumes[j]->enumAndChron)
1103                         printf (" enumAndChron: %s\n",
1104                                 data->volumes[j]->enumAndChron);
1105                 }
1106                 for (j = 0; j<data->num_circulationData; j++)
1107                 {
1108                     printf ("circulation %d\n", j);
1109                     if (data->circulationData[j]->availableNow)
1110                         printf (" availableNow: %d\n",
1111                                 *data->circulationData[j]->availableNow);
1112                     if (data->circulationData[j]->availablityDate)
1113                         printf (" availabiltyDate: %s\n",
1114                                 data->circulationData[j]->availablityDate);
1115                     if (data->circulationData[j]->availableThru)
1116                         printf (" availableThru: %s\n",
1117                                 data->circulationData[j]->availableThru);
1118                     if (data->circulationData[j]->restrictions)
1119                         printf (" restrictions: %s\n",
1120                                 data->circulationData[j]->restrictions);
1121                     if (data->circulationData[j]->itemId)
1122                         printf (" itemId: %s\n",
1123                                 data->circulationData[j]->itemId);
1124                     if (data->circulationData[j]->renewable)
1125                         printf (" renewable: %d\n",
1126                                 *data->circulationData[j]->renewable);
1127                     if (data->circulationData[j]->onHold)
1128                         printf (" onHold: %d\n",
1129                                 *data->circulationData[j]->onHold);
1130                     if (data->circulationData[j]->enumAndChron)
1131                         printf (" enumAndChron: %s\n",
1132                                 data->circulationData[j]->enumAndChron);
1133                     if (data->circulationData[j]->midspine)
1134                         printf (" midspine: %s\n",
1135                                 data->circulationData[j]->midspine);
1136                     if (data->circulationData[j]->temporaryLocation)
1137                         printf (" temporaryLocation: %s\n",
1138                                 data->circulationData[j]->temporaryLocation);
1139                 }
1140             }
1141         }
1142     }
1143     else 
1144     {
1145         printf("Unknown record representation.\n");
1146         if (!z_External(print, &r, 0, 0))
1147         {
1148             odr_perror(print, "Printing external");
1149             odr_reset(print);
1150         }
1151     }
1152 }
1153
1154 static void display_diagrecs(Z_DiagRec **pp, int num)
1155 {
1156     int i;
1157     oident *ent;
1158     Z_DefaultDiagFormat *r;
1159
1160     printf("Diagnostic message(s) from database:\n");
1161     for (i = 0; i<num; i++)
1162     {
1163         Z_DiagRec *p = pp[i];
1164         if (p->which != Z_DiagRec_defaultFormat)
1165         {
1166             printf("Diagnostic record not in default format.\n");
1167             return;
1168         }
1169         else
1170             r = p->u.defaultFormat;
1171         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
1172             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
1173             printf("Missing or unknown diagset\n");
1174         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
1175         switch (r->which)
1176         {
1177         case Z_DefaultDiagFormat_v2Addinfo:
1178             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
1179             break;
1180         case Z_DefaultDiagFormat_v3Addinfo:
1181             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
1182             break;
1183         }
1184     }
1185 }
1186
1187
1188 static void display_nameplusrecord(Z_NamePlusRecord *p)
1189 {
1190     if (p->databaseName)
1191         printf("[%s]", p->databaseName);
1192     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
1193         display_diagrecs(&p->u.surrogateDiagnostic, 1);
1194     else if (p->which == Z_NamePlusRecord_databaseRecord)
1195         display_record(p->u.databaseRecord);
1196 }
1197
1198 static void display_records(Z_Records *p)
1199 {
1200     int i;
1201
1202     if (p->which == Z_Records_NSD)
1203     {
1204         Z_DiagRec dr, *dr_p = &dr;
1205         dr.which = Z_DiagRec_defaultFormat;
1206         dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
1207         display_diagrecs (&dr_p, 1);
1208     }
1209     else if (p->which == Z_Records_multipleNSD)
1210         display_diagrecs (p->u.multipleNonSurDiagnostics->diagRecs,
1211                           p->u.multipleNonSurDiagnostics->num_diagRecs);
1212     else 
1213     {
1214         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
1215         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
1216             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
1217     }
1218 }
1219
1220 static int send_deleteResultSetRequest(const char *arg)
1221 {
1222     char names[8][32];
1223     int i;
1224
1225     Z_APDU *apdu = zget_APDU(out, Z_APDU_deleteResultSetRequest);
1226     Z_DeleteResultSetRequest *req = apdu->u.deleteResultSetRequest;
1227
1228     req->referenceId = set_refid (out);
1229
1230     req->num_resultSetList =
1231         sscanf (arg, "%30s %30s %30s %30s %30s %30s %30s %30s",
1232                 names[0], names[1], names[2], names[3],
1233                 names[4], names[5], names[6], names[7]);
1234
1235     req->deleteFunction = (int *)
1236         odr_malloc (out, sizeof(*req->deleteFunction));
1237     if (req->num_resultSetList > 0)
1238     {
1239         *req->deleteFunction = Z_DeleteResultSetRequest_list;
1240         req->resultSetList = (char **)
1241             odr_malloc (out, sizeof(*req->resultSetList)*
1242                         req->num_resultSetList);
1243         for (i = 0; i<req->num_resultSetList; i++)
1244             req->resultSetList[i] = names[i];
1245     }
1246     else
1247     {
1248         *req->deleteFunction = Z_DeleteResultSetRequest_all;
1249         req->resultSetList = 0;
1250     }
1251     
1252     send_apdu(apdu);
1253     printf("Sent deleteResultSetRequest.\n");
1254     return 2;
1255 }
1256
1257 #if YAZ_HAVE_XML2
1258 static int send_srw(Z_SRW_PDU *sr)
1259 {
1260     const char *charset = negotiationCharset;
1261     const char *host_port = cur_host;
1262     Z_GDU *gdu;
1263     char *path = 0;
1264
1265     path = odr_malloc(out, 2+strlen(databaseNames[0]));
1266     *path = '/';
1267     strcpy(path+1, databaseNames[0]);
1268
1269     gdu = z_get_HTTP_Request_host_path(out, host_port, path);
1270
1271     if (!strcmp(sru_method, "get"))
1272     {
1273         yaz_sru_get_encode(gdu->u.HTTP_Request, sr, out, charset);
1274     }
1275     else if (!strcmp(sru_method, "post"))
1276     {
1277         yaz_sru_post_encode(gdu->u.HTTP_Request, sr, out, charset);
1278     }
1279     else if (!strcmp(sru_method, "soap"))
1280     {
1281         yaz_sru_soap_encode(gdu->u.HTTP_Request, sr, out, charset);
1282     }
1283
1284     if (z_GDU(out, &gdu, 0, 0))
1285     {
1286         /* encode OK */
1287         char *buf_out;
1288         int len_out;
1289         int r;
1290         if (apdu_file)
1291         {
1292             if (!z_GDU(print, &gdu, 0, 0))
1293                 printf ("Failed to print outgoing SRU package\n");
1294             odr_reset(print);
1295         }
1296         buf_out = odr_getbuf(out, &len_out, 0);
1297         
1298         /* we don't odr_reset(out), since we may need the buffer again */
1299
1300         do_hex_dump(buf_out, len_out);
1301
1302         r = cs_put(conn, buf_out, len_out);
1303
1304         if (r >= 0)
1305             return 2;
1306     }
1307     return 0;
1308 }
1309 #endif
1310
1311 #if YAZ_HAVE_XML2
1312 static char *encode_SRW_term(ODR o, const char *q)
1313 {
1314     const char *in_charset = "ISO-8859-1";
1315     WRBUF w = wrbuf_alloc();
1316     yaz_iconv_t cd;
1317     char *res;
1318     if (outputCharset)
1319         in_charset = outputCharset;
1320     cd = yaz_iconv_open("UTF-8", in_charset);
1321     if (!cd)
1322     {
1323         wrbuf_free(w, 1);
1324         return odr_strdup(o, q);
1325     }
1326     wrbuf_iconv_write(w, cd, q, strlen(q));
1327     if (wrbuf_len(w))
1328     {
1329         int len = wrbuf_len(w);
1330         res = odr_strdupn(o, wrbuf_buf(w), len);
1331     }
1332     else
1333         res = odr_strdup(o, q);    
1334     yaz_iconv_close(cd);
1335     wrbuf_free(w, 1);
1336     return res;
1337 }
1338
1339
1340 static int send_SRW_scanRequest(const char *arg, int pos, int num)
1341 {
1342     Z_SRW_PDU *sr = 0;
1343     
1344     /* regular requestse .. */
1345     sr = yaz_srw_get(out, Z_SRW_scan_request);
1346
1347     switch(queryType)
1348     {
1349     case QueryType_CQL:
1350         sr->u.scan_request->query_type = Z_SRW_query_type_cql;
1351         sr->u.scan_request->scanClause.cql = encode_SRW_term(out, arg);
1352         break;
1353     case QueryType_Prefix:
1354         sr->u.scan_request->query_type = Z_SRW_query_type_pqf;
1355         sr->u.scan_request->scanClause.pqf = encode_SRW_term(out, arg);
1356         break;
1357     default:
1358         printf ("Only CQL and PQF supported in SRW\n");
1359         return 0;
1360     }
1361     sr->u.scan_request->responsePosition = odr_intdup(out, pos);
1362     sr->u.scan_request->maximumTerms = odr_intdup(out, num);
1363     return send_srw(sr);
1364 }
1365
1366 static int send_SRW_searchRequest(const char *arg)
1367 {
1368     Z_SRW_PDU *sr = 0;
1369     
1370     if (!srw_sr)
1371     {
1372         assert(srw_sr_odr_out == 0);
1373         srw_sr_odr_out = odr_createmem(ODR_ENCODE);
1374     }
1375     odr_reset(srw_sr_odr_out);
1376
1377     setno = 1;
1378
1379     /* save this for later .. when fetching individual records */
1380     srw_sr =  yaz_srw_get(srw_sr_odr_out, Z_SRW_searchRetrieve_request);
1381     
1382     /* regular request .. */
1383     sr = yaz_srw_get(out, Z_SRW_searchRetrieve_request);
1384
1385     switch(queryType)
1386     {
1387     case QueryType_CQL:
1388         srw_sr->u.request->query_type = Z_SRW_query_type_cql;
1389         srw_sr->u.request->query.cql = encode_SRW_term(srw_sr_odr_out, arg);
1390
1391         sr->u.request->query_type = Z_SRW_query_type_cql;
1392         sr->u.request->query.cql = encode_SRW_term(srw_sr_odr_out, arg);
1393         break;
1394     case QueryType_Prefix:
1395         srw_sr->u.request->query_type = Z_SRW_query_type_pqf;
1396         srw_sr->u.request->query.pqf = encode_SRW_term(srw_sr_odr_out, arg);
1397
1398         sr->u.request->query_type = Z_SRW_query_type_pqf;
1399         sr->u.request->query.pqf = encode_SRW_term(srw_sr_odr_out, arg);
1400         break;
1401     default:
1402         printf ("Only CQL and PQF supported in SRW\n");
1403         return 0;
1404     }
1405     sr->u.request->maximumRecords = odr_intdup(out, 0);
1406
1407     if (record_schema)
1408         sr->u.request->recordSchema = record_schema;
1409     if (recordsyntax_size == 1 && recordsyntax_list[0] == VAL_TEXT_XML)
1410         sr->u.request->recordPacking = "xml";
1411     return send_srw(sr);
1412 }
1413 #endif
1414
1415 static int send_searchRequest(const char *arg)
1416 {
1417     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
1418     Z_SearchRequest *req = apdu->u.searchRequest;
1419     Z_Query query;
1420     struct ccl_rpn_node *rpn = NULL;
1421     int error, pos;
1422     char setstring[100];
1423     Z_RPNQuery *RPNquery;
1424     Odr_oct ccl_query;
1425     YAZ_PQF_Parser pqf_parser;
1426     Z_External *ext;
1427     QueryType myQueryType = queryType;
1428     char pqfbuf[512];
1429
1430     if (myQueryType == QueryType_CCL2RPN)
1431     {
1432         rpn = ccl_find_str(bibset, arg, &error, &pos);
1433         if (error)
1434         {
1435             printf("CCL ERROR: %s\n", ccl_err_msg(error));
1436             return 0;
1437         }
1438     } else if (myQueryType == QueryType_CQL2RPN) {
1439         /* ### All this code should be wrapped in a utility function */
1440         CQL_parser parser;
1441         struct cql_node *node;
1442         const char *addinfo;
1443         if (cqltrans == 0) {
1444             printf("Can't use CQL: no translation file.  Try set_cqlfile\n");
1445             return 0;
1446         }
1447         parser = cql_parser_create();
1448         if ((error = cql_parser_string(parser, arg)) != 0) {
1449             printf("Can't parse CQL: must be a syntax error\n");
1450             return 0;
1451         }
1452         node = cql_parser_result(parser);
1453         if ((error = cql_transform_buf(cqltrans, node, pqfbuf,
1454                                        sizeof pqfbuf)) != 0) {
1455             error = cql_transform_error(cqltrans, &addinfo);
1456             printf ("Can't convert CQL to PQF: %s (addinfo=%s)\n",
1457                     cql_strerror(error), addinfo);
1458             return 0;
1459         }
1460         arg = pqfbuf;
1461         myQueryType = QueryType_Prefix;
1462     }
1463
1464     req->referenceId = set_refid (out);
1465     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
1466     {
1467         static unsigned char big[2100];
1468         static Odr_oct bigo;
1469
1470         /* send a very big referenceid to test transport stack etc. */
1471         memset(big, 'A', 2100);
1472         bigo.len = bigo.size = 2100;
1473         bigo.buf = big;
1474         req->referenceId = &bigo;
1475     }
1476     
1477     if (setnumber >= 0)
1478     {
1479         sprintf(setstring, "%d", ++setnumber);
1480         req->resultSetName = setstring;
1481     }
1482     *req->smallSetUpperBound = smallSetUpperBound;
1483     *req->largeSetLowerBound = largeSetLowerBound;
1484     *req->mediumSetPresentNumber = mediumSetPresentNumber;
1485     if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
1486         mediumSetPresentNumber > 0))
1487     {
1488         if (recordsyntax_size > 0)
1489             req->preferredRecordSyntax =
1490                 yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax_list[0]);
1491         req->smallSetElementSetNames =
1492             req->mediumSetElementSetNames = elementSetNames;
1493     }
1494     req->num_databaseNames = num_databaseNames;
1495     req->databaseNames = databaseNames;
1496
1497     req->query = &query;
1498
1499     switch (myQueryType)
1500     {
1501     case QueryType_Prefix:
1502         query.which = Z_Query_type_1;
1503         pqf_parser = yaz_pqf_create ();
1504         RPNquery = yaz_pqf_parse (pqf_parser, out, arg);
1505         if (!RPNquery)
1506         {
1507             const char *pqf_msg;
1508             size_t off;
1509             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1510             int ioff = off;
1511             printf("%*s^\n", ioff+4, "");
1512             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
1513             
1514             yaz_pqf_destroy (pqf_parser);
1515             return 0;
1516         }
1517         yaz_pqf_destroy (pqf_parser);
1518         query.u.type_1 = RPNquery;
1519         break;
1520     case QueryType_CCL:
1521         query.which = Z_Query_type_2;
1522         query.u.type_2 = &ccl_query;
1523         ccl_query.buf = (unsigned char*) arg;
1524         ccl_query.len = strlen(arg);
1525         break;
1526     case QueryType_CCL2RPN:
1527         query.which = Z_Query_type_1;
1528         RPNquery = ccl_rpn_query(out, rpn);
1529         if (!RPNquery)
1530         {
1531             printf ("Couldn't convert from CCL to RPN\n");
1532             return 0;
1533         }
1534         query.u.type_1 = RPNquery;
1535         ccl_rpn_delete (rpn);
1536         break;
1537     case QueryType_CQL:
1538         query.which = Z_Query_type_104;
1539         ext = (Z_External *) odr_malloc(out, sizeof(*ext));
1540         ext->direct_reference = odr_getoidbystr(out, "1.2.840.10003.16.2");
1541         ext->indirect_reference = 0;
1542         ext->descriptor = 0;
1543         ext->which = Z_External_CQL;
1544         ext->u.cql = odr_strdup(out, arg);
1545         query.u.type_104 =  ext;
1546         break;
1547     default:
1548         printf ("Unsupported query type\n");
1549         return 0;
1550     }
1551     if (send_apdu(apdu))
1552         printf("Sent searchRequest.\n");
1553     setno = 1;
1554     return 2;
1555 }
1556
1557 /* display Query Expression as part of searchResult-1 */
1558 static void display_queryExpression (const char *lead, Z_QueryExpression *qe)
1559 {
1560     if (!qe)
1561         return;
1562     printf(" %s=", lead);
1563     if (qe->which == Z_QueryExpression_term)
1564     {
1565         if (qe->u.term->queryTerm)
1566         {
1567             Z_Term *term = qe->u.term->queryTerm;
1568             switch (term->which)
1569             {
1570             case Z_Term_general:
1571                 printf ("%.*s", term->u.general->len, term->u.general->buf);
1572                 break;
1573             case Z_Term_characterString:
1574                 printf ("%s", term->u.characterString);
1575                 break;
1576             case Z_Term_numeric:
1577                 printf ("%d", *term->u.numeric);
1578                 break;
1579             case Z_Term_null:
1580                 printf ("null");
1581                 break;
1582             }
1583         }
1584     }
1585 }
1586
1587 /* see if we can find USR:SearchResult-1 */
1588 static void display_searchResult (Z_OtherInformation *o)
1589 {
1590     int i;
1591     if (!o)
1592         return ;
1593     for (i = 0; i < o->num_elements; i++)
1594     {
1595         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
1596         {
1597             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
1598             
1599             if (ext->which == Z_External_searchResult1)
1600             {
1601                 int j;
1602                 Z_SearchInfoReport *sr = ext->u.searchResult1;
1603                 printf ("SearchResult-1:");
1604                 for (j = 0; j < sr->num; j++)
1605                 {
1606                     if (j)
1607                         printf(",");
1608                     if (!sr->elements[j]->subqueryExpression)
1609                         printf("%d", j);
1610                     display_queryExpression("term",
1611                         sr->elements[j]->subqueryExpression);
1612                     display_queryExpression("interpretation",
1613                         sr->elements[j]->subqueryInterpretation);
1614                     display_queryExpression("recommendation",
1615                         sr->elements[j]->subqueryRecommendation);
1616                     if (sr->elements[j]->subqueryCount)
1617                         printf(" cnt=%d", *sr->elements[j]->subqueryCount);
1618                     if (sr->elements[j]->subqueryId)
1619                         printf(" id=%s ", sr->elements[j]->subqueryId);
1620                 }
1621                 printf ("\n");
1622             }
1623         }
1624     }
1625 }
1626
1627 static int process_searchResponse(Z_SearchResponse *res)
1628 {
1629     printf ("Received SearchResponse.\n");
1630     print_refid (res->referenceId);
1631     if (*res->searchStatus)
1632         printf("Search was a success.\n");
1633     else
1634         printf("Search was a bloomin' failure.\n");
1635     printf("Number of hits: %d", *res->resultCount);
1636     last_hit_count = *res->resultCount;
1637     if (setnumber >= 0)
1638         printf (", setno %d", setnumber);
1639     putchar('\n');
1640     if (res->resultSetStatus)
1641     {
1642         printf("Result Set Status: ");
1643         switch(*res->resultSetStatus)
1644         {
1645         case Z_SearchResponse_subset:
1646             printf("subset"); break;
1647         case Z_SearchResponse_interim:
1648             printf("interim"); break;
1649         case Z_SearchResponse_none:
1650             printf("none"); break;
1651         case Z_SearchResponse_estimate:
1652             printf("estimate"); break;
1653         default:
1654             printf("%d", *res->resultSetStatus);
1655         }            
1656         putchar('\n');
1657     }
1658     display_searchResult (res->additionalSearchInfo);
1659     printf("records returned: %d\n",
1660            *res->numberOfRecordsReturned);
1661     setno += *res->numberOfRecordsReturned;
1662     if (res->records)
1663         display_records(res->records);
1664     return 0;
1665 }
1666
1667 static void print_level(int iLevel)
1668 {
1669     int i;
1670     for (i = 0; i < iLevel * 4; i++)
1671         printf(" ");
1672 }
1673
1674 static void print_int(int iLevel, const char *pTag, int *pInt)
1675 {
1676     if (pInt != NULL)
1677     {
1678         print_level(iLevel);
1679         printf("%s: %d\n", pTag, *pInt);
1680     }
1681 }
1682
1683 static void print_string(int iLevel, const char *pTag, const char *pString)
1684 {
1685     if (pString != NULL)
1686     {
1687         print_level(iLevel);
1688         printf("%s: %s\n", pTag, pString);
1689     }
1690 }
1691
1692 static void print_oid(int iLevel, const char *pTag, Odr_oid *pOid)
1693 {
1694     if (pOid != NULL)
1695     {
1696         int *pInt = pOid;
1697
1698         print_level(iLevel);
1699         printf("%s:", pTag);
1700         for (; *pInt != -1; pInt++)
1701             printf(" %d", *pInt);
1702         printf("\n");
1703     }
1704 }
1705
1706 static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
1707 {
1708     if (referenceId != NULL)
1709     {
1710         int i;
1711
1712         print_level(iLevel);
1713         printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
1714         for (i = 0; i < referenceId->len; i++)
1715             printf("%c", referenceId->buf[i]);
1716         printf("\n");
1717     }
1718 }
1719
1720 static void print_string_or_numeric(int iLevel, const char *pTag, Z_StringOrNumeric *pStringNumeric)
1721 {
1722     if (pStringNumeric != NULL)
1723     {
1724         switch (pStringNumeric->which)
1725         {
1726         case Z_StringOrNumeric_string:
1727             print_string(iLevel, pTag, pStringNumeric->u.string);
1728             break;
1729             
1730         case Z_StringOrNumeric_numeric:
1731             print_int(iLevel, pTag, pStringNumeric->u.numeric);
1732             break;
1733             
1734         default:
1735             print_level(iLevel);
1736             printf("%s: valid type for Z_StringOrNumeric\n", pTag);
1737             break;
1738         }
1739     }
1740 }
1741
1742 static void print_universe_report_duplicate(
1743     int iLevel,
1744     Z_UniverseReportDuplicate *pUniverseReportDuplicate)
1745 {
1746     if (pUniverseReportDuplicate != NULL)
1747     {
1748         print_level(iLevel);
1749         printf("Universe Report Duplicate: \n");
1750         iLevel++;
1751         print_string_or_numeric(iLevel, "Hit No",
1752                                 pUniverseReportDuplicate->hitno);
1753     }
1754 }
1755
1756 static void print_universe_report_hits(
1757     int iLevel,
1758     Z_UniverseReportHits *pUniverseReportHits)
1759 {
1760     if (pUniverseReportHits != NULL)
1761     {
1762         print_level(iLevel);
1763         printf("Universe Report Hits: \n");
1764         iLevel++;
1765         print_string_or_numeric(iLevel, "Database",
1766                                 pUniverseReportHits->database);
1767         print_string_or_numeric(iLevel, "Hits", pUniverseReportHits->hits);
1768     }
1769 }
1770
1771 static void print_universe_report(int iLevel, Z_UniverseReport *pUniverseReport)
1772 {
1773     if (pUniverseReport != NULL)
1774     {
1775         print_level(iLevel);
1776         printf("Universe Report: \n");
1777         iLevel++;
1778         print_int(iLevel, "Total Hits", pUniverseReport->totalHits);
1779         switch (pUniverseReport->which)
1780         {
1781         case Z_UniverseReport_databaseHits:
1782             print_universe_report_hits(iLevel,
1783                                        pUniverseReport->u.databaseHits);
1784             break;
1785             
1786         case Z_UniverseReport_duplicate:
1787             print_universe_report_duplicate(iLevel,
1788                                             pUniverseReport->u.duplicate);
1789             break;
1790             
1791         default:
1792             print_level(iLevel);
1793             printf("Type: %d\n", pUniverseReport->which);
1794             break;
1795         }
1796     }
1797 }
1798
1799 static void print_external(int iLevel, Z_External *pExternal)
1800 {
1801     if (pExternal != NULL)
1802     {
1803         print_level(iLevel);
1804         printf("External: \n");
1805         iLevel++;
1806         print_oid(iLevel, "Direct Reference", pExternal->direct_reference);
1807         print_int(iLevel, "InDirect Reference", pExternal->indirect_reference);
1808         print_string(iLevel, "Descriptor", pExternal->descriptor);
1809         switch (pExternal->which)
1810         {
1811         case Z_External_universeReport:
1812             print_universe_report(iLevel, pExternal->u.universeReport);
1813             break;
1814             
1815         default:
1816             print_level(iLevel);
1817             printf("Type: %d\n", pExternal->which);
1818             break;
1819         }
1820     }
1821 }
1822
1823 static int process_resourceControlRequest (Z_ResourceControlRequest *req)
1824 {
1825     printf ("Received ResourceControlRequest.\n");
1826     print_referenceId(1, req->referenceId);
1827     print_int(1, "Suspended Flag", req->suspendedFlag);
1828     print_int(1, "Partial Results Available", req->partialResultsAvailable);
1829     print_int(1, "Response Required", req->responseRequired);
1830     print_int(1, "Triggered Request Flag", req->triggeredRequestFlag);
1831     print_external(1, req->resourceReport);
1832     return 0;
1833 }
1834
1835 void process_ESResponse(Z_ExtendedServicesResponse *res)
1836 {
1837     printf("Status: ");
1838     switch (*res->operationStatus)
1839     {
1840     case Z_ExtendedServicesResponse_done:
1841         printf ("done\n");
1842         break;
1843     case Z_ExtendedServicesResponse_accepted:
1844         printf ("accepted\n");
1845         break;
1846     case Z_ExtendedServicesResponse_failure:
1847         printf ("failure\n");
1848         display_diagrecs(res->diagnostics, res->num_diagnostics);
1849         break;
1850     default:
1851         printf ("unknown\n");
1852     }
1853     if ( (*res->operationStatus != Z_ExtendedServicesResponse_failure) &&
1854         (res->num_diagnostics != 0) ) {
1855         display_diagrecs(res->diagnostics, res->num_diagnostics);
1856     }
1857     print_refid (res->referenceId);
1858     if (res->taskPackage && 
1859         res->taskPackage->which == Z_External_extendedService)
1860     {
1861         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
1862         Odr_oct *id = taskPackage->targetReference;
1863         Z_External *ext = taskPackage->taskSpecificParameters;
1864         
1865         if (id)
1866         {
1867             printf ("Target Reference: ");
1868             print_stringn (id->buf, id->len);
1869             printf ("\n");
1870         }
1871         if (ext->which == Z_External_update)
1872         {
1873             Z_IUUpdateTaskPackage *utp = ext->u.update->u.taskPackage;
1874             if (utp && utp->targetPart)
1875             {
1876                 Z_IUTargetPart *targetPart = utp->targetPart;
1877                 int i;
1878
1879                 for (i = 0; i<targetPart->num_taskPackageRecords;  i++)
1880                 {
1881
1882                     Z_IUTaskPackageRecordStructure *tpr =
1883                         targetPart->taskPackageRecords[i];
1884                     printf ("task package record %d\n", i+1);
1885                     if (tpr->which == Z_IUTaskPackageRecordStructure_record)
1886                     {
1887                         display_record (tpr->u.record);
1888                     }
1889                     else
1890                     {
1891                         printf ("other type\n");
1892                     }
1893                 }
1894             }
1895         }
1896     }
1897     if (res->taskPackage && res->taskPackage->which == Z_External_octet)
1898     {
1899         Odr_oct *doc = res->taskPackage->u.octet_aligned;
1900         printf("%.*s\n", doc->len, doc->buf);
1901     }
1902 }
1903
1904 const char *get_ill_element (void *clientData, const char *element)
1905 {
1906     return 0;
1907 }
1908
1909 static Z_External *create_external_itemRequest(void)
1910 {
1911     struct ill_get_ctl ctl;
1912     ILL_ItemRequest *req;
1913     Z_External *r = 0;
1914     int item_request_size = 0;
1915     char *item_request_buf = 0;
1916
1917     ctl.odr = out;
1918     ctl.clientData = 0;
1919     ctl.f = get_ill_element;
1920     
1921     req = ill_get_ItemRequest(&ctl, "ill", 0);
1922     if (!req)
1923         printf ("ill_get_ItemRequest failed\n");
1924         
1925     if (!ill_ItemRequest (out, &req, 0, 0))
1926     {
1927         if (apdu_file)
1928         {
1929             ill_ItemRequest(print, &req, 0, 0);
1930             odr_reset(print);
1931         }
1932         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1933         if (item_request_buf)
1934             odr_setbuf (out, item_request_buf, item_request_size, 1);
1935         printf ("Couldn't encode ItemRequest, size %d\n", item_request_size);
1936         return 0;
1937     }
1938     else
1939     {
1940         oident oid;
1941         
1942         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1943         oid.proto = PROTO_GENERAL;
1944         oid.oclass = CLASS_GENERAL;
1945         oid.value = VAL_ISO_ILL_1;
1946         
1947         r = (Z_External *) odr_malloc (out, sizeof(*r));
1948         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1949         r->indirect_reference = 0;
1950         r->descriptor = 0;
1951         r->which = Z_External_single;
1952         
1953         r->u.single_ASN1_type = (Odr_oct *)
1954             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1955         r->u.single_ASN1_type->buf = (unsigned char *)
1956         odr_malloc (out, item_request_size);
1957         r->u.single_ASN1_type->len = item_request_size;
1958         r->u.single_ASN1_type->size = item_request_size;
1959         memcpy (r->u.single_ASN1_type->buf, item_request_buf,
1960                 item_request_size);
1961         
1962         do_hex_dump(item_request_buf,item_request_size);
1963     }
1964     return r;
1965 }
1966
1967 static Z_External *create_external_ILL_APDU(int which)
1968 {
1969     struct ill_get_ctl ctl;
1970     ILL_APDU *ill_apdu;
1971     Z_External *r = 0;
1972     int ill_request_size = 0;
1973     char *ill_request_buf = 0;
1974         
1975     ctl.odr = out;
1976     ctl.clientData = 0;
1977     ctl.f = get_ill_element;
1978
1979     ill_apdu = ill_get_APDU(&ctl, "ill", 0);
1980
1981     if (!ill_APDU (out, &ill_apdu, 0, 0))
1982     {
1983         if (apdu_file)
1984         {
1985             printf ("-------------------\n");
1986             ill_APDU(print, &ill_apdu, 0, 0);
1987             odr_reset(print);
1988             printf ("-------------------\n");
1989         }
1990         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1991         if (ill_request_buf)
1992             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
1993         printf ("Couldn't encode ILL-Request, size %d\n", ill_request_size);
1994         return 0;
1995     }
1996     else
1997     {
1998         oident oid;
1999         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
2000         
2001         oid.proto = PROTO_GENERAL;
2002         oid.oclass = CLASS_GENERAL;
2003         oid.value = VAL_ISO_ILL_1;
2004         
2005         r = (Z_External *) odr_malloc (out, sizeof(*r));
2006         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
2007         r->indirect_reference = 0;
2008         r->descriptor = 0;
2009         r->which = Z_External_single;
2010         
2011         r->u.single_ASN1_type = (Odr_oct *)
2012             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
2013         r->u.single_ASN1_type->buf = (unsigned char *)
2014         odr_malloc (out, ill_request_size);
2015         r->u.single_ASN1_type->len = ill_request_size;
2016         r->u.single_ASN1_type->size = ill_request_size;
2017         memcpy (r->u.single_ASN1_type->buf, ill_request_buf, ill_request_size);
2018 /*         printf ("len = %d\n", ill_request_size); */
2019 /*              do_hex_dump(ill_request_buf,ill_request_size); */
2020 /*              printf("--- end of extenal\n"); */
2021
2022     }
2023     return r;
2024 }
2025
2026
2027 static Z_External *create_ItemOrderExternal(const char *type, int itemno)
2028 {
2029     Z_External *r = (Z_External *) odr_malloc(out, sizeof(Z_External));
2030     oident ItemOrderRequest;
2031   
2032     ItemOrderRequest.proto = PROTO_Z3950;
2033     ItemOrderRequest.oclass = CLASS_EXTSERV;
2034     ItemOrderRequest.value = VAL_ITEMORDER;
2035  
2036     r->direct_reference = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest)); 
2037     r->indirect_reference = 0;
2038     r->descriptor = 0;
2039
2040     r->which = Z_External_itemOrder;
2041
2042     r->u.itemOrder = (Z_ItemOrder *) odr_malloc(out,sizeof(Z_ItemOrder));
2043     memset(r->u.itemOrder, 0, sizeof(Z_ItemOrder));
2044     r->u.itemOrder->which=Z_IOItemOrder_esRequest;
2045
2046     r->u.itemOrder->u.esRequest = (Z_IORequest *) 
2047         odr_malloc(out,sizeof(Z_IORequest));
2048     memset(r->u.itemOrder->u.esRequest, 0, sizeof(Z_IORequest));
2049
2050     r->u.itemOrder->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
2051         odr_malloc(out,sizeof(Z_IOOriginPartToKeep));
2052     memset(r->u.itemOrder->u.esRequest->toKeep, 0, sizeof(Z_IOOriginPartToKeep));
2053     r->u.itemOrder->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
2054         odr_malloc(out,sizeof(Z_IOOriginPartNotToKeep));
2055     memset(r->u.itemOrder->u.esRequest->notToKeep, 0, sizeof(Z_IOOriginPartNotToKeep));
2056
2057     r->u.itemOrder->u.esRequest->toKeep->supplDescription = NULL;
2058     r->u.itemOrder->u.esRequest->toKeep->contact = NULL;
2059     r->u.itemOrder->u.esRequest->toKeep->addlBilling = NULL;
2060
2061     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem =
2062         (Z_IOResultSetItem *) odr_malloc(out, sizeof(Z_IOResultSetItem));
2063     memset(r->u.itemOrder->u.esRequest->notToKeep->resultSetItem, 0, sizeof(Z_IOResultSetItem));
2064     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->resultSetId = "1";
2065
2066     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item =
2067         (int *) odr_malloc(out, sizeof(int));
2068     *r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item = itemno;
2069
2070     if (!strcmp (type, "item") || !strcmp(type, "2"))
2071     {
2072         printf ("using item-request\n");
2073         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
2074             create_external_itemRequest();
2075     }
2076     else if (!strcmp(type, "ill") || !strcmp(type, "1"))
2077     {
2078         printf ("using ILL-request\n");
2079         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
2080             create_external_ILL_APDU(ILL_APDU_ILL_Request);
2081     }
2082     else if (!strcmp(type, "xml") || !strcmp(type, "3"))
2083     {
2084     const char *xml_buf =
2085         "<itemorder>\n"
2086         "  <type>request</type>\n"
2087         "  <libraryNo>000200</libraryNo>\n"
2088         "  <borrowerTicketNo> 1212 </borrowerTicketNo>\n"
2089         "</itemorder>";
2090         r->u.itemOrder->u.esRequest->notToKeep->itemRequest =
2091             z_ext_record (out, VAL_TEXT_XML, xml_buf, strlen(xml_buf));
2092     }
2093     else
2094         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 0;
2095
2096     return r;
2097 }
2098
2099 static int send_itemorder(const char *type, int itemno)
2100 {
2101     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest);
2102     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2103     oident ItemOrderRequest;
2104
2105
2106     req->referenceId = set_refid (out);
2107
2108     ItemOrderRequest.proto = PROTO_Z3950;
2109     ItemOrderRequest.oclass = CLASS_EXTSERV;
2110     ItemOrderRequest.value = VAL_ITEMORDER;
2111     req->packageType = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest));
2112     req->packageName = esPackageName;
2113
2114     req->taskSpecificParameters = create_ItemOrderExternal(type, itemno);
2115
2116     send_apdu(apdu);
2117     return 0;
2118 }
2119
2120 static int only_z3950(void)
2121 {
2122     if (!conn)
2123     {
2124         printf ("Not connected yet\n");
2125         return 1;
2126     }
2127     if (protocol == PROTO_HTTP)
2128     {
2129         printf ("Not supported by SRW\n");
2130         return 1;
2131     }
2132     return 0;
2133 }
2134
2135 static int cmd_update_common(const char *arg, int version);
2136
2137 static int cmd_update(const char *arg)
2138 {
2139     return cmd_update_common(arg, 1);
2140 }
2141
2142 static int cmd_update0(const char *arg)
2143 {
2144     return cmd_update_common(arg, 0);
2145 }
2146
2147 static int cmd_update_Z3950(int version, int action_no, const char *recid,
2148                             char *rec_buf, int rec_len);
2149
2150 static int cmd_update_SRW(int action_no, const char *recid,
2151                           char *rec_buf, int rec_len);
2152
2153 static int cmd_update_common(const char *arg, int version)
2154 {
2155     char action[20], recid[20];
2156     char *rec_buf;
2157     int rec_len;
2158     int action_no;
2159     int noread = 0;
2160
2161     *action = 0;
2162     *recid = 0;
2163     sscanf (arg, "%19s %19s%n", action, recid, &noread);
2164     if (noread == 0)
2165     {
2166         printf("Update must be followed by action and recid\n");
2167         printf(" where action is one of insert,replace,delete.update\n");
2168         printf(" recid is some record ID (any string)\n");
2169         return 0;
2170     }
2171
2172     if (!strcmp (action, "insert"))
2173         action_no = Z_IUOriginPartToKeep_recordInsert;
2174     else if (!strcmp (action, "replace"))
2175         action_no = Z_IUOriginPartToKeep_recordReplace;
2176     else if (!strcmp (action, "delete"))
2177         action_no = Z_IUOriginPartToKeep_recordDelete;
2178     else if (!strcmp (action, "update"))
2179         action_no = Z_IUOriginPartToKeep_specialUpdate;
2180     else 
2181     {
2182         printf ("Bad action: %s\n", action);
2183         printf ("Possible values: insert, replace, delete, update\n");
2184         return 0;
2185     }
2186
2187     arg += noread;
2188     if (parse_cmd_doc(&arg, out, &rec_buf, &rec_len, 1) == 0)
2189         return 0;
2190
2191 #if YAZ_HAVE_XML2
2192     if (protocol == PROTO_HTTP)
2193         return cmd_update_SRW(action_no, recid, rec_buf, rec_len);
2194 #endif
2195     return cmd_update_Z3950(version, action_no, recid, rec_buf, rec_len);
2196 }
2197
2198 #if YAZ_HAVE_XML2
2199 static int cmd_update_SRW(int action_no, const char *recid,
2200                           char *rec_buf, int rec_len)
2201 {
2202     if (!conn)
2203         cmd_open(0);
2204     if (!conn)
2205         return 0;
2206     else
2207     {
2208         Z_SRW_PDU *srw = yaz_srw_get(out, Z_SRW_update_request);
2209         Z_SRW_updateRequest *sr = srw->u.update_request;
2210
2211         switch(action_no)
2212         {
2213         case Z_IUOriginPartToKeep_recordInsert:
2214             sr->operation = "info:srw/action/1/create";
2215             break;
2216         case Z_IUOriginPartToKeep_recordReplace:
2217             sr->operation = "info:srw/action/1/replace";
2218             break;
2219         case Z_IUOriginPartToKeep_recordDelete:
2220             sr->operation = "info:srw/action/1/delete";
2221             break;
2222         }
2223         if (rec_buf)
2224         {
2225             sr->record = yaz_srw_get_record(out);
2226             sr->record->recordData_buf = rec_buf;
2227             sr->record->recordData_len = rec_len;
2228             sr->record->recordSchema = record_schema;
2229         }
2230         if (recid)
2231             sr->recordId = odr_strdup(out, recid);
2232         return send_srw(srw);
2233     }
2234 }
2235 #endif
2236                           
2237 static int cmd_update_Z3950(int version, int action_no, const char *recid,
2238                             char *rec_buf, int rec_len)
2239 {
2240     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest );
2241     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2242     Z_External *r;
2243     Z_External *record_this = 0;
2244
2245     if (rec_buf)
2246         record_this = z_ext_record (out, VAL_TEXT_XML, rec_buf, rec_len);
2247     else
2248     {
2249         if (!record_last)
2250         {
2251             printf ("No last record (update ignored)\n");
2252             return 0;
2253         }
2254         record_this = record_last;
2255     }
2256
2257     req->packageType =
2258         yaz_oidval_to_z3950oid(out, CLASS_EXTSERV,
2259                                version == 0 ? VAL_DBUPDATE0 : VAL_DBUPDATE);
2260
2261     req->packageName = esPackageName;
2262     
2263     req->referenceId = set_refid (out);
2264
2265     r = req->taskSpecificParameters = (Z_External *)
2266         odr_malloc (out, sizeof(*r));
2267     r->direct_reference = req->packageType;
2268     r->indirect_reference = 0;
2269     r->descriptor = 0;
2270     if (version == 0)
2271     {
2272         Z_IU0OriginPartToKeep *toKeep;
2273         Z_IU0SuppliedRecords *notToKeep;
2274
2275         r->which = Z_External_update0;
2276         r->u.update0 = (Z_IU0Update *) odr_malloc(out, sizeof(*r->u.update0));
2277         r->u.update0->which = Z_IUUpdate_esRequest;
2278         r->u.update0->u.esRequest = (Z_IU0UpdateEsRequest *)
2279             odr_malloc(out, sizeof(*r->u.update0->u.esRequest));
2280         toKeep = r->u.update0->u.esRequest->toKeep = (Z_IU0OriginPartToKeep *)
2281             odr_malloc(out, sizeof(*r->u.update0->u.esRequest->toKeep));
2282         
2283         toKeep->databaseName = databaseNames[0];
2284         toKeep->schema = 0;
2285         toKeep->elementSetName = 0;
2286
2287         toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
2288         *toKeep->action = action_no;
2289         
2290         notToKeep = r->u.update0->u.esRequest->notToKeep = (Z_IU0SuppliedRecords *)
2291             odr_malloc(out, sizeof(*r->u.update0->u.esRequest->notToKeep));
2292         notToKeep->num = 1;
2293         notToKeep->elements = (Z_IU0SuppliedRecords_elem **)
2294             odr_malloc(out, sizeof(*notToKeep->elements));
2295         notToKeep->elements[0] = (Z_IU0SuppliedRecords_elem *)
2296             odr_malloc(out, sizeof(**notToKeep->elements));
2297         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
2298         if (*recid && strcmp(recid, "none"))
2299         {
2300             notToKeep->elements[0]->u.opaque = (Odr_oct *)
2301                 odr_malloc (out, sizeof(Odr_oct));
2302             notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
2303             notToKeep->elements[0]->u.opaque->size = strlen(recid);
2304             notToKeep->elements[0]->u.opaque->len = strlen(recid);
2305         }
2306         else
2307             notToKeep->elements[0]->u.opaque = 0;
2308         notToKeep->elements[0]->supplementalId = 0;
2309         notToKeep->elements[0]->correlationInfo = 0;
2310         notToKeep->elements[0]->record = record_this;
2311     }
2312     else
2313     {
2314         Z_IUOriginPartToKeep *toKeep;
2315         Z_IUSuppliedRecords *notToKeep;
2316
2317         r->which = Z_External_update;
2318         r->u.update = (Z_IUUpdate *) odr_malloc(out, sizeof(*r->u.update));
2319         r->u.update->which = Z_IUUpdate_esRequest;
2320         r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
2321             odr_malloc(out, sizeof(*r->u.update->u.esRequest));
2322         toKeep = r->u.update->u.esRequest->toKeep = (Z_IUOriginPartToKeep *)
2323             odr_malloc(out, sizeof(*r->u.update->u.esRequest->toKeep));
2324         
2325         toKeep->databaseName = databaseNames[0];
2326         toKeep->schema = 0;
2327         toKeep->elementSetName = 0;
2328         toKeep->actionQualifier = 0;
2329         toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
2330         *toKeep->action = action_no;
2331
2332         notToKeep = r->u.update->u.esRequest->notToKeep = (Z_IUSuppliedRecords *)
2333             odr_malloc(out, sizeof(*r->u.update->u.esRequest->notToKeep));
2334         notToKeep->num = 1;
2335         notToKeep->elements = (Z_IUSuppliedRecords_elem **)
2336             odr_malloc(out, sizeof(*notToKeep->elements));
2337         notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
2338             odr_malloc(out, sizeof(**notToKeep->elements));
2339         notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
2340         if (*recid)
2341         {
2342             notToKeep->elements[0]->u.opaque = (Odr_oct *)
2343                 odr_malloc (out, sizeof(Odr_oct));
2344             notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
2345             notToKeep->elements[0]->u.opaque->size = strlen(recid);
2346             notToKeep->elements[0]->u.opaque->len = strlen(recid);
2347         }
2348         else
2349             notToKeep->elements[0]->u.opaque = 0;
2350         notToKeep->elements[0]->supplementalId = 0;
2351         notToKeep->elements[0]->correlationInfo = 0;
2352         notToKeep->elements[0]->record = record_this;
2353     }
2354     
2355     send_apdu(apdu);
2356
2357     return 2;
2358 }
2359
2360 static int cmd_xmles(const char *arg)
2361 {
2362     if (only_z3950())
2363         return 1;
2364     else
2365     {
2366         char *asn_buf = 0;
2367         int noread = 0;
2368         char oid_str[51];
2369         int oid_value_xmles = VAL_XMLES;
2370         Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest);
2371         Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
2372         
2373
2374         Z_External *ext = (Z_External *) odr_malloc(out, sizeof(*ext));
2375         
2376         req->referenceId = set_refid (out);
2377         req->taskSpecificParameters = ext;
2378         ext->indirect_reference = 0;
2379         ext->descriptor = 0;
2380         ext->which = Z_External_octet;
2381         ext->u.single_ASN1_type = (Odr_oct *) odr_malloc (out, sizeof(Odr_oct));        
2382         sscanf(arg, "%50s%n", oid_str, &noread);
2383         if (noread == 0)
2384         {
2385             printf("Missing OID for xmles\n");
2386             return 0;
2387         }
2388         arg += noread;
2389         oid_value_xmles  = oid_getvalbyname(oid_str);
2390         if (oid_value_xmles == VAL_NONE)
2391         {
2392             printf("Bad OID: %s\n", oid_str);
2393             return 0;
2394         }
2395
2396         if (parse_cmd_doc(&arg, out, &asn_buf,
2397                           &ext->u.single_ASN1_type->len, 0) == 0)
2398             return 0;
2399
2400         ext->u.single_ASN1_type->buf = (unsigned char *) asn_buf;
2401
2402         req->packageType = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV,
2403                                                   oid_value_xmles);
2404         
2405         ext->direct_reference = yaz_oidval_to_z3950oid(out, CLASS_EXTSERV,
2406                                                        oid_value_xmles);
2407         send_apdu(apdu);
2408         
2409         return 2;
2410     }
2411 }
2412
2413 static int cmd_itemorder(const char *arg)
2414 {
2415     char type[12];
2416     int itemno;
2417    
2418     if (only_z3950())
2419         return 1;
2420     if (sscanf (arg, "%10s %d", type, &itemno) != 2)
2421         return 0;
2422
2423     printf("Item order request\n");
2424     fflush(stdout);
2425     send_itemorder(type, itemno);
2426     return 2;
2427 }
2428
2429 static void show_opt(const char *arg, void *clientData)
2430 {
2431     printf ("%s ", arg);
2432 }
2433
2434 static int cmd_zversion(const char *arg)
2435 {
2436     if (*arg && arg)
2437         z3950_version = atoi(arg);
2438     else
2439         printf ("version is %d\n", z3950_version);
2440     return 0;
2441 }
2442
2443 static int cmd_options(const char *arg)
2444 {
2445     if (*arg)
2446     {
2447         int r;
2448         int pos;
2449         r = yaz_init_opt_encode(&z3950_options, arg, &pos);
2450         if (r == -1)
2451             printf("Unknown option(s) near %s\n", arg+pos);
2452     }
2453     else
2454     {
2455         yaz_init_opt_decode(&z3950_options, show_opt, 0);
2456         printf ("\n");
2457     }
2458     return 0;
2459 }
2460
2461 static int cmd_explain(const char *arg)
2462 {
2463     if (protocol != PROTO_HTTP)
2464         return 0;
2465 #if YAZ_HAVE_XML2
2466     if (!conn)
2467         cmd_open(0);
2468     if (conn)
2469     {
2470         Z_SRW_PDU *sr = 0;
2471         
2472         setno = 1;
2473         
2474         /* save this for later .. when fetching individual records */
2475         sr = yaz_srw_get(out, Z_SRW_explain_request);
2476         if (recordsyntax_size > 0 && recordsyntax_list[0] == VAL_TEXT_XML)
2477             sr->u.explain_request->recordPacking = "xml";
2478         send_srw(sr);
2479         return 2;
2480     }
2481 #endif
2482     return 0;
2483 }
2484
2485 static int cmd_init(const char *arg)
2486 {
2487     if (*arg)
2488     {
2489         strncpy (cur_host, arg, sizeof(cur_host)-1);
2490         cur_host[sizeof(cur_host)-1] = 0;
2491     }
2492     if (only_z3950())
2493         return 1;
2494     send_initRequest(cur_host);
2495     return 2;
2496 }
2497
2498 static int cmd_sru(const char *arg)
2499 {
2500     if (!*arg)
2501     {
2502         printf("SRU method is: %s\n", sru_method);
2503     }
2504     else
2505     {
2506         if (!yaz_matchstr(arg, "post"))
2507             sru_method = "post";
2508         else if (!yaz_matchstr(arg, "get"))
2509             sru_method = "get";
2510         else if (!yaz_matchstr(arg, "soap"))
2511             sru_method = "soap";
2512         else
2513         {
2514             printf("Unknown SRU method: %s\n", arg);
2515             printf("Specify one of POST, GET, SOAP\n");
2516         }
2517     }
2518     return 0;
2519 }
2520
2521 static int cmd_find(const char *arg)
2522 {
2523     if (!*arg)
2524     {
2525         printf("Find what?\n");
2526         return 0;
2527     }
2528     if (protocol == PROTO_HTTP)
2529     {
2530 #if YAZ_HAVE_XML2
2531         if (!conn)
2532             cmd_open(0);
2533         if (!conn)
2534             return 0;
2535         if (!send_SRW_searchRequest(arg))
2536             return 0;
2537 #else
2538         return 0;
2539 #endif
2540     }
2541     else
2542     {
2543         if (!conn)
2544         {
2545             try_reconnect(); 
2546             
2547             if (!conn) {                                        
2548                 printf("Not connected yet\n");
2549                 return 0;
2550             }
2551         }
2552         if (!send_searchRequest(arg))
2553             return 0;
2554     }
2555     return 2;
2556 }
2557
2558 static int cmd_delete(const char *arg)
2559 {
2560     if (only_z3950())
2561         return 0;
2562     if (!send_deleteResultSetRequest(arg))
2563         return 0;
2564     return 2;
2565 }
2566
2567 static int cmd_ssub(const char *arg)
2568 {
2569     if (!(smallSetUpperBound = atoi(arg)))
2570         return 0;
2571     return 1;
2572 }
2573
2574 static int cmd_lslb(const char *arg)
2575 {
2576     if (!(largeSetLowerBound = atoi(arg)))
2577         return 0;
2578     return 1;
2579 }
2580
2581 static int cmd_mspn(const char *arg)
2582 {
2583     if (!(mediumSetPresentNumber = atoi(arg)))
2584         return 0;
2585     return 1;
2586 }
2587
2588 static int cmd_status(const char *arg)
2589 {
2590     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
2591     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
2592     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
2593     return 1;
2594 }
2595
2596 static int cmd_setnames(const char *arg)
2597 {
2598     if (*arg == '1')         /* enable ? */
2599         setnumber = 0;
2600     else if (*arg == '0')    /* disable ? */
2601         setnumber = -1;
2602     else if (setnumber < 0)  /* no args, toggle .. */
2603         setnumber = 0;
2604     else
2605         setnumber = -1;
2606    
2607     if (setnumber >= 0)
2608         printf("Set numbering enabled.\n");
2609     else
2610         printf("Set numbering disabled.\n");
2611     return 1;
2612 }
2613
2614 /* PRESENT SERVICE ----------------------------- */
2615
2616 static void parse_show_args(const char *arg_c, char *setstring,
2617                             int *start, int *number)
2618 {
2619     char arg[40];
2620     char *p;
2621
2622     strncpy(arg, arg_c, sizeof(arg)-1);
2623     arg[sizeof(arg)-1] = '\0';
2624
2625     if ((p = strchr(arg, '+')))
2626     {
2627         *number = atoi(p + 1);
2628         *p = '\0';
2629     }
2630     if (*arg)
2631     {
2632         if (!strcmp(arg, "all"))
2633         {
2634             *number = last_hit_count;
2635             *start = 1;
2636         }
2637         else
2638             *start = atoi(arg);
2639     }
2640     if (p && (p=strchr(p+1, '+')))
2641         strcpy (setstring, p+1);
2642     else if (setnumber >= 0)
2643         sprintf(setstring, "%d", setnumber);
2644     else
2645         *setstring = '\0';
2646 }
2647
2648 static int send_presentRequest(const char *arg)
2649 {
2650     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
2651     Z_PresentRequest *req = apdu->u.presentRequest;
2652     Z_RecordComposition compo;
2653     int nos = 1;
2654     char setstring[100];
2655
2656     req->referenceId = set_refid (out);
2657
2658     parse_show_args(arg, setstring, &setno, &nos);
2659     if (*setstring)
2660         req->resultSetId = setstring;
2661
2662     req->resultSetStartPoint = &setno;
2663     req->numberOfRecordsRequested = &nos;
2664
2665     if (recordsyntax_size == 1)
2666         req->preferredRecordSyntax =
2667             yaz_oidval_to_z3950oid(out, CLASS_RECSYN, recordsyntax_list[0]);
2668
2669     if (record_schema || recordsyntax_size >= 2)
2670     {
2671         req->recordComposition = &compo;
2672         compo.which = Z_RecordComp_complex;
2673         compo.u.complex = (Z_CompSpec *)
2674             odr_malloc(out, sizeof(*compo.u.complex));
2675         compo.u.complex->selectAlternativeSyntax = (bool_t *) 
2676             odr_malloc(out, sizeof(bool_t));
2677         *compo.u.complex->selectAlternativeSyntax = 0;
2678
2679         compo.u.complex->generic = (Z_Specification *)
2680             odr_malloc(out, sizeof(*compo.u.complex->generic));
2681         
2682         compo.u.complex->generic->which = Z_Schema_oid;
2683         if (!record_schema)
2684             compo.u.complex->generic->schema.oid = 0;
2685         else 
2686         {
2687             compo.u.complex->generic->schema.oid =
2688                 yaz_str_to_z3950oid(out, CLASS_SCHEMA, record_schema);
2689             
2690             if (!compo.u.complex->generic->schema.oid)
2691             {
2692                 /* OID wasn't a schema! Try record syntax instead. */
2693                 compo.u.complex->generic->schema.oid = (Odr_oid *)
2694                     yaz_str_to_z3950oid(out, CLASS_RECSYN, record_schema);
2695             }
2696         }
2697         if (!elementSetNames)
2698             compo.u.complex->generic->elementSpec = 0;
2699         else
2700         {
2701             compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
2702                 odr_malloc(out, sizeof(Z_ElementSpec));
2703             compo.u.complex->generic->elementSpec->which =
2704                 Z_ElementSpec_elementSetName;
2705             compo.u.complex->generic->elementSpec->u.elementSetName =
2706                 elementSetNames->u.generic;
2707         }
2708         compo.u.complex->num_dbSpecific = 0;
2709         compo.u.complex->dbSpecific = 0;
2710         if (recordsyntax_size >= 2)
2711         {
2712             int i;
2713             compo.u.complex->num_recordSyntax = recordsyntax_size;
2714             compo.u.complex->recordSyntax = (Odr_oid **)
2715                 odr_malloc(out, recordsyntax_size * sizeof(Odr_oid*));
2716             for (i = 0; i < recordsyntax_size; i++)
2717             compo.u.complex->recordSyntax[i] =                 
2718                 yaz_oidval_to_z3950oid(out, CLASS_RECSYN,
2719                                        recordsyntax_list[i]);
2720         }
2721         else
2722         {
2723             compo.u.complex->num_recordSyntax = 0;
2724             compo.u.complex->recordSyntax = 0;
2725         }
2726     }
2727     else if (elementSetNames)
2728     {
2729         req->recordComposition = &compo;
2730         compo.which = Z_RecordComp_simple;
2731         compo.u.simple = elementSetNames;
2732     }
2733     send_apdu(apdu);
2734     printf("Sent presentRequest (%d+%d).\n", setno, nos);
2735     return 2;
2736 }
2737
2738 #if YAZ_HAVE_XML2
2739 static int send_SRW_presentRequest(const char *arg)
2740 {
2741     char setstring[100];
2742     int nos = 1;
2743     Z_SRW_PDU *sr = srw_sr;
2744
2745     if (!sr)
2746         return 0;
2747     parse_show_args(arg, setstring, &setno, &nos);
2748     sr->u.request->startRecord = odr_intdup(out, setno);
2749     sr->u.request->maximumRecords = odr_intdup(out, nos);
2750     if (record_schema)
2751         sr->u.request->recordSchema = record_schema;
2752     if (recordsyntax_size == 1 && recordsyntax_list[0] == VAL_TEXT_XML)
2753         sr->u.request->recordPacking = "xml";
2754     return send_srw(sr);
2755 }
2756 #endif
2757
2758 static void close_session (void)
2759 {
2760     if (conn)
2761         cs_close (conn);
2762     conn = 0;
2763     sent_close = 0;
2764     odr_reset(out);
2765     odr_reset(in);
2766     odr_reset(print);
2767     last_hit_count = 0;
2768 }
2769
2770 void process_close(Z_Close *req)
2771 {
2772     Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
2773     Z_Close *res = apdu->u.close;
2774
2775     static char *reasons[] =
2776     {
2777         "finished",
2778         "shutdown",
2779         "system problem",
2780         "cost limit reached",
2781         "resources",
2782         "security violation",
2783         "protocolError",
2784         "lack of activity",
2785         "peer abort",
2786         "unspecified"
2787     };
2788
2789     printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
2790         req->diagnosticInformation ? req->diagnosticInformation : "NULL");
2791     if (sent_close)
2792         close_session ();
2793     else
2794     {
2795         *res->closeReason = Z_Close_finished;
2796         send_apdu(apdu);
2797         printf("Sent response.\n");
2798         sent_close = 1;
2799     }
2800 }
2801
2802 static int cmd_show(const char *arg)
2803 {
2804     if (protocol == PROTO_HTTP)
2805     {
2806 #if YAZ_HAVE_XML2
2807         if (!conn)
2808             cmd_open(0);
2809         if (!conn)
2810             return 0;
2811         if (!send_SRW_presentRequest(arg))
2812             return 0;
2813 #else
2814         return 0;
2815 #endif
2816     }
2817     else
2818     {
2819         if (!conn)
2820         {
2821             printf("Not connected yet\n");
2822             return 0;
2823         }
2824         if (!send_presentRequest(arg))
2825             return 0;
2826     }
2827     return 2;
2828 }
2829
2830 void exit_client(int code)
2831 {
2832     file_history_save(file_history);
2833     file_history_destroy(&file_history);
2834     exit(code);
2835 }
2836
2837 int cmd_quit(const char *arg)
2838 {
2839     printf("See you later, alligator.\n");
2840     xmalloc_trav ("");
2841     exit_client(0);
2842     return 0;
2843 }
2844
2845 int cmd_cancel(const char *arg)
2846 {
2847     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
2848     Z_TriggerResourceControlRequest *req =
2849         apdu->u.triggerResourceControlRequest;
2850     bool_t rfalse = 0;
2851     char command[16];
2852   
2853     *command = '\0';
2854     sscanf(arg, "%15s", command);
2855
2856     if (only_z3950())
2857         return 0;
2858     if (session_initResponse &&
2859         !ODR_MASK_GET(session_initResponse->options,
2860                       Z_Options_triggerResourceCtrl))
2861     {
2862         printf("Target doesn't support cancel (trigger resource ctrl)\n");
2863         return 0;
2864     }
2865     *req->requestedAction = Z_TriggerResourceControlRequest_cancel;
2866     req->resultSetWanted = &rfalse;
2867     req->referenceId = set_refid (out);
2868
2869     send_apdu(apdu);
2870     printf("Sent cancel request\n");
2871     if (!strcmp(command, "wait"))
2872          return 2;
2873     return 1;
2874 }
2875
2876
2877 int cmd_cancel_find(const char *arg) {
2878     int fres;
2879     fres=cmd_find(arg);
2880     if( fres > 0 ) {
2881         return cmd_cancel("");
2882     };
2883     return fres;
2884 }
2885
2886 int send_scanrequest(const char *query, int pp, int num, const char *term)
2887 {
2888     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
2889     Z_ScanRequest *req = apdu->u.scanRequest;
2890     
2891     if (only_z3950())
2892         return 0;
2893     if (queryType == QueryType_CCL2RPN)
2894     {
2895         int error, pos;
2896         struct ccl_rpn_node *rpn;
2897
2898         rpn = ccl_find_str (bibset,  query, &error, &pos);
2899         if (error)
2900         {
2901             printf("CCL ERROR: %s\n", ccl_err_msg(error));
2902             return -1;
2903         }
2904         req->attributeSet =
2905             yaz_oidval_to_z3950oid(out, CLASS_ATTSET, VAL_BIB1);
2906         if (!(req->termListAndStartPoint = ccl_scan_query (out, rpn)))
2907         {
2908             printf("Couldn't convert CCL to Scan term\n");
2909             return -1;
2910         }
2911         ccl_rpn_delete (rpn);
2912     }
2913     else
2914     {
2915         YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
2916
2917         if (!(req->termListAndStartPoint =
2918               yaz_pqf_scan(pqf_parser, out, &req->attributeSet, query)))
2919         {
2920             const char *pqf_msg;
2921             size_t off;
2922             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
2923             int ioff = off;
2924             printf("%*s^\n", ioff+7, "");
2925             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
2926             yaz_pqf_destroy (pqf_parser);
2927             return -1;
2928         }
2929         yaz_pqf_destroy (pqf_parser);
2930     }
2931     if (term && *term)
2932     {
2933         if (req->termListAndStartPoint->term &&
2934             req->termListAndStartPoint->term->which == Z_Term_general &&
2935             req->termListAndStartPoint->term->u.general)
2936         {
2937             req->termListAndStartPoint->term->u.general->buf =
2938                 (unsigned char *) odr_strdup(out, term);
2939             req->termListAndStartPoint->term->u.general->len =
2940                 req->termListAndStartPoint->term->u.general->size =
2941                 strlen(term);
2942         }
2943     }
2944     req->referenceId = set_refid (out);
2945     req->num_databaseNames = num_databaseNames;
2946     req->databaseNames = databaseNames;
2947     req->numberOfTermsRequested = &num;
2948     req->preferredPositionInResponse = &pp;
2949     req->stepSize = odr_intdup(out, scan_stepSize);
2950     send_apdu(apdu);
2951     return 2;
2952 }
2953
2954 int send_sortrequest(const char *arg, int newset)
2955 {
2956     Z_APDU *apdu = zget_APDU(out, Z_APDU_sortRequest);
2957     Z_SortRequest *req = apdu->u.sortRequest;
2958     Z_SortKeySpecList *sksl = (Z_SortKeySpecList *)
2959         odr_malloc (out, sizeof(*sksl));
2960     char setstring[32];
2961
2962     if (only_z3950())
2963         return 0;
2964     if (setnumber >= 0)
2965         sprintf (setstring, "%d", setnumber);
2966     else
2967         sprintf (setstring, "default");
2968
2969     req->referenceId = set_refid (out);
2970
2971     req->num_inputResultSetNames = 1;
2972     req->inputResultSetNames = (Z_InternationalString **)
2973         odr_malloc (out, sizeof(*req->inputResultSetNames));
2974     req->inputResultSetNames[0] = odr_strdup (out, setstring);
2975
2976     if (newset && setnumber >= 0)
2977         sprintf (setstring, "%d", ++setnumber);
2978
2979     req->sortedResultSetName = odr_strdup (out, setstring);
2980
2981     req->sortSequence = yaz_sort_spec (out, arg);
2982     if (!req->sortSequence)
2983     {
2984         printf ("Missing sort specifications\n");
2985         return -1;
2986     }
2987     send_apdu(apdu);
2988     return 2;
2989 }
2990
2991 void display_term(Z_TermInfo *t)
2992 {
2993     if (t->displayTerm)
2994         printf("%s", t->displayTerm);
2995     else if (t->term->which == Z_Term_general)
2996     {
2997         printf("%.*s", t->term->u.general->len, t->term->u.general->buf);
2998         sprintf(last_scan_line, "%.*s", t->term->u.general->len,
2999             t->term->u.general->buf);
3000     }
3001     else
3002         printf("Term (not general)");
3003     if (t->globalOccurrences)
3004         printf (" (%d)\n", *t->globalOccurrences);
3005     else
3006         printf ("\n");
3007 }
3008
3009 void process_scanResponse(Z_ScanResponse *res)
3010 {
3011     int i;
3012     Z_Entry **entries = NULL;
3013     int num_entries = 0;
3014    
3015     printf("Received ScanResponse\n"); 
3016     print_refid (res->referenceId);
3017     printf("%d entries", *res->numberOfEntriesReturned);
3018     if (res->positionOfTerm)
3019         printf (", position=%d", *res->positionOfTerm); 
3020     printf ("\n");
3021     if (*res->scanStatus != Z_Scan_success)
3022         printf("Scan returned code %d\n", *res->scanStatus);
3023     if (!res->entries)
3024         return;
3025     if ((entries = res->entries->entries))
3026         num_entries = res->entries->num_entries;
3027     for (i = 0; i < num_entries; i++)
3028     {
3029         int pos_term = res->positionOfTerm ? *res->positionOfTerm : -1;
3030         if (entries[i]->which == Z_Entry_termInfo)
3031         {
3032             printf("%c ", i + 1 == pos_term ? '*' : ' ');
3033             display_term(entries[i]->u.termInfo);
3034         }
3035         else
3036             display_diagrecs(&entries[i]->u.surrogateDiagnostic, 1);
3037     }
3038     if (res->entries->nonsurrogateDiagnostics)
3039         display_diagrecs (res->entries->nonsurrogateDiagnostics,
3040                           res->entries->num_nonsurrogateDiagnostics);
3041 }
3042
3043 void process_sortResponse(Z_SortResponse *res)
3044 {
3045     printf("Received SortResponse: status=");
3046     switch (*res->sortStatus)
3047     {
3048     case Z_SortResponse_success:
3049         printf ("success"); break;
3050     case Z_SortResponse_partial_1:
3051         printf ("partial"); break;
3052     case Z_SortResponse_failure:
3053         printf ("failure"); break;
3054     default:
3055         printf ("unknown (%d)", *res->sortStatus);
3056     }
3057     printf ("\n");
3058     print_refid (res->referenceId);
3059     if (res->diagnostics)
3060         display_diagrecs(res->diagnostics,
3061                          res->num_diagnostics);
3062 }
3063
3064 void process_deleteResultSetResponse (Z_DeleteResultSetResponse *res)
3065 {
3066     printf("Got deleteResultSetResponse status=%d\n",
3067            *res->deleteOperationStatus);
3068     if (res->deleteListStatuses)
3069     {
3070         int i;
3071         for (i = 0; i < res->deleteListStatuses->num; i++)
3072         {
3073             printf ("%s status=%d\n", res->deleteListStatuses->elements[i]->id,
3074                     *res->deleteListStatuses->elements[i]->status);
3075         }
3076     }
3077 }
3078
3079 int cmd_sort_generic(const char *arg, int newset)
3080 {
3081     if (only_z3950())
3082         return 0;
3083     if (session_initResponse && 
3084         !ODR_MASK_GET(session_initResponse->options, Z_Options_sort))
3085     {
3086         printf("Target doesn't support sort\n");
3087         return 0;
3088     }
3089     if (*arg)
3090     {
3091         if (send_sortrequest(arg, newset) < 0)
3092             return 0;
3093         return 2;
3094     }
3095     return 0;
3096 }
3097
3098 int cmd_sort(const char *arg)
3099 {
3100     return cmd_sort_generic (arg, 0);
3101 }
3102
3103 int cmd_sort_newset (const char *arg)
3104 {
3105     return cmd_sort_generic (arg, 1);
3106 }
3107
3108 int cmd_scanstep(const char *arg)
3109 {
3110     scan_stepSize = atoi(arg);
3111     return 0;
3112 }
3113
3114 int cmd_scanpos(const char *arg)
3115 {
3116     int r = sscanf(arg, "%d", &scan_position);
3117     if (r == 0)
3118         scan_position = 1;
3119     return 0;
3120 }
3121
3122 int cmd_scansize(const char *arg)
3123 {
3124     int r = sscanf(arg, "%d", &scan_size);
3125     if (r == 0)
3126         scan_size = 20;
3127     return 0;
3128 }
3129
3130 int cmd_scan(const char *arg)
3131 {
3132     if (protocol == PROTO_HTTP)
3133     {
3134 #if YAZ_HAVE_XML2
3135         if (!conn)
3136             cmd_open(0);
3137         if (!conn)
3138             return 0;
3139         if (*arg)
3140         {
3141             if (send_SRW_scanRequest(arg, scan_position, scan_size) < 0)
3142                 return 0;
3143         }
3144         else
3145         {
3146             if (send_SRW_scanRequest(last_scan_line, 1, scan_size) < 0)
3147                 return 0;
3148         }
3149         return 2;
3150 #else
3151         return 0;
3152 #endif
3153     }
3154     else
3155     {
3156         if (!conn)
3157         {
3158             try_reconnect();
3159             
3160             if (!conn) {                                                                
3161                 printf("Session not initialized yet\n");
3162                 return 0;
3163             }
3164         }
3165         if (session_initResponse && 
3166             !ODR_MASK_GET(session_initResponse->options, Z_Options_scan))
3167         {
3168             printf("Target doesn't support scan\n");
3169             return 0;
3170         }
3171         if (*arg)
3172         {
3173             strcpy (last_scan_query, arg);
3174             if (send_scanrequest(arg, scan_position, scan_size, 0) < 0)
3175                 return 0;
3176         }
3177         else
3178         {
3179             if (send_scanrequest(last_scan_query, 1, scan_size, last_scan_line) < 0)
3180                 return 0;
3181         }
3182         return 2;
3183     }
3184 }
3185
3186 int cmd_schema(const char *arg)
3187 {
3188     xfree(record_schema);
3189     record_schema = 0;
3190     if (arg && *arg)
3191         record_schema = xstrdup(arg);
3192     return 1;
3193 }
3194
3195 int cmd_format(const char *arg)
3196 {
3197     const char *cp = arg;
3198     int nor;
3199     int idx = 0;
3200     oid_value nsyntax[RECORDSYNTAX_MAX];
3201     char form_str[41];
3202     if (!arg || !*arg)
3203     {
3204         printf("Usage: format <recordsyntax>\n");
3205         return 0;
3206     }
3207     while (sscanf(cp, "%40s%n", form_str, &nor) >= 1 && nor > 0 
3208            && idx < RECORDSYNTAX_MAX)
3209     {
3210         nsyntax[idx] = oid_getvalbyname(form_str);
3211         if (!strcmp(form_str, "none"))
3212             break;
3213         if (nsyntax[idx] == VAL_NONE)
3214         {
3215             printf ("unknown record syntax: %s\n", form_str);
3216             return 0;
3217         }
3218         cp += nor;
3219         idx++;
3220     }
3221     recordsyntax_size = idx;
3222     memcpy(recordsyntax_list, nsyntax, idx * sizeof(*nsyntax));
3223     return 1;
3224 }
3225
3226 int cmd_elements(const char *arg)
3227 {
3228     static Z_ElementSetNames esn;
3229     static char what[100];
3230
3231     if (!arg || !*arg)
3232     {
3233         elementSetNames = 0;
3234         return 1;
3235     }
3236     strcpy(what, arg);
3237     esn.which = Z_ElementSetNames_generic;
3238     esn.u.generic = what;
3239     elementSetNames = &esn;
3240     return 1;
3241 }
3242
3243 int cmd_attributeset(const char *arg)
3244 {
3245     char what[100];
3246
3247     if (!arg || !*arg)
3248     {
3249         printf("Usage: attributeset <setname>\n");
3250         return 0;
3251     }
3252     sscanf(arg, "%s", what);
3253     if (p_query_attset (what))
3254     {
3255         printf("Unknown attribute set name\n");
3256         return 0;
3257     }
3258     return 1;
3259 }
3260
3261 int cmd_querytype (const char *arg)
3262 {
3263     if (!strcmp (arg, "ccl"))
3264         queryType = QueryType_CCL;
3265     else if (!strcmp (arg, "prefix") || !strcmp(arg, "rpn"))
3266         queryType = QueryType_Prefix;
3267     else if (!strcmp (arg, "ccl2rpn") || !strcmp (arg, "cclrpn"))
3268         queryType = QueryType_CCL2RPN;
3269     else if (!strcmp(arg, "cql"))
3270         queryType = QueryType_CQL;        
3271     else if (!strcmp (arg, "cql2rpn") || !strcmp (arg, "cqlrpn"))
3272         queryType = QueryType_CQL2RPN;
3273     else
3274     {
3275         printf ("Querytype must be one of:\n");
3276         printf (" prefix         - Prefix query\n");
3277         printf (" ccl            - CCL query\n");
3278         printf (" ccl2rpn        - CCL query converted to RPN\n");
3279         printf (" cql            - CQL\n");
3280         printf (" cql2rpn        - CQL query converted to RPN\n");
3281         return 0;
3282     }
3283     return 1;
3284 }
3285
3286 int cmd_refid (const char *arg)
3287 {
3288     xfree (refid);
3289     refid = NULL;
3290     if (*arg)
3291         refid = xstrdup (arg);
3292     return 1;
3293 }
3294
3295 int cmd_close(const char *arg)
3296 {
3297     Z_APDU *apdu;
3298     Z_Close *req;
3299     if (only_z3950())
3300         return 0;
3301     apdu = zget_APDU(out, Z_APDU_close);
3302     req = apdu->u.close;
3303     *req->closeReason = Z_Close_finished;
3304     send_apdu(apdu);
3305     printf("Sent close request.\n");
3306     sent_close = 1;
3307     return 2;
3308 }
3309
3310 int cmd_packagename(const char* arg)
3311 {
3312     xfree (esPackageName);
3313     esPackageName = NULL;
3314     if (*arg)
3315         esPackageName = xstrdup(arg);
3316     return 1;
3317 }
3318
3319 int cmd_proxy(const char* arg)
3320 {
3321     xfree(yazProxy);
3322     yazProxy = 0;
3323     if (*arg)
3324         yazProxy = xstrdup (arg);
3325     return 1;
3326 }
3327
3328 int cmd_marccharset(const char *arg)
3329 {
3330     char l1[30];
3331
3332     *l1 = 0;
3333     if (sscanf(arg, "%29s", l1) < 1)
3334     {
3335         printf("MARC character set is `%s'\n", 
3336                marcCharset ? marcCharset: "none");
3337         return 1;
3338     }
3339     xfree (marcCharset);
3340     marcCharset = 0;
3341     if (strcmp(l1, "-"))
3342         marcCharset = xstrdup(l1);
3343     return 1;
3344 }
3345
3346 int cmd_displaycharset(const char *arg)
3347 {
3348     char l1[30];
3349
3350     *l1 = 0;
3351     if (sscanf(arg, "%29s", l1) < 1)
3352     {
3353         printf("Display character set is `%s'\n", 
3354                outputCharset ? outputCharset: "none");
3355     }
3356     else
3357     {
3358         xfree (outputCharset);
3359         outputCharset = 0;
3360         if (!strcmp(l1, "auto") && codeset)
3361         {
3362             if (codeset)
3363             {
3364                 printf ("Display character set: %s\n", codeset);
3365                 outputCharset = xstrdup(codeset);
3366             }
3367             else
3368                 printf ("No codeset found on this system\n");
3369         }
3370         else if (strcmp(l1, "-") && strcmp(l1, "none"))
3371             outputCharset = xstrdup(l1);
3372     } 
3373     return 1;
3374 }
3375
3376 int cmd_negcharset(const char *arg)
3377 {
3378     char l1[30];
3379
3380     *l1 = 0;
3381     if (sscanf(arg, "%29s %d %d", l1, &negotiationCharsetRecords,
3382                &negotiationCharsetVersion) < 1)
3383     {
3384         printf("Current negotiation character set is `%s'\n", 
3385                negotiationCharset ? negotiationCharset: "none");  
3386         printf("Records in charset %s\n", negotiationCharsetRecords ? 
3387                "yes" : "no");
3388         printf("Charneg version %d\n", negotiationCharsetVersion);
3389     }
3390     else
3391     {
3392         xfree (negotiationCharset);
3393         negotiationCharset = NULL;
3394         if (*l1 && strcmp(l1, "-") && strcmp(l1, "none"))
3395         {
3396             negotiationCharset = xstrdup(l1);
3397             printf ("Character set negotiation : %s\n", negotiationCharset);
3398         }
3399     }
3400     return 1;
3401 }
3402
3403 int cmd_charset(const char* arg)
3404 {
3405     char l1[30], l2[30], l3[30];
3406
3407     *l1 = *l2 = *l3 = 0;
3408     if (sscanf(arg, "%29s %29s %29s", l1, l2, l3) < 1)
3409     {
3410         cmd_negcharset("");
3411         cmd_displaycharset("");
3412         cmd_marccharset("");
3413     }
3414     else
3415     {
3416         cmd_negcharset(l1);
3417         if (*l2)
3418             cmd_displaycharset(l2);
3419         if (*l3)
3420             cmd_marccharset(l3);
3421     }
3422     return 1;
3423 }
3424
3425 int cmd_lang(const char* arg)
3426 {
3427     if (*arg == '\0') {
3428         printf("Current language is `%s'\n", (yazLang)?yazLang:NULL);
3429         return 1;
3430     }
3431     xfree (yazLang);
3432     yazLang = NULL;
3433     if (*arg)
3434         yazLang = xstrdup(arg);
3435     return 1;
3436 }
3437
3438 int cmd_source(const char* arg, int echo ) 
3439 {
3440     /* first should open the file and read one line at a time.. */
3441     FILE* includeFile;
3442     char line[102400], *cp;
3443
3444     if(strlen(arg)<1) {
3445         fprintf(stderr,"Error in source command use a filename\n");
3446         return -1;
3447     }
3448     
3449     includeFile = fopen (arg, "r");
3450     
3451     if(!includeFile) {
3452         fprintf(stderr,"Unable to open file %s for reading\n",arg);
3453         return -1;
3454     }
3455     
3456     while(!feof(includeFile)) {
3457         memset(line,0,sizeof(line));
3458         fgets(line,sizeof(line),includeFile);
3459         
3460         if(strlen(line) < 2) continue;
3461         if(line[0] == '#') continue;
3462         
3463         if ((cp = strrchr (line, '\n')))
3464             *cp = '\0';
3465         
3466         if( echo ) {
3467             printf( "processing line: %s\n",line );
3468         };
3469         process_cmd_line(line);
3470     }
3471     
3472     if(fclose(includeFile)<0) {
3473         perror("unable to close include file");
3474         exit(1);
3475     }
3476     return 1;
3477 }
3478
3479 int cmd_source_echo(const char* arg)
3480
3481     cmd_source(arg, 1);
3482     return 1;
3483 }
3484
3485 int cmd_source_noecho(const char* arg)
3486 {
3487     cmd_source(arg, 0);
3488     return 1;
3489 }
3490
3491
3492 int cmd_subshell(const char* args)
3493 {
3494     if(strlen(args)) 
3495         system(args);
3496     else 
3497         system(getenv("SHELL"));
3498     
3499     printf("\n");
3500     return 1;
3501 }
3502
3503 int cmd_set_berfile(const char *arg)
3504 {
3505     if (ber_file && ber_file != stdout && ber_file != stderr)
3506         fclose(ber_file);
3507     if (!strcmp(arg, ""))
3508         ber_file = 0;
3509     else if (!strcmp(arg, "-"))
3510         ber_file = stdout;
3511     else
3512         ber_file = fopen(arg, "a");
3513     return 1;
3514 }
3515
3516 int cmd_set_apdufile(const char *arg)
3517 {
3518     if(apdu_file && apdu_file != stderr && apdu_file != stderr)
3519         fclose(apdu_file);
3520     if (!strcmp(arg, ""))
3521         apdu_file = 0;
3522     else if (!strcmp(arg, "-"))
3523         apdu_file = stderr;
3524     else
3525     {
3526         apdu_file = fopen(arg, "a");
3527         if (!apdu_file)
3528             perror("unable to open apdu log file");
3529     }
3530     if (apdu_file)
3531         odr_setprint(print, apdu_file);
3532     return 1;
3533 }
3534
3535 int cmd_set_cclfile(const char* arg)
3536 {  
3537     FILE *inf;
3538
3539     bibset = ccl_qual_mk (); 
3540     inf = fopen (arg, "r");
3541     if (!inf)
3542         perror("unable to open CCL file");
3543     else
3544     {
3545         ccl_qual_file (bibset, inf);
3546         fclose (inf);
3547     }
3548     strcpy(ccl_fields,arg);
3549     return 0;
3550 }
3551
3552 int cmd_set_cqlfile(const char* arg)
3553 {
3554     cql_transform_t newcqltrans;
3555
3556     if ((newcqltrans = cql_transform_open_fname(arg)) == 0) {
3557         perror("unable to open CQL file");
3558         return 0;
3559     }
3560     if (cqltrans != 0)
3561         cql_transform_close(cqltrans);
3562
3563     cqltrans = newcqltrans;
3564     strcpy(cql_fields, arg);
3565     return 0;
3566 }
3567
3568 int cmd_set_auto_reconnect(const char* arg)
3569 {  
3570     if(strlen(arg)==0) {
3571         auto_reconnect = ! auto_reconnect;
3572     } else if(strcmp(arg,"on")==0) {
3573         auto_reconnect = 1;
3574     } else if(strcmp(arg,"off")==0) {
3575         auto_reconnect = 0;             
3576     } else {
3577         printf("Error use on or off\n");
3578         return 1;
3579     }
3580     
3581     if (auto_reconnect)
3582         printf("Set auto reconnect enabled.\n");
3583     else
3584         printf("Set auto reconnect disabled.\n");
3585     
3586     return 0;
3587 }
3588
3589
3590 int cmd_set_auto_wait(const char* arg)
3591 {  
3592     if(strlen(arg)==0) {
3593         auto_wait = ! auto_wait;
3594     } else if(strcmp(arg,"on")==0) {
3595         auto_wait = 1;
3596     } else if(strcmp(arg,"off")==0) {
3597         auto_wait = 0;          
3598     } else {
3599         printf("Error use on or off\n");
3600         return 1;
3601     }
3602     
3603     if (auto_wait)
3604         printf("Set auto wait enabled.\n");
3605     else
3606         printf("Set auto wait disabled.\n");
3607     
3608     return 0;
3609 }
3610
3611 int cmd_set_marcdump(const char* arg)
3612 {
3613     if(marc_file && marc_file != stderr) { /* don't close stdout*/
3614         fclose(marc_file);
3615     }
3616
3617     if (!strcmp(arg, ""))
3618         marc_file = 0;
3619     else if (!strcmp(arg, "-"))
3620         marc_file = stderr;
3621     else
3622     {
3623         marc_file = fopen(arg, "a");
3624         if (!marc_file)
3625             perror("unable to open marc log file");
3626     }
3627     return 1;
3628 }
3629
3630 /* 
3631    this command takes 3 arge {name class oid} 
3632 */
3633 int cmd_register_oid(const char* args) {
3634     static struct {
3635         char* className;
3636         oid_class oclass;
3637     } oid_classes[] = {
3638         {"appctx",CLASS_APPCTX},
3639         {"absyn",CLASS_ABSYN},
3640         {"attset",CLASS_ATTSET},
3641         {"transyn",CLASS_TRANSYN},
3642         {"diagset",CLASS_DIAGSET},
3643         {"recsyn",CLASS_RECSYN},
3644         {"resform",CLASS_RESFORM},
3645         {"accform",CLASS_ACCFORM},
3646         {"extserv",CLASS_EXTSERV},
3647         {"userinfo",CLASS_USERINFO},
3648         {"elemspec",CLASS_ELEMSPEC},
3649         {"varset",CLASS_VARSET},
3650         {"schema",CLASS_SCHEMA},
3651         {"tagset",CLASS_TAGSET},
3652         {"general",CLASS_GENERAL},
3653         {0,(enum oid_class) 0}
3654     };
3655     char oname_str[101], oclass_str[101], oid_str[101];  
3656     char* name;
3657     int i;
3658     oid_class oidclass = CLASS_GENERAL;
3659     int val = 0, oid[OID_SIZE];
3660     struct oident * new_oident=NULL;
3661     
3662     if (sscanf (args, "%100[^ ] %100[^ ] %100s",
3663                 oname_str,oclass_str, oid_str) < 1) {
3664         printf("Error in register command \n");
3665         return 0;
3666     }
3667     
3668     for (i = 0; oid_classes[i].className; i++) {
3669         if (!strcmp(oid_classes[i].className, oclass_str))
3670         {
3671             oidclass=oid_classes[i].oclass;
3672             break;
3673         }
3674     }
3675     
3676     if(!(oid_classes[i].className)) {
3677         printf("Unknown oid class %s\n",oclass_str);
3678         return 0;
3679     }
3680     
3681     i = 0;
3682     name = oid_str;
3683     val = 0;
3684     
3685     while (isdigit (*(unsigned char *) name))
3686     {
3687         val = val*10 + (*name - '0');
3688         name++;
3689         if (*name == '.')
3690         {
3691             if (i < OID_SIZE-1)
3692                 oid[i++] = val;
3693             val = 0;
3694             name++;
3695         }
3696     }
3697     oid[i] = val;
3698     oid[i+1] = -1;
3699     
3700     new_oident = oid_addent (oid, PROTO_GENERAL, oidclass, oname_str,
3701                              VAL_DYNAMIC);  
3702     if(strcmp(new_oident->desc,oname_str))
3703     {
3704         fprintf(stderr,"oid is already named as %s, registration failed\n",
3705                 new_oident->desc);
3706     }
3707     return 1;  
3708 }
3709
3710 int cmd_push_command(const char* arg) 
3711 {
3712 #if HAVE_READLINE_HISTORY_H
3713     if(strlen(arg)>1) 
3714         add_history(arg);
3715 #else 
3716     fprintf(stderr,"Not compiled with the readline/history module\n");
3717 #endif
3718     return 1;
3719 }
3720
3721 void source_rcfile(void)
3722 {
3723     /*  Look for a $HOME/.yazclientrc and source it if it exists */
3724     struct stat statbuf;
3725     char fname[1000];
3726     char* homedir = getenv("HOME");
3727
3728     sprintf(fname, "%.500s%s%s", homedir ? homedir : "",
3729             homedir ? "/" : "",
3730             ".yazclientrc");
3731
3732     if (stat(fname,&statbuf)==0)
3733         cmd_source(fname, 0 );
3734 }
3735
3736 void add_to_readline_history(void *client_data, const char *line)
3737 {
3738 #if HAVE_READLINE_HISTORY_H
3739     if (strlen(line))
3740         add_history(line);
3741 #endif
3742 }
3743
3744 static void initialize(void)
3745 {
3746     FILE *inf;
3747     int i;
3748     
3749     if (!(out = odr_createmem(ODR_ENCODE)) ||
3750         !(in = odr_createmem(ODR_DECODE)) ||
3751         !(print = odr_createmem(ODR_PRINT)))
3752     {
3753         fprintf(stderr, "failed to allocate ODR streams\n");
3754         exit(1);
3755     }
3756     oid_init();
3757     
3758     setvbuf(stdout, 0, _IONBF, 0);
3759     if (apdu_file)
3760         odr_setprint(print, apdu_file);
3761
3762     bibset = ccl_qual_mk (); 
3763     inf = fopen (ccl_fields, "r");
3764     if (inf)
3765     {
3766         ccl_qual_file (bibset, inf);
3767         fclose (inf);
3768     }
3769
3770     cqltrans = cql_transform_open_fname(cql_fields);
3771     /* If this fails, no problem: we detect cqltrans == 0 later */
3772
3773 #if HAVE_READLINE_READLINE_H
3774     rl_attempted_completion_function = (CPPFunction*)readline_completer;
3775 #endif
3776     for(i = 0; i < maxOtherInfosSupported; ++i) {
3777         extraOtherInfos[i].oidval = -1;
3778     }
3779     
3780     source_rcfile();
3781
3782     file_history = file_history_new();
3783     file_history_load(file_history);
3784     file_history_trav(file_history, 0, add_to_readline_history);
3785 }
3786
3787
3788 #if HAVE_GETTIMEOFDAY
3789 struct timeval tv_start;
3790 #endif
3791
3792 #if YAZ_HAVE_XML2
3793 static void handle_srw_record(Z_SRW_record *rec)
3794 {
3795     if (rec->recordPosition)
3796     {
3797         printf ("pos=%d", *rec->recordPosition);
3798         setno = *rec->recordPosition + 1;
3799     }
3800     if (rec->recordSchema)
3801         printf (" schema=%s", rec->recordSchema);
3802     printf ("\n");
3803     if (rec->recordData_buf && rec->recordData_len)
3804     {
3805         fwrite(rec->recordData_buf, 1, rec->recordData_len, stdout);
3806         if (marc_file)
3807             fwrite (rec->recordData_buf, 1, rec->recordData_len, marc_file);
3808     }
3809     else
3810         printf ("No data!");
3811     printf("\n");
3812 }
3813
3814 static void handle_srw_explain_response(Z_SRW_explainResponse *res)
3815 {
3816     handle_srw_record(&res->record);
3817 }
3818
3819 static void handle_srw_response(Z_SRW_searchRetrieveResponse *res)
3820 {
3821     int i;
3822
3823     printf ("Received SRW SearchRetrieve Response\n");
3824     
3825     for (i = 0; i<res->num_diagnostics; i++)
3826     {
3827         if (res->diagnostics[i].uri)
3828             printf ("SRW diagnostic %s\n",
3829                     res->diagnostics[i].uri);
3830         else
3831             printf ("SRW diagnostic missing or could not be decoded\n");
3832         if (res->diagnostics[i].message)
3833             printf ("Message: %s\n", res->diagnostics[i].message);
3834         if (res->diagnostics[i].details)
3835             printf ("Details: %s\n", res->diagnostics[i].details);
3836     }
3837     if (res->numberOfRecords)
3838         printf ("Number of hits: %d\n", *res->numberOfRecords);
3839     for (i = 0; i<res->num_records; i++)
3840         handle_srw_record(res->records + i);
3841 }
3842
3843 static void handle_srw_scan_term(Z_SRW_scanTerm *term)
3844 {
3845     if (term->displayTerm)
3846         printf("%s:", term->displayTerm);
3847     else if (term->value)
3848         printf("%s:", term->value);
3849     else
3850         printf("No value:");
3851     if (term->numberOfRecords)
3852         printf(" %d", *term->numberOfRecords);
3853     if (term->whereInList)
3854         printf(" %s", term->whereInList);
3855     if (term->value && term->displayTerm)
3856         printf(" %s", term->value);
3857
3858     strcpy(last_scan_line, term->value);
3859     printf("\n");
3860 }
3861
3862 static void handle_srw_scan_response(Z_SRW_scanResponse *res)
3863 {
3864     int i;
3865
3866     printf ("Received SRW Scan Response\n");
3867     
3868     for (i = 0; i<res->num_diagnostics; i++)
3869     {
3870         if (res->diagnostics[i].uri)
3871             printf ("SRW diagnostic %s\n",
3872                     res->diagnostics[i].uri);
3873         else
3874             printf ("SRW diagnostic missing or could not be decoded\n");
3875         if (res->diagnostics[i].message)
3876             printf ("Message: %s\n", res->diagnostics[i].message);
3877         if (res->diagnostics[i].details)
3878             printf ("Details: %s\n", res->diagnostics[i].details);
3879     }
3880     if (res->terms)
3881         for (i = 0; i<res->num_terms; i++)
3882             handle_srw_scan_term(res->terms + i);
3883 }
3884
3885 static void http_response(Z_HTTP_Response *hres)
3886 {
3887     int ret = -1;
3888     const char *content_type = z_HTTP_header_lookup(hres->headers,
3889                                                     "Content-Type");
3890     const char *connection_head = z_HTTP_header_lookup(hres->headers,
3891                                                        "Connection");
3892     if (content_type && !yaz_strcmp_del("text/xml", content_type, "; "))
3893     {
3894         Z_SOAP *soap_package = 0;
3895         ODR o = odr_createmem(ODR_DECODE);
3896         Z_SOAP_Handler soap_handlers[3] = {
3897             {YAZ_XMLNS_SRU_v1_1, 0, (Z_SOAP_fun) yaz_srw_codec},
3898             {YAZ_XMLNS_UPDATE_v0_9, 0, (Z_SOAP_fun) yaz_ucp_codec},
3899             {0, 0, 0}
3900         };
3901         ret = z_soap_codec(o, &soap_package,
3902                            &hres->content_buf, &hres->content_len,
3903                            soap_handlers);
3904         if (!ret && soap_package->which == Z_SOAP_generic)
3905         {
3906             Z_SRW_PDU *sr = soap_package->u.generic->p;
3907             if (sr->which == Z_SRW_searchRetrieve_response)
3908                 handle_srw_response(sr->u.response);
3909             else if (sr->which == Z_SRW_explain_response)
3910                 handle_srw_explain_response(sr->u.explain_response);
3911             else if (sr->which == Z_SRW_scan_response)
3912                 handle_srw_scan_response(sr->u.scan_response);
3913             else if (sr->which == Z_SRW_update_response)
3914                 printf("Got update response. Status: %s\n",
3915                        sr->u.update_response->operationStatus);
3916             else
3917                 ret = -1;
3918         }
3919         else if (soap_package && (soap_package->which == Z_SOAP_fault
3920                                   || soap_package->which == Z_SOAP_error))
3921         {
3922             printf ("HTTP Error Status=%d\n", hres->code);
3923             printf ("SOAP Fault code %s\n",
3924                     soap_package->u.fault->fault_code);
3925             printf ("SOAP Fault string %s\n", 
3926                     soap_package->u.fault->fault_string);
3927             if (soap_package->u.fault->details)
3928                 printf ("SOAP Details %s\n", 
3929                         soap_package->u.fault->details);
3930         }
3931         else
3932         {
3933             printf("z_soap_codec failed. (no SOAP error)\n");
3934             ret = -1;
3935         }
3936         odr_destroy(o);
3937     }
3938     if (ret)
3939     {
3940         if (hres->code != 200)
3941         {
3942             printf ("HTTP Error Status=%d\n", hres->code);
3943         }
3944         else
3945         {
3946             printf ("Decoding of SRW package failed\n");
3947         }
3948         close_session();
3949     }
3950     else
3951     {
3952         if (!strcmp(hres->version, "1.0"))
3953         {
3954             /* HTTP 1.0: only if Keep-Alive we stay alive.. */
3955             if (!connection_head || strcmp(connection_head, "Keep-Alive"))
3956                 close_session();
3957         }
3958         else 
3959         {
3960             /* HTTP 1.1: only if no close we stay alive .. */
3961             if (connection_head && !strcmp(connection_head, "close"))
3962                 close_session();
3963         }
3964     }
3965 }
3966 #endif
3967
3968 void wait_and_handle_response(int one_response_only) 
3969 {
3970     int reconnect_ok = 1;
3971     int res;
3972     char *netbuffer= 0;
3973     int netbufferlen = 0;
3974 #if HAVE_GETTIMEOFDAY
3975     int got_tv_end = 0;
3976     struct timeval tv_end;
3977 #endif
3978     Z_GDU *gdu;
3979     
3980     while(conn)
3981     {
3982         res = cs_get(conn, &netbuffer, &netbufferlen);
3983         if (reconnect_ok && res <= 0 && protocol == PROTO_HTTP)
3984         {
3985             cs_close(conn);
3986             conn = 0;
3987             cmd_open(0);
3988             reconnect_ok = 0;
3989             if (conn)
3990             {
3991                 char *buf_out;
3992                 int len_out;
3993                 
3994                 buf_out = odr_getbuf(out, &len_out, 0);
3995                 
3996                 do_hex_dump(buf_out, len_out);
3997
3998                 cs_put(conn, buf_out, len_out);
3999                 
4000                 odr_reset(out);
4001                 continue;
4002             }
4003         }
4004         else if (res <= 0)
4005         {
4006             printf("Target closed connection\n");
4007             close_session();
4008             break;
4009         }
4010 #if HAVE_GETTIMEOFDAY
4011         if (got_tv_end == 0)
4012             gettimeofday (&tv_end, 0); /* count first one only */
4013         got_tv_end++;
4014 #endif
4015         odr_reset(out);
4016         odr_reset(in); /* release APDU from last round */
4017         record_last = 0;
4018         do_hex_dump(netbuffer, res);
4019         odr_setbuf(in, netbuffer, res, 0);
4020         
4021         if (!z_GDU(in, &gdu, 0, 0))
4022         {
4023             FILE *f = ber_file ? ber_file : stdout;
4024             odr_perror(in, "Decoding incoming APDU");
4025             fprintf(f, "[Near %ld]\n", (long) odr_offset(in));
4026             fprintf(f, "Packet dump:\n---------\n");
4027             odr_dumpBER(f, netbuffer, res);
4028             fprintf(f, "---------\n");
4029             if (apdu_file)
4030             {
4031                 z_GDU(print, &gdu, 0, 0);
4032                 odr_reset(print);
4033             }
4034             if (conn && cs_more(conn))
4035                 continue;
4036             break;
4037         }
4038         if (ber_file)
4039             odr_dumpBER(ber_file, netbuffer, res);
4040         if (apdu_file && !z_GDU(print, &gdu, 0, 0))
4041         {
4042             odr_perror(print, "Failed to print incoming APDU");
4043             odr_reset(print);
4044                 continue;
4045         }
4046         if (gdu->which == Z_GDU_Z3950)
4047         {
4048             Z_APDU *apdu = gdu->u.z3950;
4049             switch(apdu->which)
4050             {
4051             case Z_APDU_initResponse:
4052                 process_initResponse(apdu->u.initResponse);
4053                 break;
4054             case Z_APDU_searchResponse:
4055                 process_searchResponse(apdu->u.searchResponse);
4056                 break;
4057             case Z_APDU_scanResponse:
4058                 process_scanResponse(apdu->u.scanResponse);
4059                 break;
4060             case Z_APDU_presentResponse:
4061                 print_refid (apdu->u.presentResponse->referenceId);
4062                 setno +=
4063                     *apdu->u.presentResponse->numberOfRecordsReturned;
4064                 if (apdu->u.presentResponse->records)
4065                     display_records(apdu->u.presentResponse->records);
4066                 else
4067                     printf("No records.\n");
4068                 printf ("nextResultSetPosition = %d\n",
4069                         *apdu->u.presentResponse->nextResultSetPosition);
4070                 break;
4071             case Z_APDU_sortResponse:
4072                 process_sortResponse(apdu->u.sortResponse);
4073                 break;
4074             case Z_APDU_extendedServicesResponse:
4075                 printf("Got extended services response\n");
4076                 process_ESResponse(apdu->u.extendedServicesResponse);
4077                 break;
4078             case Z_APDU_close:
4079                 printf("Target has closed the association.\n");
4080                 process_close(apdu->u.close);
4081                 break;
4082             case Z_APDU_resourceControlRequest:
4083                 process_resourceControlRequest
4084                     (apdu->u.resourceControlRequest);
4085                 break;
4086             case Z_APDU_deleteResultSetResponse:
4087                 process_deleteResultSetResponse(apdu->u.
4088                                                 deleteResultSetResponse);
4089                 break;
4090             default:
4091                 printf("Received unknown APDU type (%d).\n", 
4092                        apdu->which);
4093                 close_session ();
4094             }
4095         }
4096 #if YAZ_HAVE_XML2
4097         else if (gdu->which == Z_GDU_HTTP_Response)
4098         {
4099             http_response(gdu->u.HTTP_Response);
4100         }
4101 #endif
4102         if (one_response_only)
4103             break;
4104         if (conn && !cs_more(conn))
4105             break;
4106     }
4107 #if HAVE_GETTIMEOFDAY
4108     if (got_tv_end)
4109     {
4110 #if 0
4111         printf ("S/U S/U=%ld/%ld %ld/%ld",
4112                 (long) tv_start.tv_sec,
4113                 (long) tv_start.tv_usec,
4114                 (long) tv_end.tv_sec,
4115                 (long) tv_end.tv_usec);
4116 #endif
4117         printf ("Elapsed: %.6f\n",
4118                 (double) tv_end.tv_usec / 1e6 + tv_end.tv_sec -
4119                 ((double) tv_start.tv_usec / 1e6 + tv_start.tv_sec));
4120     }
4121 #endif
4122     xfree (netbuffer);
4123 }
4124
4125
4126 int cmd_cclparse(const char* arg) 
4127 {
4128     int error, pos;
4129     struct ccl_rpn_node *rpn=NULL;
4130     
4131     
4132     rpn = ccl_find_str (bibset, arg, &error, &pos);
4133     
4134     if (error) {
4135         int ioff = 3+strlen(last_cmd)+1+pos;
4136         printf ("%*s^ - ", ioff, " ");
4137         printf ("%s\n", ccl_err_msg (error));
4138     }
4139     else
4140     {
4141         if (rpn)
4142         {       
4143             ccl_pr_tree(rpn, stdout); 
4144         }
4145     }
4146     if (rpn)
4147         ccl_rpn_delete(rpn);
4148     
4149     printf ("\n");
4150     
4151     return 0;
4152 }
4153
4154
4155 int cmd_set_otherinfo(const char* args)
4156 {
4157     char oidstr[101], otherinfoString[101];
4158     int otherinfoNo;
4159     int sscan_res;
4160     int oidval;
4161     
4162     sscan_res = sscanf (args, "%d %100[^ ] %100s", 
4163                         &otherinfoNo, oidstr, otherinfoString);
4164     if (sscan_res==1) {
4165         /* reset this otherinfo */
4166         if(otherinfoNo>=maxOtherInfosSupported) {
4167             printf("Error otherinfo index to large (%d>%d)\n",
4168                    otherinfoNo,maxOtherInfosSupported);
4169         }
4170         extraOtherInfos[otherinfoNo].oidval = -1;
4171         if (extraOtherInfos[otherinfoNo].value)
4172             xfree(extraOtherInfos[otherinfoNo].value);                   
4173         extraOtherInfos[otherinfoNo].value = 0;
4174         return 0;
4175     }
4176     if (sscan_res<3) {
4177         printf("Error in set_otherinfo command \n");
4178         return 0;
4179     }
4180     
4181     if (otherinfoNo>=maxOtherInfosSupported) {
4182         printf("Error otherinfo index too large (%d>=%d)\n",
4183                otherinfoNo,maxOtherInfosSupported);
4184     }
4185     
4186     oidval = oid_getvalbyname (oidstr);
4187     if (oidval == VAL_NONE)
4188     {
4189         printf("Error in set_otherinfo command unknown oid %s \n",oidstr);
4190         return 0;
4191     }
4192     extraOtherInfos[otherinfoNo].oidval = oidval;
4193     if (extraOtherInfos[otherinfoNo].value)
4194         xfree(extraOtherInfos[otherinfoNo].value);
4195     extraOtherInfos[otherinfoNo].value = xstrdup(otherinfoString);
4196     
4197     return 0;
4198 }
4199
4200 int cmd_sleep(const char* args ) 
4201 {
4202     int sec=atoi(args);
4203     if( sec > 0 ) {
4204 #ifdef WIN32
4205         Sleep(sec*1000);
4206 #else
4207         sleep(sec);
4208 #endif
4209         printf("Done sleeping %d seconds\n", sec);      
4210     }
4211     return 1;    
4212 }
4213
4214 int cmd_list_otherinfo(const char* args)
4215 {
4216     int i;         
4217     
4218     if(strlen(args)>0) {
4219         i = atoi(args);
4220         if( i >= maxOtherInfosSupported ) {
4221             printf("Error otherinfo index to large (%d>%d)\n",i,maxOtherInfosSupported);
4222             return 0;
4223         }
4224
4225         if(extraOtherInfos[i].oidval != -1) 
4226             printf("  otherinfo %d %s %s\n",
4227                    i,
4228                    yaz_z3950_oid_value_to_str(
4229                        (enum oid_value) extraOtherInfos[i].oidval,
4230                        CLASS_RECSYN),
4231                    extraOtherInfos[i].value);
4232         
4233     } else {            
4234         for(i=0; i<maxOtherInfosSupported; ++i) {
4235             if(extraOtherInfos[i].oidval != -1) 
4236                 printf("  otherinfo %d %s %s\n",
4237                        i,
4238                        yaz_z3950_oid_value_to_str(
4239                            (enum oid_value) extraOtherInfos[i].oidval,
4240                            CLASS_RECSYN),
4241                        extraOtherInfos[i].value);
4242         }
4243         
4244     }
4245     return 0;
4246 }
4247
4248
4249 int cmd_list_all(const char* args) {
4250     int i;
4251     
4252     /* connection options */
4253     if(conn) {
4254         printf("Connected to         : %s\n",last_open_command);
4255     } else {
4256         if(last_open_command) 
4257             printf("Not connected to     : %s\n",last_open_command);
4258         else 
4259             printf("Not connected        : \n");
4260         
4261     }
4262     if(yazProxy) printf("using proxy          : %s\n",yazProxy);                
4263     
4264     printf("auto_reconnect       : %s\n",auto_reconnect?"on":"off");
4265     printf("auto_wait            : %s\n",auto_wait?"on":"off");
4266     
4267     if (!auth) {
4268         printf("Authentication       : none\n");
4269     } else {
4270         switch(auth->which) {
4271         case Z_IdAuthentication_idPass:
4272             printf("Authentication       : IdPass\n"); 
4273             printf("    Login User       : %s\n",auth->u.idPass->userId?auth->u.idPass->userId:"");
4274             printf("    Login Group      : %s\n",auth->u.idPass->groupId?auth->u.idPass->groupId:"");
4275             printf("    Password         : %s\n",auth->u.idPass->password?auth->u.idPass->password:"");
4276             break;
4277         case Z_IdAuthentication_open:
4278             printf("Authentication       : psOpen\n");                  
4279             printf("    Open string      : %s\n",auth->u.open); 
4280             break;
4281         default:
4282             printf("Authentication       : Unknown\n");
4283         }
4284     }
4285     if (negotiationCharset)
4286         printf("Neg. Character set   : `%s'\n", negotiationCharset);
4287     
4288     /* bases */
4289     printf("Bases                : ");
4290     for (i = 0; i<num_databaseNames; i++) printf("%s ",databaseNames[i]);
4291     printf("\n");
4292     
4293     /* Query options */
4294     printf("CCL file             : %s\n",ccl_fields);
4295     printf("CQL file             : %s\n",cql_fields);
4296     printf("Query type           : %s\n",query_type_as_string(queryType));
4297     
4298     printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
4299     
4300     /* piggy back options */
4301     printf("ssub/lslb/mspn       : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber);
4302     
4303     /* print present related options */
4304     printf("Format               : %s\n",
4305            (recordsyntax_size > 0) ? 
4306            yaz_z3950_oid_value_to_str(recordsyntax_list[0], CLASS_RECSYN) :
4307            "none");
4308     printf("Schema               : %s\n",record_schema ? record_schema : "not set");
4309     printf("Elements             : %s\n",elementSetNames?elementSetNames->u.generic:"");
4310     
4311     /* loging options */
4312     printf("APDU log             : %s\n",apdu_file?"on":"off");
4313     printf("Record log           : %s\n",marc_file?"on":"off");
4314     
4315     /* other infos */
4316     printf("Other Info: \n");
4317     cmd_list_otherinfo("");
4318     
4319     return 0;
4320 }
4321
4322 int cmd_clear_otherinfo(const char* args) 
4323 {
4324     if(strlen(args)>0) {
4325         int otherinfoNo = atoi(args);
4326         if (otherinfoNo >= maxOtherInfosSupported) {
4327             printf("Error otherinfo index too large (%d>=%d)\n",
4328                    otherinfoNo, maxOtherInfosSupported);
4329             return 0;
4330         }
4331         if (extraOtherInfos[otherinfoNo].oidval != -1)
4332         {                 
4333             /* only clear if set. */
4334             extraOtherInfos[otherinfoNo].oidval = -1;
4335             xfree(extraOtherInfos[otherinfoNo].value);
4336         }
4337     } else {
4338         int i;
4339         for(i = 0; i < maxOtherInfosSupported; ++i) 
4340         {
4341             if (extraOtherInfos[i].oidval != -1)
4342             {                               
4343                 extraOtherInfos[i].oidval = -1;
4344                 xfree(extraOtherInfos[i].value);
4345             }
4346         }
4347     }
4348     return 0;
4349 }
4350
4351 int cmd_wait_response(const char *arg)
4352 {
4353     int wait_for = atoi(arg);
4354     int i=0;
4355     if( wait_for < 1 ) {
4356         wait_for = 1;
4357     };
4358     
4359     for( i=0 ; i < wait_for ; ++i ) {
4360         wait_and_handle_response(1);
4361     };
4362     return 0;
4363 }
4364
4365 static int cmd_help (const char *line);
4366
4367 typedef char *(*completerFunctionType)(const char *text, int state);
4368
4369 static struct {
4370     char *cmd;
4371     int (*fun)(const char *arg);
4372     char *ad;
4373         completerFunctionType rl_completerfunction;
4374     int complete_filenames;
4375     char **local_tabcompletes;
4376 } cmd_array[] = {
4377     {"open", cmd_open, "('tcp'|'ssl')':<host>[':'<port>][/<db>]",NULL,0,NULL},
4378     {"quit", cmd_quit, "",NULL,0,NULL},
4379     {"find", cmd_find, "<query>",NULL,0,NULL},
4380     {"delete", cmd_delete, "<setname>",NULL,0,NULL},
4381     {"base", cmd_base, "<base-name>",NULL,0,NULL},
4382     {"show", cmd_show, "<rec#>['+'<#recs>['+'<setname>]]",NULL,0,NULL},
4383     {"scan", cmd_scan, "<term>",NULL,0,NULL},
4384     {"scanstep", cmd_scanstep, "<size>",NULL,0,NULL},
4385     {"scanpos", cmd_scanpos, "<size>",NULL,0,NULL},
4386     {"scansize", cmd_scansize, "<size>",NULL,0,NULL},
4387     {"sort", cmd_sort, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
4388     {"sort+", cmd_sort_newset, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
4389     {"authentication", cmd_authentication, "<acctstring>",NULL,0,NULL},
4390     {"lslb", cmd_lslb, "<largeSetLowerBound>",NULL,0,NULL},
4391     {"ssub", cmd_ssub, "<smallSetUpperBound>",NULL,0,NULL},
4392     {"mspn", cmd_mspn, "<mediumSetPresentNumber>",NULL,0,NULL},
4393     {"status", cmd_status, "",NULL,0,NULL},
4394     {"setnames", cmd_setnames, "",NULL,0,NULL},
4395     {"cancel", cmd_cancel, "",NULL,0,NULL},
4396     {"cancel_find", cmd_cancel_find, "<query>",NULL,0,NULL},
4397     {"format", cmd_format, "<recordsyntax>",complete_format,0,NULL},
4398     {"schema", cmd_schema, "<schema>",complete_schema,0,NULL},
4399     {"elements", cmd_elements, "<elementSetName>",NULL,0,NULL},
4400     {"close", cmd_close, "",NULL,0,NULL},
4401     {"attributeset", cmd_attributeset, "<attrset>",complete_attributeset,0,NULL},
4402     {"querytype", cmd_querytype, "<type>",complete_querytype,0,NULL},
4403     {"refid", cmd_refid, "<id>",NULL,0,NULL},
4404     {"itemorder", cmd_itemorder, "ill|item|xml <itemno>",NULL,0,NULL},
4405     {"update", cmd_update, "<action> <recid> [<doc>]",NULL,0,NULL},
4406     {"update0", cmd_update0, "<action> <recid> [<doc>]",NULL,0,NULL},
4407     {"xmles", cmd_xmles, "<OID> <doc>",NULL,0,NULL},
4408     {"packagename", cmd_packagename, "<packagename>",NULL,0,NULL},
4409     {"proxy", cmd_proxy, "[('tcp'|'ssl')]<host>[':'<port>]",NULL,0,NULL},
4410     {"charset", cmd_charset, "<nego_charset> <output_charset>",NULL,0,NULL},
4411     {"negcharset", cmd_negcharset, "<nego_charset>",NULL,0,NULL},
4412     {"displaycharset", cmd_displaycharset, "<output_charset>",NULL,0,NULL},
4413     {"marccharset", cmd_marccharset, "<charset_name>",NULL,0,NULL},
4414     {"lang", cmd_lang, "<language_code>",NULL,0,NULL},
4415     {"source", cmd_source_echo, "<filename>",NULL,1,NULL},
4416     {".", cmd_source_echo, "<filename>",NULL,1,NULL},
4417     {"!", cmd_subshell, "Subshell command",NULL,1,NULL},
4418     {"set_apdufile", cmd_set_apdufile, "<filename>",NULL,1,NULL},
4419     {"set_berfile", cmd_set_berfile, "<filename>",NULL,1,NULL},
4420     {"set_marcdump", cmd_set_marcdump," <filename>",NULL,1,NULL},
4421     {"set_cclfile", cmd_set_cclfile," <filename>",NULL,1,NULL},
4422     {"set_cqlfile", cmd_set_cqlfile," <filename>",NULL,1,NULL},
4423     {"set_auto_reconnect", cmd_set_auto_reconnect," on|off",complete_auto_reconnect,1,NULL},
4424     {"set_auto_wait", cmd_set_auto_wait," on|off",complete_auto_reconnect,1,NULL},
4425     {"set_otherinfo", cmd_set_otherinfo,"<otherinfoinddex> <oid> <string>",NULL,0,NULL},
4426     {"sleep", cmd_sleep,"<seconds>",NULL,0,NULL},
4427     {"register_oid", cmd_register_oid,"<name> <class> <oid>",NULL,0,NULL},
4428     {"push_command", cmd_push_command,"<command>",command_generator,0,NULL},
4429     {"register_tab", cmd_register_tab,"<commandname> <tab>",command_generator,0,NULL},
4430     {"cclparse", cmd_cclparse,"<ccl find command>",NULL,0,NULL},
4431     {"list_otherinfo",cmd_list_otherinfo,"[otherinfoinddex]",NULL,0,NULL},
4432     {"list_all",cmd_list_all,"",NULL,0,NULL},
4433     {"clear_otherinfo",cmd_clear_otherinfo,"",NULL,0,NULL},
4434     {"wait_response",cmd_wait_response,"<number>",NULL,0,NULL},
4435     /* Server Admin Functions */
4436     {"adm-reindex", cmd_adm_reindex, "<database-name>",NULL,0,NULL},
4437     {"adm-truncate", cmd_adm_truncate, "('database'|'index')<object-name>",NULL,0,NULL},
4438     {"adm-create", cmd_adm_create, "",NULL,0,NULL},
4439     {"adm-drop", cmd_adm_drop, "('database'|'index')<object-name>",NULL,0,NULL},
4440     {"adm-import", cmd_adm_import, "<record-type> <dir> <pattern>",NULL,0,NULL},
4441     {"adm-refresh", cmd_adm_refresh, "",NULL,0,NULL},
4442     {"adm-commit", cmd_adm_commit, "",NULL,0,NULL},
4443     {"adm-shutdown", cmd_adm_shutdown, "",NULL,0,NULL},
4444     {"adm-startup", cmd_adm_startup, "",NULL,0,NULL},
4445     {"explain", cmd_explain, "", NULL, 0, NULL},
4446     {"options", cmd_options, "", NULL, 0, NULL},
4447     {"zversion", cmd_zversion, "", NULL, 0, NULL},
4448     {"help", cmd_help, "", NULL,0,NULL},
4449     {"init", cmd_init, "", NULL,0,NULL},
4450     {"sru", cmd_sru, "", NULL,0,NULL},
4451     {"exit", cmd_quit, "",NULL,0,NULL},
4452     {0,0,0,0,0,0}
4453 };
4454
4455 static int cmd_help (const char *line)
4456 {
4457     int i;
4458     char topic[21];
4459     
4460     *topic = 0;
4461     sscanf (line, "%20s", topic);
4462
4463     if (*topic == 0)
4464         printf("Commands:\n");
4465     for (i = 0; cmd_array[i].cmd; i++)
4466         if (*topic == 0 || strcmp (topic, cmd_array[i].cmd) == 0)
4467             printf("   %s %s\n", cmd_array[i].cmd, cmd_array[i].ad);
4468     if (!strcmp(topic, "find"))
4469     {
4470         printf("RPN:\n");
4471         printf(" \"term\"                        Simple Term\n");
4472         printf(" @attr [attset] type=value op  Attribute\n");
4473         printf(" @and opl opr                  And\n");
4474         printf(" @or opl opr                   Or\n");
4475         printf(" @not opl opr                  And-Not\n");
4476         printf(" @set set                      Result set\n");
4477         printf(" @prox exl dist ord rel uc ut  Proximity. Use help prox\n");
4478         printf("\n");
4479         printf("Bib-1 attribute types\n");
4480         printf("1=Use:         ");
4481         printf("4=Title 7=ISBN 8=ISSN 30=Date 62=Abstract 1003=Author 1016=Any\n");
4482         printf("2=Relation:    ");
4483         printf("1<   2<=  3=  4>=  5>  6!=  102=Relevance\n");
4484         printf("3=Position:    ");
4485         printf("1=First in Field  2=First in subfield  3=Any position\n");
4486         printf("4=Structure:   ");
4487         printf("1=Phrase  2=Word  3=Key  4=Year  5=Date  6=WordList\n");
4488         printf("5=Truncation:  ");
4489         printf("1=Right  2=Left  3=L&R  100=No  101=#  102=Re-1  103=Re-2\n");
4490         printf("6=Completeness:");
4491         printf("1=Incomplete subfield  2=Complete subfield  3=Complete field\n");
4492     }
4493     if (!strcmp(topic, "prox"))
4494     {
4495         printf("Proximity:\n");
4496         printf(" @prox exl dist ord rel uc ut\n");
4497         printf(" exl:  exclude flag . 0=include, 1=exclude.\n");
4498         printf(" dist: distance integer.\n");
4499         printf(" ord:  order flag. 0=unordered, 1=ordered.\n");
4500         printf(" rel:  relation integer. 1<  2<=  3= 4>=  5>  6!= .\n");
4501         printf(" uc:   unit class. k=known, p=private.\n");
4502         printf(" ut:   unit type. 1=character, 2=word, 3=sentence,\n");
4503         printf("        4=paragraph, 5=section, 6=chapter, 7=document,\n");
4504         printf("        8=element, 9=subelement, 10=elementType, 11=byte.\n");
4505         printf("\nExamples:\n");
4506         printf(" Search for a and b in-order at most 3 words apart:\n");
4507         printf("  @prox 0 3 1 2 k 2 a b\n");
4508         printf(" Search for any order of a and b next to each other:\n");
4509         printf("  @prox 0 1 0 3 k 2 a b\n");
4510     }
4511     return 1;
4512 }
4513
4514 int cmd_register_tab(const char* arg) 
4515 {
4516 #if HAVE_READLINE_READLINE_H
4517     char command[101], tabargument[101];
4518     int i;
4519     int num_of_tabs;
4520     char** tabslist;
4521     
4522     if (sscanf (arg, "%100s %100s", command, tabargument) < 1) {
4523         return 0;
4524     }
4525     
4526     /* locate the amdn in the list */
4527     for (i = 0; cmd_array[i].cmd; i++) {
4528         if (!strncmp(cmd_array[i].cmd, command, strlen(command))) {
4529             break;
4530         }
4531     }
4532     
4533     if (!cmd_array[i].cmd) { 
4534         fprintf(stderr,"Unknown command %s\n",command);
4535         return 1;
4536     }
4537     
4538         
4539     if (!cmd_array[i].local_tabcompletes)
4540         cmd_array[i].local_tabcompletes = (char **) calloc(1,sizeof(char**));
4541     
4542     num_of_tabs=0;              
4543     
4544     tabslist = cmd_array[i].local_tabcompletes;
4545     for(; tabslist && *tabslist; tabslist++) {
4546         num_of_tabs++;
4547     }
4548     
4549     cmd_array[i].local_tabcompletes = (char **)
4550         realloc(cmd_array[i].local_tabcompletes,
4551                 (num_of_tabs+2)*sizeof(char**));
4552     tabslist = cmd_array[i].local_tabcompletes;
4553     tabslist[num_of_tabs] = strdup(tabargument);
4554     tabslist[num_of_tabs+1] = NULL;
4555 #endif
4556     return 1;
4557 }
4558
4559
4560 void process_cmd_line(char* line)
4561 {  
4562     int i, res;
4563     char word[32], arg[10240];
4564     
4565 #if HAVE_GETTIMEOFDAY
4566     gettimeofday (&tv_start, 0);
4567 #endif
4568     
4569     if ((res = sscanf(line, "%31s %10239[^;]", word, arg)) <= 0)
4570     {
4571         strcpy(word, last_cmd);
4572         *arg = '\0';
4573     }
4574     else if (res == 1)
4575         *arg = 0;
4576     strcpy(last_cmd, word);
4577     
4578     /* removed tailing spaces from the arg command */
4579     { 
4580         char* p = arg;
4581         char* lastnonspace=NULL;
4582         
4583         for(;*p; ++p) {
4584             if(!isspace(*(unsigned char *) p)) {
4585                 lastnonspace = p;
4586             }
4587         }
4588         if(lastnonspace) 
4589             *(++lastnonspace) = 0;
4590     }
4591     
4592     for (i = 0; cmd_array[i].cmd; i++)
4593         if (!strncmp(cmd_array[i].cmd, word, strlen(word)))
4594         {
4595             res = (*cmd_array[i].fun)(arg);
4596             break;
4597         }
4598     
4599     if (!cmd_array[i].cmd) /* dump our help-screen */
4600     {
4601         printf("Unknown command: %s.\n", word);
4602         printf("Type 'help' for list of commands\n");
4603         res = 1;
4604     }
4605     
4606     if(apdu_file) fflush(apdu_file);
4607     
4608     if (res >= 2 && auto_wait)
4609         wait_and_handle_response(0);
4610     
4611     if(apdu_file)
4612         fflush(apdu_file);
4613     if(marc_file)
4614         fflush(marc_file);
4615 }
4616
4617 static char *command_generator(const char *text, int state) 
4618 {
4619 #if HAVE_READLINE_READLINE_H
4620     static int idx; 
4621     if (state==0) {
4622         idx = 0;
4623     }
4624     for( ; cmd_array[idx].cmd; ++idx) {
4625         if (!strncmp(cmd_array[idx].cmd, text, strlen(text))) {
4626             ++idx;  /* skip this entry on the next run */
4627             return strdup(cmd_array[idx-1].cmd);
4628         }
4629     }
4630 #endif
4631     return NULL;
4632 }
4633
4634 #if HAVE_READLINE_READLINE_H
4635 static char** default_completer_list = NULL;
4636
4637 static char* default_completer(const char* text, int state)
4638 {
4639     return complete_from_list(default_completer_list, text, state);
4640 }
4641 #endif
4642
4643 #if HAVE_READLINE_READLINE_H
4644
4645 /* 
4646    This function only known how to complete on the first word
4647 */
4648 char **readline_completer(char *text, int start, int end)
4649 {
4650     completerFunctionType completerToUse;
4651     
4652     if(start == 0) {
4653 #if HAVE_READLINE_RL_COMPLETION_MATCHES
4654         char** res = rl_completion_matches(text, command_generator); 
4655 #else
4656         char** res = completion_matches(text,
4657                                         (CPFunction*)command_generator); 
4658 #endif
4659         rl_attempted_completion_over = 1;
4660         return res;
4661     } else {
4662         char arg[10240],word[32];
4663         int i=0 ,res;
4664         if ((res = sscanf(rl_line_buffer, "%31s %10239[^;]", word, arg)) <= 0) {     
4665             rl_attempted_completion_over = 1;
4666             return NULL;
4667         }
4668         
4669         for (i = 0; cmd_array[i].cmd; i++)
4670             if (!strncmp(cmd_array[i].cmd, word, strlen(word)))
4671                 break;
4672         
4673         if(!cmd_array[i].cmd)
4674             return NULL;
4675         
4676         default_completer_list = cmd_array[i].local_tabcompletes;
4677         
4678         completerToUse = cmd_array[i].rl_completerfunction;
4679         if (!completerToUse) 
4680         { /* if command completer is not defined use the default completer */
4681             completerToUse = default_completer;
4682         }
4683         if (completerToUse) {
4684 #ifdef HAVE_READLINE_RL_COMPLETION_MATCHES
4685             char** res=
4686                 rl_completion_matches(text, completerToUse);
4687 #else
4688             char** res=
4689                 completion_matches(text, (CPFunction*)completerToUse);
4690 #endif
4691             if (!cmd_array[i].complete_filenames) 
4692                 rl_attempted_completion_over = 1;
4693             return res;
4694         } else {
4695             if (!cmd_array[i].complete_filenames) 
4696                 rl_attempted_completion_over = 1;
4697             return 0;
4698         }
4699     }
4700 }
4701 #endif
4702
4703 static void client(void)
4704 {
4705     char line[10240];
4706
4707     line[10239] = '\0';
4708
4709 #if HAVE_GETTIMEOFDAY
4710     gettimeofday (&tv_start, 0);
4711 #endif
4712
4713     while (1)
4714     {
4715         char *line_in = NULL;
4716 #if HAVE_READLINE_READLINE_H
4717         if (isatty(0))
4718         {
4719             line_in=readline(C_PROMPT);
4720             if (!line_in)
4721                 break;
4722 #if HAVE_READLINE_HISTORY_H
4723             if (*line_in)
4724                 add_history(line_in);
4725 #endif
4726             strncpy(line, line_in, sizeof(line)-1);
4727             free(line_in);
4728         }
4729 #endif 
4730         if (!line_in)
4731         {
4732             char *end_p;
4733             printf (C_PROMPT);
4734             fflush(stdout);
4735             if (!fgets(line, sizeof(line)-1, stdin))
4736                 break;
4737             if ((end_p = strchr (line, '\n')))
4738                 *end_p = '\0';
4739         }
4740         if (isatty(0))
4741             file_history_add_line(file_history, line);
4742         process_cmd_line(line);
4743     }
4744 }
4745
4746 static void show_version(void)
4747 {
4748     char vstr[20];
4749
4750     yaz_version(vstr, 0);
4751     printf ("YAZ version: %s\n", YAZ_VERSION);
4752     if (strcmp(vstr, YAZ_VERSION))
4753         printf ("YAZ DLL/SO: %s\n", vstr);
4754     exit(0);
4755 }
4756
4757 int main(int argc, char **argv)
4758 {
4759     char *prog = *argv;
4760     char *open_command = 0;
4761     char *auth_command = 0;
4762     char *arg;
4763     int ret;
4764     
4765 #if HAVE_LOCALE_H
4766     if (!setlocale(LC_CTYPE, ""))
4767         fprintf (stderr, "setlocale failed\n");
4768 #endif
4769 #if HAVE_LANGINFO_H
4770 #ifdef CODESET
4771     codeset = nl_langinfo(CODESET);
4772 #endif
4773 #endif
4774     if (codeset)
4775         outputCharset = xstrdup(codeset);
4776     
4777     ODR_MASK_SET(&z3950_options, Z_Options_search);
4778     ODR_MASK_SET(&z3950_options, Z_Options_present);
4779     ODR_MASK_SET(&z3950_options, Z_Options_namedResultSets);
4780     ODR_MASK_SET(&z3950_options, Z_Options_triggerResourceCtrl);
4781     ODR_MASK_SET(&z3950_options, Z_Options_scan);
4782     ODR_MASK_SET(&z3950_options, Z_Options_sort);
4783     ODR_MASK_SET(&z3950_options, Z_Options_extendedServices);
4784     ODR_MASK_SET(&z3950_options, Z_Options_delSet);
4785
4786     while ((ret = options("k:c:q:a:b:m:v:p:u:t:Vxd:", argv, argc, &arg)) != -2)
4787     {
4788         switch (ret)
4789         {
4790         case 0:
4791             if (!open_command)
4792             {
4793                 open_command = (char *) xmalloc (strlen(arg)+6);
4794                 strcpy (open_command, "open ");
4795                 strcat (open_command, arg);
4796             }
4797             else
4798             {
4799                 fprintf(stderr, "%s: Specify at most one server address\n",
4800                         prog);
4801                 exit(1);
4802             }
4803             break;
4804         case 'd':
4805             dump_file_prefix = arg;
4806             break;
4807         case 'k':
4808             kilobytes = atoi(arg);
4809             break;
4810         case 'm':
4811             if (!(marc_file = fopen (arg, "a")))
4812             {
4813                 perror (arg);
4814                 exit (1);
4815             }
4816             break;
4817         case 't':
4818             outputCharset = xstrdup(arg);
4819             break;
4820         case 'c':
4821             strncpy (ccl_fields, arg, sizeof(ccl_fields)-1);
4822             ccl_fields[sizeof(ccl_fields)-1] = '\0';
4823             break;
4824         case 'q':
4825             strncpy (cql_fields, arg, sizeof(cql_fields)-1);
4826             cql_fields[sizeof(cql_fields)-1] = '\0';
4827             break;
4828         case 'b':
4829             if (!strcmp(arg, "-"))
4830                 ber_file=stderr;
4831             else
4832                 ber_file=fopen(arg, "a");
4833             break;
4834         case 'a':
4835             if (!strcmp(arg, "-"))
4836                 apdu_file=stderr;
4837             else
4838                 apdu_file=fopen(arg, "a");
4839             break;
4840         case 'x':
4841             hex_dump = 1;
4842             break;
4843         case 'p':
4844             yazProxy = xstrdup(arg);
4845             break;
4846         case 'u':
4847             if (!auth_command)
4848             {
4849                 auth_command = (char *) xmalloc (strlen(arg)+6);
4850                 strcpy (auth_command, "auth ");
4851                 strcat (auth_command, arg);
4852             }
4853             break;
4854         case 'v':
4855             yaz_log_init(yaz_log_mask_str(arg), "", 0);
4856             break;
4857         case 'V':
4858             show_version();
4859             break;
4860         default:
4861             fprintf (stderr, "Usage: %s "
4862                      " [-a <apdulog>]"
4863                      " [-b berdump]"
4864                      " [-d dump]\n"
4865                      " [-c cclfields]"
4866                      " [-k size]"
4867                      " [-m <marclog>]\n" 
4868                      " [-p <proxy-addr>]"
4869                      " [-q cqlfields]"
4870                      " [-u <auth>]"
4871                      " [-V]"
4872                      " [<server-addr>]\n",
4873                      prog);
4874             exit (1);
4875         }      
4876     }
4877     initialize();
4878     if (auth_command)
4879     {
4880 #ifdef HAVE_GETTIMEOFDAY
4881         gettimeofday (&tv_start, 0);
4882 #endif
4883         process_cmd_line (auth_command);
4884 #if HAVE_READLINE_HISTORY_H
4885         add_history(auth_command);
4886 #endif
4887         xfree(auth_command);
4888     }
4889     if (open_command)
4890     {
4891 #ifdef HAVE_GETTIMEOFDAY
4892         gettimeofday (&tv_start, 0);
4893 #endif
4894         process_cmd_line (open_command);
4895 #if HAVE_READLINE_HISTORY_H
4896         add_history(open_command);
4897 #endif
4898         xfree(open_command);
4899     }
4900     client();
4901     exit_client(0);
4902     return 0;
4903 }
4904 /*
4905  * Local variables:
4906  * c-basic-offset: 4
4907  * indent-tabs-mode: nil
4908  * End:
4909  * vim: shiftwidth=4 tabstop=8 expandtab
4910  */
4911