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