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