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