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