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