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