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