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