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