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