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