SRW fixes
[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.191 2003-04-30 14:04:45 adam Exp $
6  */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <assert.h>
11 #if HAVE_LOCALE_H
12 #include <locale.h>
13 #endif
14
15 #if HAVE_LANGINFO_H
16 #include <langinfo.h>
17 #endif
18
19 #include <time.h>
20 #include <ctype.h>
21
22 #ifdef WIN32
23 #include <io.h>
24 #define S_ISREG(x) (x & _S_IFREG)
25 #define S_ISDIR(x) (x & _S_IFDIR)
26 #endif
27
28 #include <yaz/yaz-util.h>
29
30 #include <yaz/comstack.h>
31
32 #include <yaz/proto.h>
33 #include <yaz/marcdisp.h>
34 #include <yaz/diagbib1.h>
35 #include <yaz/otherinfo.h>
36 #include <yaz/charneg.h>
37
38 #include <yaz/pquery.h>
39 #include <yaz/sortspec.h>
40
41 #include <yaz/ill.h>
42 #include <yaz/srw.h>
43 #include <yaz/yaz-ccl.h>
44
45 #if HAVE_READLINE_READLINE_H
46 #include <readline/readline.h>
47 #include <unistd.h>
48 #endif
49 #if HAVE_READLINE_HISTORY_H
50 #include <readline/history.h>
51 #endif
52
53 #include <sys/stat.h>
54
55 #include "admin.h"
56 #include "tabcomplete.h"
57
58 #define C_PROMPT "Z> "
59
60 static char *codeset = 0;               /* character set for output */
61
62 static ODR out, in, print;              /* encoding and decoding streams */
63 static ODR srw_sr_odr_out = 0;
64 static Z_SRW_PDU *srw_sr = 0;
65 static FILE *apdu_file = 0;
66 static FILE *ber_file = 0;
67 static COMSTACK conn = 0;               /* our z-association */
68 static Z_IdAuthentication *auth = 0;    /* our current auth definition */
69 char *databaseNames[128];
70 int num_databaseNames = 0;
71 static Z_External *record_last = 0;
72 static int setnumber = -1;              /* current result set number */
73 static int smallSetUpperBound = 0;
74 static int largeSetLowerBound = 1;
75 static int mediumSetPresentNumber = 0;
76 static Z_ElementSetNames *elementSetNames = 0; 
77 static int setno = 1;                   /* current set offset */
78 static enum oid_proto protocol = PROTO_Z3950;      /* current app protocol */
79 static enum oid_value recordsyntax = VAL_USMARC;
80 //static enum oid_value schema = VAL_NONE;
81 static char *schema = 0;
82 static int sent_close = 0;
83 static NMEM session_mem = NULL;         /* memory handle for init-response */
84 static Z_InitResponse *session = 0;     /* session parameters */
85 static char last_scan_line[512] = "0";
86 static char last_scan_query[512] = "0";
87 static char ccl_fields[512] = "default.bib";
88 static char* esPackageName = 0;
89 static char* yazProxy = 0;
90 static int kilobytes = 1024;
91 static char* yazCharset = 0;
92 static char* yazLang = 0;
93
94 static char last_cmd[32] = "?";
95 static FILE *marc_file = 0;
96 static char *refid = NULL;
97 static char *last_open_command = NULL;
98 static int auto_reconnect = 0;
99
100 typedef enum {
101     QueryType_Prefix,
102     QueryType_CCL,
103     QueryType_CCL2RPN,
104     QueryType_CQL
105 } QueryType;
106
107 static QueryType queryType = QueryType_Prefix;
108
109 static CCL_bibset bibset;               /* CCL bibset handle */
110
111 #if HAVE_READLINE_COMPLETION_OVER
112
113 #else
114 /* readline doesn't have this var. Define it ourselves. */
115 int rl_attempted_completion_over = 0;
116 #endif
117
118 /* set this one to 1, to avoid decode of unknown MARCs  */
119 #define AVOID_MARC_DECODE 1
120
121 #define maxOtherInfosSupported 10
122 struct {
123     int oidval;
124     char* value;
125 } extraOtherInfos[maxOtherInfosSupported];
126         
127
128 void process_cmd_line(char* line);
129 char ** readline_completer(char *text, int start, int end);
130 char *command_generator(const char *text, int state);
131 char** curret_global_list=NULL;
132 int cmd_register_tab(const char* arg);
133
134 static void close_session (void);
135
136 ODR getODROutputStream()
137 {
138     return out;
139 }
140
141 const char* query_type_as_string(QueryType q) 
142 {
143     switch (q) { 
144     case QueryType_Prefix: return "prefix (RPN sent to server)";
145     case QueryType_CCL: return "CCL (CCL sent to server) ";
146     case QueryType_CCL2RPN: return "CCL -> RPN (RPN sent to server)";
147     case QueryType_CQL: return "CQL (CQL sent to server)";
148     default: 
149         return "unknown Query type internal yaz-client error";
150     }
151 }
152
153
154 void do_hex_dump(char* buf,int len) 
155 {
156 #if 0 
157     int i,x;
158     for( i=0; i<len ; i=i+16 ) 
159     {                   
160         printf(" %4.4d ",i);
161         for(x=0 ; i+x<len && x<16; ++x) 
162         {
163             printf("%2.2X ",(unsigned int)((unsigned char)buf[i+x]));
164         }
165         printf("\n");
166     }
167 #endif
168 }
169
170 void add_otherInfos(Z_APDU *a) 
171 {
172     Z_OtherInformation **oi;
173     int i;
174                 
175     yaz_oi_APDU(a, &oi);
176     for(i=0; i<maxOtherInfosSupported; ++i) 
177     {
178         if(extraOtherInfos[i].oidval != -1) 
179             yaz_oi_set_string_oidval(oi, out, extraOtherInfos[i].oidval,
180                                      1, extraOtherInfos[i].value);
181     }   
182 }
183
184 void send_apdu(Z_APDU *a)
185 {
186     char *buf;
187     int len;
188     
189     add_otherInfos(a);
190     
191     if (apdu_file)
192     {
193         z_APDU(print, &a, 0, 0);
194         odr_reset(print);
195     }
196     if (!z_APDU(out, &a, 0, 0))
197     {
198         odr_perror(out, "Encoding APDU");
199         close_session();
200         return;
201     }
202     buf = odr_getbuf(out, &len, 0);
203     if (ber_file)
204         odr_dumpBER(ber_file, buf, len);
205     /* printf ("sending APDU of size %d\n", len); */
206     if (cs_put(conn, buf, len) < 0)
207     {
208         fprintf(stderr, "cs_put: %s", cs_errmsg(cs_errno(conn)));
209         close_session();
210         return;
211     }
212     do_hex_dump(buf,len);
213     odr_reset(out); /* release the APDU structure  */
214 }
215
216 static void print_stringn(const unsigned char *buf, size_t len)
217 {
218     size_t i;
219     for (i = 0; i<len; i++)
220         if ((buf[i] <= 126 && buf[i] >= 32) || strchr ("\n\r\t\f", buf[i]))
221             printf ("%c", buf[i]);
222         else
223             printf ("\\X%02X", buf[i]);
224 }
225
226 static void print_refid (Z_ReferenceId *id)
227 {
228     if (id)
229     {
230         printf ("Reference Id: ");
231         print_stringn (id->buf, id->len);
232         printf ("\n");
233     }
234 }
235
236 static Z_ReferenceId *set_refid (ODR out)
237 {
238     Z_ReferenceId *id;
239     if (!refid)
240         return 0;
241     id = (Z_ReferenceId *) odr_malloc (out, sizeof(*id));
242     id->size = id->len = strlen(refid);
243     id->buf = (unsigned char *) odr_malloc (out, id->len);
244     memcpy (id->buf, refid, id->len);
245     return id;
246 }   
247
248 /* INIT SERVICE ------------------------------- */
249
250 static void send_initRequest(const char* type_and_host)
251 {
252     Z_APDU *apdu = zget_APDU(out, Z_APDU_initRequest);
253     Z_InitRequest *req = apdu->u.initRequest;
254
255     ODR_MASK_SET(req->options, Z_Options_search);
256     ODR_MASK_SET(req->options, Z_Options_present);
257     ODR_MASK_SET(req->options, Z_Options_namedResultSets);
258     ODR_MASK_SET(req->options, Z_Options_triggerResourceCtrl);
259     ODR_MASK_SET(req->options, Z_Options_scan);
260     ODR_MASK_SET(req->options, Z_Options_sort);
261     ODR_MASK_SET(req->options, Z_Options_extendedServices);
262     ODR_MASK_SET(req->options, Z_Options_delSet);
263
264     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_1);
265     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_2);
266     ODR_MASK_SET(req->protocolVersion, Z_ProtocolVersion_3);
267
268     *req->maximumRecordSize = 1024*kilobytes;
269     *req->preferredMessageSize = 1024*kilobytes;
270
271     req->idAuthentication = auth;
272
273     req->referenceId = set_refid (out);
274
275     if (yazProxy) 
276         yaz_oi_set_string_oidval(&req->otherInfo, out, VAL_PROXY,
277         1, type_and_host);
278     
279     if (yazCharset || yazLang) {
280         Z_OtherInformation **p;
281         Z_OtherInformationUnit *p0;
282         
283         yaz_oi_APDU(apdu, &p);
284         
285         if ((p0=yaz_oi_update(p, out, NULL, 0, 0))) {
286                 ODR_MASK_SET(req->options, Z_Options_negotiationModel);
287                 
288                 p0->which = Z_OtherInfo_externallyDefinedInfo;
289                 p0->information.externallyDefinedInfo =
290                         yaz_set_proposal_charneg(out,
291                                 (const char**)&yazCharset, (yazCharset)?1:0,
292                                 (const char**)&yazLang, (yazLang)?1:0, 1);
293         }
294     }
295     
296     send_apdu(apdu);
297     printf("Sent initrequest.\n");
298 }
299
300 static int process_initResponse(Z_InitResponse *res)
301 {
302     int ver = 0;
303     /* save session parameters for later use */
304     session_mem = odr_extract_mem(in);
305     session = res;
306
307     for (ver = 0; ver<5; ver++)
308         if (!ODR_MASK_GET(res->protocolVersion, ver))
309             break;
310
311     if (!*res->result)
312         printf("Connection rejected by v%d target.\n", ver);
313     else
314         printf("Connection accepted by v%d target.\n", ver);
315     if (res->implementationId)
316         printf("ID     : %s\n", res->implementationId);
317     if (res->implementationName)
318         printf("Name   : %s\n", res->implementationName);
319     if (res->implementationVersion)
320         printf("Version: %s\n", res->implementationVersion);
321     if (res->userInformationField)
322     {
323         printf("UserInformationfield:\n");
324         if (!z_External(print, (Z_External**)&res-> userInformationField,
325             0, 0))
326         {
327             odr_perror(print, "Printing userinfo\n");
328             odr_reset(print);
329         }
330         if (res->userInformationField->which == Z_External_octet)
331         {
332             printf("Guessing visiblestring:\n");
333             printf("'%s'\n", res->userInformationField->u. octet_aligned->buf);
334         }
335         odr_reset (print);
336     }
337     printf ("Options:");
338     if (ODR_MASK_GET(res->options, Z_Options_search))
339         printf (" search");
340     if (ODR_MASK_GET(res->options, Z_Options_present))
341         printf (" present");
342     if (ODR_MASK_GET(res->options, Z_Options_delSet))
343         printf (" delSet");
344     if (ODR_MASK_GET(res->options, Z_Options_resourceReport))
345         printf (" resourceReport");
346     if (ODR_MASK_GET(res->options, Z_Options_resourceCtrl))
347         printf (" resourceCtrl");
348     if (ODR_MASK_GET(res->options, Z_Options_accessCtrl))
349         printf (" accessCtrl");
350     if (ODR_MASK_GET(res->options, Z_Options_scan))
351         printf (" scan");
352     if (ODR_MASK_GET(res->options, Z_Options_sort))
353         printf (" sort");
354     if (ODR_MASK_GET(res->options, Z_Options_extendedServices))
355         printf (" extendedServices");
356     if (ODR_MASK_GET(res->options, Z_Options_level_1Segmentation))
357         printf (" level1Segmentation");
358     if (ODR_MASK_GET(res->options, Z_Options_level_2Segmentation))
359         printf (" level2Segmentation");
360     if (ODR_MASK_GET(res->options, Z_Options_concurrentOperations))
361         printf (" concurrentOperations");
362     if (ODR_MASK_GET(res->options, Z_Options_namedResultSets))
363     {
364         printf (" namedResultSets");
365         setnumber = 0;
366     }
367     if (ODR_MASK_GET(res->options, Z_Options_encapsulation))
368         printf (" encapsulation");
369     if (ODR_MASK_GET(res->options, Z_Options_resultCount))
370         printf (" resultCount");
371     if (ODR_MASK_GET(res->options, Z_Options_negotiationModel))
372         printf (" negotiationModel");
373     if (ODR_MASK_GET(res->options, Z_Options_duplicateDetection))
374         printf (" duplicateDetection");
375     if (ODR_MASK_GET(res->options, Z_Options_queryType104))
376         printf (" queryType104");
377     printf ("\n");
378     
379     if (ODR_MASK_GET(res->options, Z_Options_negotiationModel)) {
380     
381         Z_CharSetandLanguageNegotiation *p =
382                 yaz_get_charneg_record(res->otherInfo);
383         
384         if (p) {
385             
386             char *charset=NULL, *lang=NULL;
387             int selected;
388             
389             yaz_get_response_charneg(session_mem, p, &charset, &lang,
390                                      &selected);
391             
392             printf("Accepted character set : %s\n", charset);
393             printf("Accepted code language : %s\n", lang ? lang : "none");
394             printf("Accepted records in ...: %d\n", selected );
395         }
396     }
397     fflush (stdout);
398     return 0;
399 }
400
401 static int set_base(const char *arg)
402 {
403     int i;
404     const char *cp;
405
406     for (i = 0; i<num_databaseNames; i++)
407         xfree (databaseNames[i]);
408     num_databaseNames = 0;
409     while (1)
410     {
411         char *cp1;
412         if (!(cp = strchr(arg, ' ')))
413             cp = arg + strlen(arg);
414         if (cp - arg < 1)
415             break;
416         databaseNames[num_databaseNames] = (char *)xmalloc (1 + cp - arg);
417         memcpy (databaseNames[num_databaseNames], arg, cp - arg);
418         databaseNames[num_databaseNames][cp - arg] = '\0';
419
420         for (cp1 = databaseNames[num_databaseNames]; *cp1 ; cp1++)
421             if (*cp1 == '+')
422                 *cp1 = ' ';
423         num_databaseNames++;
424
425         if (!*cp)
426             break;
427         arg = cp+1;
428     }
429     if (num_databaseNames == 0)
430     {
431         num_databaseNames = 1;
432         databaseNames[0] = xstrdup("");
433     }
434     return 1;
435 }
436
437 static int cmd_base(const char *arg)
438 {
439     if (!*arg)
440     {
441         printf("Usage: base <database> <database> ...\n");
442         return 0;
443     }
444     return set_base(arg);
445 }
446
447 void cmd_open_remember_last_open_command(const char* arg, char* new_open_command)
448 {
449     if(last_open_command != arg) 
450     {
451         if(last_open_command) xfree(last_open_command);
452         last_open_command = xstrdup(new_open_command);
453     }
454 }
455
456 int session_connect(const char *arg)
457 {
458     void *add;
459     char type_and_host[101];
460     const char *basep = 0;
461     if (conn)
462     {
463         cs_close (conn);
464         conn = NULL;
465         if (session_mem)
466         {
467             nmem_destroy (session_mem);
468             session_mem = NULL;
469         }
470     }   
471     cs_get_host_args(arg, &basep);
472
473     strncpy(type_and_host, arg, sizeof(type_and_host)-1);
474     type_and_host[sizeof(type_and_host)-1] = '\0';
475
476     cmd_open_remember_last_open_command(arg,type_and_host);
477
478     if (yazProxy)
479         conn = cs_create_host(yazProxy, 1, &add);
480     else
481         conn = cs_create_host(arg, 1, &add);
482     if (!conn)
483     {
484         printf ("Couldn't create comstack\n");
485         return 0;
486     }
487 #if HAVE_XML2
488 #else
489     if (conn->protocol == PROTO_HTTP)
490     {
491         printf ("SRW/HTTP not enabled in this YAZ\n");
492         cs_close(conn);
493         conn = 0;
494         return 0;
495     }
496 #endif
497     protocol = conn->protocol;
498     if (conn->protocol == PROTO_HTTP)
499         set_base("");
500     else
501         set_base("Default");
502     printf("Connecting...");
503     fflush(stdout);
504     if (cs_connect(conn, add) < 0)
505     {
506         printf ("error = %s\n", cs_strerror(conn));
507         if (conn->cerrno == CSYSERR)
508         {
509             char msg[256];
510             yaz_strerror(msg, sizeof(msg));
511             printf ("%s\n", msg);
512         }
513         cs_close(conn);
514         conn = 0;
515         return 0;
516     }
517     printf("OK.\n");
518     if (basep && *basep)
519         set_base (basep);
520     if (protocol == PROTO_Z3950)
521     {
522         send_initRequest(type_and_host);
523         return 2;
524     }
525     return 0;
526 }
527
528 int cmd_open(const char *arg)
529 {
530     static char cur_host[200];
531     if (arg)
532     {
533         strncpy (cur_host, arg, sizeof(cur_host)-1);
534         cur_host[sizeof(cur_host)-1] = 0;
535     }
536     return session_connect(cur_host);
537 }
538
539 void try_reconnect() 
540 {
541     char* open_command;
542         
543     if(!( auto_reconnect && last_open_command) ) return ;
544
545     open_command = (char *) xmalloc (strlen(last_open_command)+6);
546     strcpy (open_command, "open ");
547         
548     strcat (open_command, last_open_command);
549
550     process_cmd_line(open_command);
551         
552     xfree(open_command);                                
553 }
554
555 int cmd_authentication(const char *arg)
556 {
557     static Z_IdAuthentication au;
558     static char user[40], group[40], pass[40];
559     static Z_IdPass idPass;
560     int r;
561
562     if (!*arg)
563     {
564         printf("Auth field set to null\n");
565         auth = 0;
566         return 1;
567     }
568     r = sscanf (arg, "%39s %39s %39s", user, group, pass);
569     if (r == 0)
570     {
571         printf("Auth field set to null\n");
572         auth = 0;
573     }
574     if (r == 1)
575     {
576         auth = &au;
577         au.which = Z_IdAuthentication_open;
578         au.u.open = user;
579     }
580     if (r == 2)
581     {
582         auth = &au;
583         au.which = Z_IdAuthentication_idPass;
584         au.u.idPass = &idPass;
585         idPass.groupId = NULL;
586         idPass.userId = user;
587         idPass.password = group;
588     }
589     if (r == 3)
590     {
591         auth = &au;
592         au.which = Z_IdAuthentication_idPass;
593         au.u.idPass = &idPass;
594         idPass.groupId = group;
595         idPass.userId = user;
596         idPass.password = pass;
597     }
598     return 1;
599 }
600
601 /* SEARCH SERVICE ------------------------------ */
602 static void display_record(Z_External *r);
603
604 static void print_record(const unsigned char *buf, size_t len)
605 {
606     size_t i = len;
607     print_stringn (buf, len);
608     /* add newline if not already added ... */
609     if (i <= 0 || buf[i-1] != '\n')
610         printf ("\n");
611 }
612
613 static void display_record(Z_External *r)
614 {
615     oident *ent = oid_getentbyoid(r->direct_reference);
616
617     record_last = r;
618     /*
619      * Tell the user what we got.
620      */
621     if (r->direct_reference)
622     {
623         printf("Record type: ");
624         if (ent)
625             printf("%s\n", ent->desc);
626         else if (!odr_oid(print, &r->direct_reference, 0, 0))
627         {
628             odr_perror(print, "print oid");
629             odr_reset(print);
630         }
631     }
632     /* Check if this is a known, ASN.1 type tucked away in an octet string */
633     if (ent && r->which == Z_External_octet)
634     {
635         Z_ext_typeent *type = z_ext_getentbyref(ent->value);
636         void *rr;
637
638         if (type)
639         {
640             /*
641              * Call the given decoder to process the record.
642              */
643             odr_setbuf(in, (char*)r->u.octet_aligned->buf,
644                 r->u.octet_aligned->len, 0);
645             if (!(*type->fun)(in, (char **)&rr, 0, 0))
646             {
647                 odr_perror(in, "Decoding constructed record.");
648                 fprintf(stdout, "[Near %d]\n", odr_offset(in));
649                 fprintf(stdout, "Packet dump:\n---------\n");
650                 odr_dumpBER(stdout, (char*)r->u.octet_aligned->buf,
651                             r->u.octet_aligned->len);
652                 fprintf(stdout, "---------\n");
653                 
654                 /* note just ignores the error ant print the bytes form the octet_aligned later */
655             } else {
656                 /*
657                  * Note: we throw away the original, BER-encoded record here.
658                  * Do something else with it if you want to keep it.
659                  */
660                 r->u.sutrs = (Z_SUTRS *) rr; /* we don't actually check the type here. */
661                 r->which = type->what;
662             }
663         }
664     }
665     if (ent && ent->oclass != CLASS_RECSYN) 
666         return;
667     if (ent && ent->value == VAL_SOIF)
668         print_record((const unsigned char *) r->u.octet_aligned->buf,
669                      r->u.octet_aligned->len);
670     else if (r->which == Z_External_octet)
671     {
672         const char *octet_buf = (char*)r->u.octet_aligned->buf;
673         if (ent->value == VAL_TEXT_XML || ent->value == VAL_APPLICATION_XML ||
674             ent->value == VAL_HTML)
675         {
676             print_record((const unsigned char *) octet_buf,
677                          r->u.octet_aligned->len);
678         }
679         else if (ent->value == VAL_POSTSCRIPT)
680         {
681             int size = r->u.octet_aligned->len;
682             if (size > 100)
683                 size = 100;
684             print_record((const unsigned char *) octet_buf, size);
685         }
686         else
687         {
688             if ( 
689 #if AVOID_MARC_DECODE
690                 /* primitive check for a marc OID 5.1-29 except 16 */
691                 ent->oidsuffix[0] == 5 && ent->oidsuffix[1] < 30 &&
692                 ent->oidsuffix[1] != 16
693 #else
694                 1
695 #endif
696                 )
697             {
698                 char *result;
699                 int rlen;
700                 yaz_iconv_t cd = 0;
701                 yaz_marc_t mt = yaz_marc_create();
702                     
703                 if (yaz_marc_decode_buf(mt, octet_buf,r->u.octet_aligned->len,
704                                         &result, &rlen)> 0)
705                 {
706                     char *from = 0;
707                     if (ent->value == VAL_USMARC)
708                     {
709                         if (octet_buf[9] == 'a')
710                             from = "UTF-8";
711                         else
712                             from = "MARC8";
713                     }
714                     else
715                         from = "ISO-8859-1";
716
717                     if (codeset && from)
718                     {   
719                         printf ("convert from %s to %s\n", from, codeset);
720                         cd = yaz_iconv_open(codeset, from);
721                     }
722                     if (!cd)
723                         fwrite (result, 1, rlen, stdout);
724                     else
725                     {
726                         char outbuf[12];
727                         size_t inbytesleft = rlen;
728                         const char *inp = result;
729                         
730                         while (inbytesleft)
731                         {
732                             size_t outbytesleft = sizeof(outbuf);
733                             char *outp = outbuf;
734                             size_t r = yaz_iconv (cd, (char**) &inp,
735                                                   &inbytesleft, 
736                                                   &outp, &outbytesleft);
737                             if (r == (size_t) (-1))
738                             {
739                                 int e = yaz_iconv_error(cd);
740                                 if (e != YAZ_ICONV_E2BIG)
741                                     break;
742                             }
743                             fwrite (outbuf, outp - outbuf, 1, stdout);
744                         }
745                     }
746                 }
747                 else
748                 {
749                     printf ("bad MARC. Dumping as it is:\n");
750                     print_record((const unsigned char*) octet_buf,
751                                   r->u.octet_aligned->len);
752                 }       
753                 yaz_marc_destroy(mt);
754                 if (cd)
755                     yaz_iconv_close(cd);
756             }
757             else
758             {
759                 print_record((const unsigned char*) octet_buf,
760                              r->u.octet_aligned->len);
761             }
762         }
763         if (marc_file)
764             fwrite (octet_buf, 1, r->u.octet_aligned->len, marc_file);
765     }
766     else if (ent && ent->value == VAL_SUTRS)
767     {
768         if (r->which != Z_External_sutrs)
769         {
770             printf("Expecting single SUTRS type for SUTRS.\n");
771             return;
772         }
773         print_record(r->u.sutrs->buf, r->u.sutrs->len);
774     }
775     else if (ent && ent->value == VAL_GRS1)
776     {
777         WRBUF w;
778         if (r->which != Z_External_grs1)
779         {
780             printf("Expecting single GRS type for GRS.\n");
781             return;
782         }
783         w = wrbuf_alloc();
784         yaz_display_grs1(w, r->u.grs1, 0);
785         puts (wrbuf_buf(w));
786         wrbuf_free(w, 1);
787     }
788     else 
789     {
790         printf("Unknown record representation.\n");
791         if (!z_External(print, &r, 0, 0))
792         {
793             odr_perror(print, "Printing external");
794             odr_reset(print);
795         }
796     }
797 }
798
799 static void display_diagrecs(Z_DiagRec **pp, int num)
800 {
801     int i;
802     oident *ent;
803     Z_DefaultDiagFormat *r;
804
805     printf("Diagnostic message(s) from database:\n");
806     for (i = 0; i<num; i++)
807     {
808         Z_DiagRec *p = pp[i];
809         if (p->which != Z_DiagRec_defaultFormat)
810         {
811             printf("Diagnostic record not in default format.\n");
812             return;
813         }
814         else
815             r = p->u.defaultFormat;
816         if (!(ent = oid_getentbyoid(r->diagnosticSetId)) ||
817             ent->oclass != CLASS_DIAGSET || ent->value != VAL_BIB1)
818             printf("Missing or unknown diagset\n");
819         printf("    [%d] %s", *r->condition, diagbib1_str(*r->condition));
820         switch (r->which)
821         {
822         case Z_DefaultDiagFormat_v2Addinfo:
823             printf (" -- v2 addinfo '%s'\n", r->u.v2Addinfo);
824             break;
825         case Z_DefaultDiagFormat_v3Addinfo:
826             printf (" -- v3 addinfo '%s'\n", r->u.v3Addinfo);
827             break;
828         }
829     }
830 }
831
832
833 static void display_nameplusrecord(Z_NamePlusRecord *p)
834 {
835     if (p->databaseName)
836         printf("[%s]", p->databaseName);
837     if (p->which == Z_NamePlusRecord_surrogateDiagnostic)
838         display_diagrecs(&p->u.surrogateDiagnostic, 1);
839     else if (p->which == Z_NamePlusRecord_databaseRecord)
840         display_record(p->u.databaseRecord);
841 }
842
843 static void display_records(Z_Records *p)
844 {
845     int i;
846
847     if (p->which == Z_Records_NSD)
848     {
849         Z_DiagRec dr, *dr_p = &dr;
850         dr.which = Z_DiagRec_defaultFormat;
851         dr.u.defaultFormat = p->u.nonSurrogateDiagnostic;
852         display_diagrecs (&dr_p, 1);
853     }
854     else if (p->which == Z_Records_multipleNSD)
855         display_diagrecs (p->u.multipleNonSurDiagnostics->diagRecs,
856                           p->u.multipleNonSurDiagnostics->num_diagRecs);
857     else 
858     {
859         printf("Records: %d\n", p->u.databaseOrSurDiagnostics->num_records);
860         for (i = 0; i < p->u.databaseOrSurDiagnostics->num_records; i++)
861             display_nameplusrecord(p->u.databaseOrSurDiagnostics->records[i]);
862     }
863 }
864
865 static int send_deleteResultSetRequest(const char *arg)
866 {
867     char names[8][32];
868     int i;
869
870     Z_APDU *apdu = zget_APDU(out, Z_APDU_deleteResultSetRequest);
871     Z_DeleteResultSetRequest *req = apdu->u.deleteResultSetRequest;
872
873     req->referenceId = set_refid (out);
874
875     req->num_resultSetList =
876         sscanf (arg, "%30s %30s %30s %30s %30s %30s %30s %30s",
877                 names[0], names[1], names[2], names[3],
878                 names[4], names[5], names[6], names[7]);
879
880     req->deleteFunction = (int *)
881         odr_malloc (out, sizeof(*req->deleteFunction));
882     if (req->num_resultSetList > 0)
883     {
884         *req->deleteFunction = Z_DeleteRequest_list;
885         req->resultSetList = (char **)
886             odr_malloc (out, sizeof(*req->resultSetList)*
887                         req->num_resultSetList);
888         for (i = 0; i<req->num_resultSetList; i++)
889             req->resultSetList[i] = names[i];
890     }
891     else
892     {
893         *req->deleteFunction = Z_DeleteRequest_all;
894         req->resultSetList = 0;
895     }
896     
897     send_apdu(apdu);
898     printf("Sent deleteResultSetRequest.\n");
899     return 2;
900 }
901
902 #if HAVE_XML2
903 static int send_srw(Z_SRW_PDU *sr)
904 {
905     const char *charset = 0;
906     const char *host_port = 0;
907     char *path = 0;
908     char ctype[50];
909     Z_SOAP_Handler h[2] = {
910         {"http://www.loc.gov/zing/srw/v1.0/", 0, (Z_SOAP_fun) yaz_srw_codec},
911         {0, 0, 0}
912     };
913     ODR o = odr_createmem(ODR_ENCODE);
914     int ret;
915     Z_SOAP *p = odr_malloc(o, sizeof(*p));
916     Z_GDU *gdu;
917
918     path = odr_malloc(out, strlen(databaseNames[0])+2);
919     *path = '/';
920     strcpy(path+1, databaseNames[0]);
921
922     gdu = z_get_HTTP_Request(out);
923     gdu->u.HTTP_Request->path = odr_strdup(out, path);
924
925     if (host_port)
926     {
927         const char *cp0 = strstr(host_port, "://");
928         const char *cp1 = 0;
929         if (cp0)
930             cp0 = cp0+3;
931         else
932             cp0 = host_port;
933
934         cp1 = strchr(cp0, '/');
935         if (!cp1)
936             cp1 = cp0+strlen(cp0);
937
938         if (cp0 && cp1)
939         {
940             char *h = odr_malloc(out, cp1 - cp0 + 1);
941             memcpy (h, cp0, cp1 - cp0);
942             h[cp1-cp0] = '\0';
943             z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
944                               "host", h);
945         }
946     }
947
948     strcpy(ctype, "text/xml");
949     if (charset && strlen(charset) < 20)
950     {
951         strcat(ctype, "; charset=");
952         strcat(ctype, charset);
953     }
954     z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
955                       "Content-Type", ctype);
956     z_HTTP_header_add(out, &gdu->u.HTTP_Request->headers,
957                       "SOAPAction", "\"\"");
958     p->which = Z_SOAP_generic;
959     p->u.generic = odr_malloc(o, sizeof(*p->u.generic));
960     p->u.generic->no = 0;
961     p->u.generic->ns = 0;
962     p->u.generic->p = sr;
963     p->ns = "http://schemas.xmlsoap.org/soap/envelope/";
964
965     ret = z_soap_codec_enc(o, &p,
966                            &gdu->u.HTTP_Request->content_buf,
967                            &gdu->u.HTTP_Request->content_len, h,
968                            charset);
969
970     if (z_GDU(out, &gdu, 0, 0))
971     {
972         /* encode OK */
973         char *buf_out;
974         int len_out;
975         int r;
976         buf_out = odr_getbuf(out, &len_out, 0);
977         
978         /* we don't odr_reset(out), since we may need the buffer again */
979
980         r = cs_put(conn, buf_out, len_out);
981
982         odr_destroy(o);
983         
984         if (r >= 0)
985             return 2;
986     }
987     return 0;
988 }
989 #endif
990
991 #if HAVE_XML2
992 static int send_SRW_searchRequest(const char *arg)
993 {
994     Z_SRW_PDU *sr = 0;
995     
996     if (!srw_sr)
997     {
998         assert(srw_sr_odr_out == 0);
999         srw_sr_odr_out = odr_createmem(ODR_ENCODE);
1000     }
1001     odr_reset(srw_sr_odr_out);
1002
1003     setno = 1;
1004
1005     /* save this for later .. when fetching individual records */
1006     srw_sr = sr = yaz_srw_get(srw_sr_odr_out, Z_SRW_searchRetrieve_request);
1007     sr->u.request->query_type = Z_SRW_query_type_cql;
1008     sr->u.request->query.cql = odr_strdup(srw_sr_odr_out, arg);
1009
1010     sr = yaz_srw_get(out, Z_SRW_searchRetrieve_request);
1011     sr->u.request->query_type = Z_SRW_query_type_cql;
1012     sr->u.request->query.cql = odr_strdup(out, arg);
1013     if (schema)
1014         sr->u.request->recordSchema = schema;
1015     return send_srw(sr);
1016 }
1017 #endif
1018
1019 static int send_searchRequest(const char *arg)
1020 {
1021     Z_APDU *apdu = zget_APDU(out, Z_APDU_searchRequest);
1022     Z_SearchRequest *req = apdu->u.searchRequest;
1023     Z_Query query;
1024     int oid[OID_SIZE];
1025     struct ccl_rpn_node *rpn = NULL;
1026     int error, pos;
1027     char setstring[100];
1028     Z_RPNQuery *RPNquery;
1029     Odr_oct ccl_query;
1030     YAZ_PQF_Parser pqf_parser;
1031     Z_External *ext;
1032
1033     if (queryType == QueryType_CCL2RPN)
1034     {
1035         rpn = ccl_find_str(bibset, arg, &error, &pos);
1036         if (error)
1037         {
1038             printf("CCL ERROR: %s\n", ccl_err_msg(error));
1039             return 0;
1040         }
1041     }
1042     req->referenceId = set_refid (out);
1043     if (!strcmp(arg, "@big")) /* strictly for troublemaking */
1044     {
1045         static unsigned char big[2100];
1046         static Odr_oct bigo;
1047
1048         /* send a very big referenceid to test transport stack etc. */
1049         memset(big, 'A', 2100);
1050         bigo.len = bigo.size = 2100;
1051         bigo.buf = big;
1052         req->referenceId = &bigo;
1053     }
1054     
1055     if (setnumber >= 0)
1056     {
1057         sprintf(setstring, "%d", ++setnumber);
1058         req->resultSetName = setstring;
1059     }
1060     *req->smallSetUpperBound = smallSetUpperBound;
1061     *req->largeSetLowerBound = largeSetLowerBound;
1062     *req->mediumSetPresentNumber = mediumSetPresentNumber;
1063     if (smallSetUpperBound > 0 || (largeSetLowerBound > 1 &&
1064         mediumSetPresentNumber > 0))
1065     {
1066         oident prefsyn;
1067
1068         prefsyn.proto = protocol;
1069         prefsyn.oclass = CLASS_RECSYN;
1070         prefsyn.value = recordsyntax;
1071         req->preferredRecordSyntax =
1072             odr_oiddup(out, oid_ent_to_oid(&prefsyn, oid));
1073         req->smallSetElementSetNames =
1074             req->mediumSetElementSetNames = elementSetNames;
1075     }
1076     req->num_databaseNames = num_databaseNames;
1077     req->databaseNames = databaseNames;
1078
1079     req->query = &query;
1080
1081     switch (queryType)
1082     {
1083     case QueryType_Prefix:
1084         query.which = Z_Query_type_1;
1085         pqf_parser = yaz_pqf_create ();
1086         RPNquery = yaz_pqf_parse (pqf_parser, out, arg);
1087         if (!RPNquery)
1088         {
1089             const char *pqf_msg;
1090             size_t off;
1091             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1092             printf("%*s^\n", off+4, "");
1093             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
1094             
1095             yaz_pqf_destroy (pqf_parser);
1096             return 0;
1097         }
1098         yaz_pqf_destroy (pqf_parser);
1099         query.u.type_1 = RPNquery;
1100         break;
1101     case QueryType_CCL:
1102         query.which = Z_Query_type_2;
1103         query.u.type_2 = &ccl_query;
1104         ccl_query.buf = (unsigned char*) arg;
1105         ccl_query.len = strlen(arg);
1106         break;
1107     case QueryType_CCL2RPN:
1108         query.which = Z_Query_type_1;
1109         RPNquery = ccl_rpn_query(out, rpn);
1110         if (!RPNquery)
1111         {
1112             printf ("Couldn't convert from CCL to RPN\n");
1113             return 0;
1114         }
1115         query.u.type_1 = RPNquery;
1116         ccl_rpn_delete (rpn);
1117         break;
1118     case QueryType_CQL:
1119         query.which = Z_Query_type_104;
1120         ext = (Z_External *) odr_malloc(out, sizeof(*ext));
1121         ext->direct_reference = odr_getoidbystr(out, "1.2.840.10003.16.2");
1122         ext->indirect_reference = 0;
1123         ext->descriptor = 0;
1124         ext->which = Z_External_CQL;
1125         ext->u.cql = odr_strdup(out, arg);
1126         query.u.type_104 =  ext;
1127         break;
1128     default:
1129         printf ("Unsupported query type\n");
1130         return 0;
1131     }
1132     send_apdu(apdu);
1133     setno = 1;
1134     printf("Sent searchRequest.\n");
1135     return 2;
1136 }
1137
1138 /* display Query Expression as part of searchResult-1 */
1139 static void display_queryExpression (Z_QueryExpression *qe)
1140 {
1141     if (!qe)
1142         return;
1143     if (qe->which == Z_QueryExpression_term)
1144     {
1145         if (qe->u.term->queryTerm)
1146         {
1147             Z_Term *term = qe->u.term->queryTerm;
1148             switch (term->which)
1149             {
1150             case Z_Term_general:
1151                 printf (" %.*s", term->u.general->len, term->u.general->buf);
1152                 break;
1153             case Z_Term_characterString:
1154                 printf (" %s", term->u.characterString);
1155                 break;
1156             case Z_Term_numeric:
1157                 printf (" %d", *term->u.numeric);
1158                 break;
1159             case Z_Term_null:
1160                 printf (" null");
1161                 break;
1162             }
1163         }
1164     }
1165 }
1166
1167 /* see if we can find USR:SearchResult-1 */
1168 static void display_searchResult (Z_OtherInformation *o)
1169 {
1170     int i;
1171     if (!o)
1172         return ;
1173     for (i = 0; i < o->num_elements; i++)
1174     {
1175         if (o->list[i]->which == Z_OtherInfo_externallyDefinedInfo)
1176         {
1177             Z_External *ext = o->list[i]->information.externallyDefinedInfo;
1178             
1179             if (ext->which == Z_External_searchResult1)
1180             {
1181                 int j;
1182                 Z_SearchInfoReport *sr = ext->u.searchResult1;
1183                 printf ("SearchResult-1:");
1184                 for (j = 0; j < sr->num; j++)
1185                 {
1186                     if (!sr->elements[j]->subqueryExpression)
1187                         printf (" %d", j);
1188                     display_queryExpression (
1189                         sr->elements[j]->subqueryExpression);
1190                     display_queryExpression (
1191                         sr->elements[j]->subqueryInterpretation);
1192                     display_queryExpression (
1193                         sr->elements[j]->subqueryRecommendation);
1194                     if (sr->elements[j]->subqueryCount)
1195                         printf ("(%d)", *sr->elements[j]->subqueryCount);
1196                 }
1197                 printf ("\n");
1198             }
1199         }
1200     }
1201 }
1202
1203 static int process_searchResponse(Z_SearchResponse *res)
1204 {
1205     printf ("Received SearchResponse.\n");
1206     print_refid (res->referenceId);
1207     if (*res->searchStatus)
1208         printf("Search was a success.\n");
1209     else
1210         printf("Search was a bloomin' failure.\n");
1211     printf("Number of hits: %d", *res->resultCount);
1212     if (setnumber >= 0)
1213         printf (", setno %d", setnumber);
1214     printf ("\n");
1215     display_searchResult (res->additionalSearchInfo);
1216     printf("records returned: %d\n",
1217            *res->numberOfRecordsReturned);
1218     setno += *res->numberOfRecordsReturned;
1219     if (res->records)
1220         display_records(res->records);
1221     return 0;
1222 }
1223
1224 static void print_level(int iLevel)
1225 {
1226     int i;
1227     for (i = 0; i < iLevel * 4; i++)
1228         printf(" ");
1229 }
1230
1231 static void print_int(int iLevel, const char *pTag, int *pInt)
1232 {
1233     if (pInt != NULL)
1234     {
1235         print_level(iLevel);
1236         printf("%s: %d\n", pTag, *pInt);
1237     }
1238 }
1239
1240 static void print_string(int iLevel, const char *pTag, const char *pString)
1241 {
1242     if (pString != NULL)
1243     {
1244         print_level(iLevel);
1245         printf("%s: %s\n", pTag, pString);
1246     }
1247 }
1248
1249 static void print_oid(int iLevel, const char *pTag, Odr_oid *pOid)
1250 {
1251     if (pOid != NULL)
1252     {
1253         int *pInt = pOid;
1254
1255         print_level(iLevel);
1256         printf("%s:", pTag);
1257         for (; *pInt != -1; pInt++)
1258             printf(" %d", *pInt);
1259         printf("\n");
1260     }
1261 }
1262
1263 static void print_referenceId(int iLevel, Z_ReferenceId *referenceId)
1264 {
1265     if (referenceId != NULL)
1266     {
1267         int i;
1268
1269         print_level(iLevel);
1270         printf("Ref Id (%d, %d): ", referenceId->len, referenceId->size);
1271         for (i = 0; i < referenceId->len; i++)
1272             printf("%c", referenceId->buf[i]);
1273         printf("\n");
1274     }
1275 }
1276
1277 static void print_string_or_numeric(int iLevel, const char *pTag, Z_StringOrNumeric *pStringNumeric)
1278 {
1279     if (pStringNumeric != NULL)
1280     {
1281         switch (pStringNumeric->which)
1282         {
1283         case Z_StringOrNumeric_string:
1284             print_string(iLevel, pTag, pStringNumeric->u.string);
1285             break;
1286             
1287         case Z_StringOrNumeric_numeric:
1288             print_int(iLevel, pTag, pStringNumeric->u.numeric);
1289             break;
1290             
1291         default:
1292             print_level(iLevel);
1293             printf("%s: valid type for Z_StringOrNumeric\n", pTag);
1294             break;
1295         }
1296     }
1297 }
1298
1299 static void print_universe_report_duplicate(
1300     int iLevel,
1301     Z_UniverseReportDuplicate *pUniverseReportDuplicate)
1302 {
1303     if (pUniverseReportDuplicate != NULL)
1304     {
1305         print_level(iLevel);
1306         printf("Universe Report Duplicate: \n");
1307         iLevel++;
1308         print_string_or_numeric(iLevel, "Hit No",
1309                                 pUniverseReportDuplicate->hitno);
1310     }
1311 }
1312
1313 static void print_universe_report_hits(
1314     int iLevel,
1315     Z_UniverseReportHits *pUniverseReportHits)
1316 {
1317     if (pUniverseReportHits != NULL)
1318     {
1319         print_level(iLevel);
1320         printf("Universe Report Hits: \n");
1321         iLevel++;
1322         print_string_or_numeric(iLevel, "Database",
1323                                 pUniverseReportHits->database);
1324         print_string_or_numeric(iLevel, "Hits", pUniverseReportHits->hits);
1325     }
1326 }
1327
1328 static void print_universe_report(int iLevel, Z_UniverseReport *pUniverseReport)
1329 {
1330     if (pUniverseReport != NULL)
1331     {
1332         print_level(iLevel);
1333         printf("Universe Report: \n");
1334         iLevel++;
1335         print_int(iLevel, "Total Hits", pUniverseReport->totalHits);
1336         switch (pUniverseReport->which)
1337         {
1338         case Z_UniverseReport_databaseHits:
1339             print_universe_report_hits(iLevel,
1340                                        pUniverseReport->u.databaseHits);
1341             break;
1342             
1343         case Z_UniverseReport_duplicate:
1344             print_universe_report_duplicate(iLevel,
1345                                             pUniverseReport->u.duplicate);
1346             break;
1347             
1348         default:
1349             print_level(iLevel);
1350             printf("Type: %d\n", pUniverseReport->which);
1351             break;
1352         }
1353     }
1354 }
1355
1356 static void print_external(int iLevel, Z_External *pExternal)
1357 {
1358     if (pExternal != NULL)
1359     {
1360         print_level(iLevel);
1361         printf("External: \n");
1362         iLevel++;
1363         print_oid(iLevel, "Direct Reference", pExternal->direct_reference);
1364         print_int(iLevel, "InDirect Reference", pExternal->indirect_reference);
1365         print_string(iLevel, "Descriptor", pExternal->descriptor);
1366         switch (pExternal->which)
1367         {
1368         case Z_External_universeReport:
1369             print_universe_report(iLevel, pExternal->u.universeReport);
1370             break;
1371             
1372         default:
1373             print_level(iLevel);
1374             printf("Type: %d\n", pExternal->which);
1375             break;
1376         }
1377     }
1378 }
1379
1380 static int process_resourceControlRequest (Z_ResourceControlRequest *req)
1381 {
1382     printf ("Received ResourceControlRequest.\n");
1383     print_referenceId(1, req->referenceId);
1384     print_int(1, "Suspended Flag", req->suspendedFlag);
1385     print_int(1, "Partial Results Available", req->partialResultsAvailable);
1386     print_int(1, "Response Required", req->responseRequired);
1387     print_int(1, "Triggered Request Flag", req->triggeredRequestFlag);
1388     print_external(1, req->resourceReport);
1389     return 0;
1390 }
1391
1392 void process_ESResponse(Z_ExtendedServicesResponse *res)
1393 {
1394     printf("Status: ");
1395     switch (*res->operationStatus)
1396     {
1397     case Z_ExtendedServicesResponse_done:
1398         printf ("done\n");
1399         break;
1400     case Z_ExtendedServicesResponse_accepted:
1401         printf ("accepted\n");
1402         break;
1403     case Z_ExtendedServicesResponse_failure:
1404         printf ("failure\n");
1405         display_diagrecs(res->diagnostics, res->num_diagnostics);
1406         break;
1407     default:
1408         printf ("unknown\n");
1409     }
1410     if ( (*res->operationStatus != Z_ExtendedServicesResponse_failure) &&
1411         (res->num_diagnostics != 0) ) {
1412         display_diagrecs(res->diagnostics, res->num_diagnostics);
1413     }
1414     print_refid (res->referenceId);
1415     if (res->taskPackage && 
1416         res->taskPackage->which == Z_External_extendedService)
1417     {
1418         Z_TaskPackage *taskPackage = res->taskPackage->u.extendedService;
1419         Odr_oct *id = taskPackage->targetReference;
1420         Z_External *ext = taskPackage->taskSpecificParameters;
1421         
1422         if (id)
1423         {
1424             printf ("Target Reference: ");
1425             print_stringn (id->buf, id->len);
1426             printf ("\n");
1427         }
1428         if (ext->which == Z_External_update)
1429         {
1430             Z_IUUpdateTaskPackage *utp = ext->u.update->u.taskPackage;
1431             if (utp && utp->targetPart)
1432             {
1433                 Z_IUTargetPart *targetPart = utp->targetPart;
1434                 int i;
1435
1436                 for (i = 0; i<targetPart->num_taskPackageRecords;  i++)
1437                 {
1438
1439                     Z_IUTaskPackageRecordStructure *tpr =
1440                         targetPart->taskPackageRecords[i];
1441                     printf ("task package record %d\n", i+1);
1442                     if (tpr->which == Z_IUTaskPackageRecordStructure_record)
1443                     {
1444                         display_record (tpr->u.record);
1445                     }
1446                     else
1447                     {
1448                         printf ("other type\n");
1449                     }
1450                 }
1451             }
1452         }
1453     }
1454 }
1455
1456 const char *get_ill_element (void *clientData, const char *element)
1457 {
1458     return 0;
1459 }
1460
1461 static Z_External *create_external_itemRequest()
1462 {
1463     struct ill_get_ctl ctl;
1464     ILL_ItemRequest *req;
1465     Z_External *r = 0;
1466     int item_request_size = 0;
1467     char *item_request_buf = 0;
1468
1469     ctl.odr = out;
1470     ctl.clientData = 0;
1471     ctl.f = get_ill_element;
1472     
1473     req = ill_get_ItemRequest(&ctl, "ill", 0);
1474     if (!req)
1475         printf ("ill_get_ItemRequest failed\n");
1476         
1477     if (!ill_ItemRequest (out, &req, 0, 0))
1478     {
1479         if (apdu_file)
1480         {
1481             ill_ItemRequest(print, &req, 0, 0);
1482             odr_reset(print);
1483         }
1484         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1485         if (item_request_buf)
1486             odr_setbuf (out, item_request_buf, item_request_size, 1);
1487         printf ("Couldn't encode ItemRequest, size %d\n", item_request_size);
1488         return 0;
1489     }
1490     else
1491     {
1492         oident oid;
1493         
1494         item_request_buf = odr_getbuf (out, &item_request_size, 0);
1495         oid.proto = PROTO_GENERAL;
1496         oid.oclass = CLASS_GENERAL;
1497         oid.value = VAL_ISO_ILL_1;
1498         
1499         r = (Z_External *) odr_malloc (out, sizeof(*r));
1500         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1501         r->indirect_reference = 0;
1502         r->descriptor = 0;
1503         r->which = Z_External_single;
1504         
1505         r->u.single_ASN1_type = (Odr_oct *)
1506             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1507         r->u.single_ASN1_type->buf = (unsigned char *)
1508         odr_malloc (out, item_request_size);
1509         r->u.single_ASN1_type->len = item_request_size;
1510         r->u.single_ASN1_type->size = item_request_size;
1511         memcpy (r->u.single_ASN1_type->buf, item_request_buf,
1512                 item_request_size);
1513         
1514         do_hex_dump(item_request_buf,item_request_size);
1515     }
1516     return r;
1517 }
1518
1519 static Z_External *create_external_ILL_APDU(int which)
1520 {
1521     struct ill_get_ctl ctl;
1522     ILL_APDU *ill_apdu;
1523     Z_External *r = 0;
1524     int ill_request_size = 0;
1525     char *ill_request_buf = 0;
1526         
1527     ctl.odr = out;
1528     ctl.clientData = 0;
1529     ctl.f = get_ill_element;
1530
1531     ill_apdu = ill_get_APDU(&ctl, "ill", 0);
1532
1533     if (!ill_APDU (out, &ill_apdu, 0, 0))
1534     {
1535         if (apdu_file)
1536         {
1537             printf ("-------------------\n");
1538             ill_APDU(print, &ill_apdu, 0, 0);
1539             odr_reset(print);
1540             printf ("-------------------\n");
1541         }
1542         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1543         if (ill_request_buf)
1544             odr_setbuf (out, ill_request_buf, ill_request_size, 1);
1545         printf ("Couldn't encode ILL-Request, size %d\n", ill_request_size);
1546         return 0;
1547     }
1548     else
1549     {
1550         oident oid;
1551         ill_request_buf = odr_getbuf (out, &ill_request_size, 0);
1552         
1553         oid.proto = PROTO_GENERAL;
1554         oid.oclass = CLASS_GENERAL;
1555         oid.value = VAL_ISO_ILL_1;
1556         
1557         r = (Z_External *) odr_malloc (out, sizeof(*r));
1558         r->direct_reference = odr_oiddup(out,oid_getoidbyent(&oid)); 
1559         r->indirect_reference = 0;
1560         r->descriptor = 0;
1561         r->which = Z_External_single;
1562         
1563         r->u.single_ASN1_type = (Odr_oct *)
1564             odr_malloc (out, sizeof(*r->u.single_ASN1_type));
1565         r->u.single_ASN1_type->buf = (unsigned char *)
1566         odr_malloc (out, ill_request_size);
1567         r->u.single_ASN1_type->len = ill_request_size;
1568         r->u.single_ASN1_type->size = ill_request_size;
1569         memcpy (r->u.single_ASN1_type->buf, ill_request_buf, ill_request_size);
1570 /*         printf ("len = %d\n", ill_request_size); */
1571 /*              do_hex_dump(ill_request_buf,ill_request_size); */
1572 /*              printf("--- end of extenal\n"); */
1573
1574     }
1575     return r;
1576 }
1577
1578
1579 static Z_External *create_ItemOrderExternal(const char *type, int itemno)
1580 {
1581     Z_External *r = (Z_External *) odr_malloc(out, sizeof(Z_External));
1582     oident ItemOrderRequest;
1583   
1584     ItemOrderRequest.proto = PROTO_Z3950;
1585     ItemOrderRequest.oclass = CLASS_EXTSERV;
1586     ItemOrderRequest.value = VAL_ITEMORDER;
1587  
1588     r->direct_reference = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest)); 
1589     r->indirect_reference = 0;
1590     r->descriptor = 0;
1591
1592     r->which = Z_External_itemOrder;
1593
1594     r->u.itemOrder = (Z_ItemOrder *) odr_malloc(out,sizeof(Z_ItemOrder));
1595     memset(r->u.itemOrder, 0, sizeof(Z_ItemOrder));
1596     r->u.itemOrder->which=Z_IOItemOrder_esRequest;
1597
1598     r->u.itemOrder->u.esRequest = (Z_IORequest *) 
1599         odr_malloc(out,sizeof(Z_IORequest));
1600     memset(r->u.itemOrder->u.esRequest, 0, sizeof(Z_IORequest));
1601
1602     r->u.itemOrder->u.esRequest->toKeep = (Z_IOOriginPartToKeep *)
1603         odr_malloc(out,sizeof(Z_IOOriginPartToKeep));
1604     memset(r->u.itemOrder->u.esRequest->toKeep, 0, sizeof(Z_IOOriginPartToKeep));
1605     r->u.itemOrder->u.esRequest->notToKeep = (Z_IOOriginPartNotToKeep *)
1606         odr_malloc(out,sizeof(Z_IOOriginPartNotToKeep));
1607     memset(r->u.itemOrder->u.esRequest->notToKeep, 0, sizeof(Z_IOOriginPartNotToKeep));
1608
1609     r->u.itemOrder->u.esRequest->toKeep->supplDescription = NULL;
1610     r->u.itemOrder->u.esRequest->toKeep->contact = NULL;
1611     r->u.itemOrder->u.esRequest->toKeep->addlBilling = NULL;
1612
1613     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem =
1614         (Z_IOResultSetItem *) odr_malloc(out, sizeof(Z_IOResultSetItem));
1615     memset(r->u.itemOrder->u.esRequest->notToKeep->resultSetItem, 0, sizeof(Z_IOResultSetItem));
1616     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->resultSetId = "1";
1617
1618     r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item =
1619         (int *) odr_malloc(out, sizeof(int));
1620     *r->u.itemOrder->u.esRequest->notToKeep->resultSetItem->item = itemno;
1621
1622     if (!strcmp (type, "item") || !strcmp(type, "2"))
1623     {
1624         printf ("using item-request\n");
1625         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
1626             create_external_itemRequest();
1627     }
1628     else if (!strcmp(type, "ill") || !strcmp(type, "1"))
1629     {
1630         printf ("using ILL-request\n");
1631         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 
1632             create_external_ILL_APDU(ILL_APDU_ILL_Request);
1633     }
1634     else if (!strcmp(type, "xml") || !strcmp(type, "3"))
1635     {
1636     const char *xml_buf =
1637         "<itemorder>\n"
1638         "  <type>request</type>\n"
1639         "  <libraryNo>000200</libraryNo>\n"
1640         "  <borrowerTicketNo> 1212 </borrowerTicketNo>\n"
1641         "</itemorder>";
1642         r->u.itemOrder->u.esRequest->notToKeep->itemRequest =
1643             z_ext_record (out, VAL_TEXT_XML, xml_buf, strlen(xml_buf));
1644     }
1645     else
1646         r->u.itemOrder->u.esRequest->notToKeep->itemRequest = 0;
1647
1648     return r;
1649 }
1650
1651 static int send_itemorder(const char *type, int itemno)
1652 {
1653     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest);
1654     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1655     oident ItemOrderRequest;
1656
1657     ItemOrderRequest.proto = PROTO_Z3950;
1658     ItemOrderRequest.oclass = CLASS_EXTSERV;
1659     ItemOrderRequest.value = VAL_ITEMORDER;
1660     req->packageType = odr_oiddup(out,oid_getoidbyent(&ItemOrderRequest));
1661     req->packageName = esPackageName;
1662
1663     req->taskSpecificParameters = create_ItemOrderExternal(type, itemno);
1664
1665     send_apdu(apdu);
1666     return 0;
1667 }
1668
1669 static int only_z3950()
1670 {
1671     if (protocol == PROTO_HTTP)
1672     {
1673         printf ("Not supported by SRW\n");
1674         return 1;
1675     }
1676     return 0;
1677 }
1678
1679 static int cmd_update(const char *arg)
1680 {
1681     Z_APDU *apdu = zget_APDU(out, Z_APDU_extendedServicesRequest );
1682     Z_ExtendedServicesRequest *req = apdu->u.extendedServicesRequest;
1683     Z_External *r;
1684     int oid[OID_SIZE];
1685     Z_IUOriginPartToKeep *toKeep;
1686     Z_IUSuppliedRecords *notToKeep;
1687     oident update_oid;
1688     char action[20], recid[20], fname[80];
1689     int action_no;
1690     Z_External *record_this = 0;
1691
1692     if (only_z3950())
1693         return 0;
1694     *action = 0;
1695     *recid = 0;
1696     *fname = 0;
1697     sscanf (arg, "%19s %19s %79s", action, recid, fname);
1698
1699     if (!strcmp (action, "insert"))
1700         action_no = Z_IUOriginPartToKeep_recordInsert;
1701     else if (!strcmp (action, "replace"))
1702         action_no = Z_IUOriginPartToKeep_recordReplace;
1703     else if (!strcmp (action, "delete"))
1704         action_no = Z_IUOriginPartToKeep_recordDelete;
1705     else if (!strcmp (action, "update"))
1706         action_no = Z_IUOriginPartToKeep_specialUpdate;
1707     else 
1708     {
1709         printf ("Bad action: %s\n", action);
1710         printf ("Possible values: insert, replace, delete, update\n");
1711         return 0;
1712     }
1713
1714     if (*fname)
1715     {
1716         FILE *inf;
1717         struct stat status;
1718         stat (fname, &status);
1719         if (S_ISREG(status.st_mode) && (inf = fopen(fname, "r")))
1720         {
1721             size_t len = status.st_size;
1722             char *buf = (char *) xmalloc (len);
1723
1724             fread (buf, 1, len, inf);
1725
1726             fclose (inf);
1727             
1728             record_this = z_ext_record (out, VAL_TEXT_XML, buf, len);
1729             
1730             xfree (buf);
1731         }
1732         else
1733         {
1734             printf ("File %s doesn't exist\n", fname);
1735             return 0;
1736         }
1737     }
1738     else
1739     {
1740         if (!record_last)
1741         {
1742             printf ("No last record (update ignored)\n");
1743             return 0;
1744         }
1745         record_this = record_last;
1746     }
1747
1748     update_oid.proto = PROTO_Z3950;
1749     update_oid.oclass = CLASS_EXTSERV;
1750     update_oid.value = VAL_DBUPDATE;
1751     oid_ent_to_oid (&update_oid, oid);
1752     req->packageType = odr_oiddup(out,oid);
1753     req->packageName = esPackageName;
1754     
1755     req->referenceId = set_refid (out);
1756
1757     r = req->taskSpecificParameters = (Z_External *)
1758         odr_malloc (out, sizeof(*r));
1759     r->direct_reference = odr_oiddup(out,oid);
1760     r->indirect_reference = 0;
1761     r->descriptor = 0;
1762     r->which = Z_External_update;
1763     r->u.update = (Z_IUUpdate *) odr_malloc(out, sizeof(*r->u.update));
1764     r->u.update->which = Z_IUUpdate_esRequest;
1765     r->u.update->u.esRequest = (Z_IUUpdateEsRequest *)
1766         odr_malloc(out, sizeof(*r->u.update->u.esRequest));
1767     toKeep = r->u.update->u.esRequest->toKeep = (Z_IUOriginPartToKeep *)
1768         odr_malloc(out, sizeof(*r->u.update->u.esRequest->toKeep));
1769     toKeep->databaseName = databaseNames[0];
1770     toKeep->schema = 0;
1771     toKeep->elementSetName = 0;
1772     toKeep->actionQualifier = 0;
1773     toKeep->action = (int *) odr_malloc(out, sizeof(*toKeep->action));
1774     *toKeep->action = action_no;
1775
1776     notToKeep = r->u.update->u.esRequest->notToKeep = (Z_IUSuppliedRecords *)
1777         odr_malloc(out, sizeof(*r->u.update->u.esRequest->notToKeep));
1778     notToKeep->num = 1;
1779     notToKeep->elements = (Z_IUSuppliedRecords_elem **)
1780         odr_malloc(out, sizeof(*notToKeep->elements));
1781     notToKeep->elements[0] = (Z_IUSuppliedRecords_elem *)
1782         odr_malloc(out, sizeof(**notToKeep->elements));
1783     notToKeep->elements[0]->which = Z_IUSuppliedRecords_elem_opaque;
1784     if (*recid)
1785     {
1786         notToKeep->elements[0]->u.opaque = (Odr_oct *)
1787             odr_malloc (out, sizeof(Odr_oct));
1788         notToKeep->elements[0]->u.opaque->buf = (unsigned char *) recid;
1789         notToKeep->elements[0]->u.opaque->size = strlen(recid);
1790         notToKeep->elements[0]->u.opaque->len = strlen(recid);
1791     }
1792     else
1793         notToKeep->elements[0]->u.opaque = 0;
1794     notToKeep->elements[0]->supplementalId = 0;
1795     notToKeep->elements[0]->correlationInfo = 0;
1796     notToKeep->elements[0]->record = record_this;
1797     
1798     send_apdu(apdu);
1799
1800     return 2;
1801 }
1802
1803 static int cmd_itemorder(const char *arg)
1804 {
1805     char type[12];
1806     int itemno;
1807    
1808     if (only_z3950())
1809         return 0;
1810     if (sscanf (arg, "%10s %d", type, &itemno) != 2)
1811         return 0;
1812
1813     printf("Item order request\n");
1814     fflush(stdout);
1815     send_itemorder(type, itemno);
1816     return 2;
1817 }
1818
1819 static int cmd_find(const char *arg)
1820 {
1821     if (!*arg)
1822     {
1823         printf("Find what?\n");
1824         return 0;
1825     }
1826     if (protocol == PROTO_HTTP)
1827     {
1828 #if HAVE_XML2
1829         if (!conn)
1830             cmd_open(0);
1831         if (!send_SRW_searchRequest(arg))
1832             return 0;
1833 #else
1834         return 0;
1835 #endif
1836     }
1837     else
1838     {
1839         if (!conn)
1840         {
1841             try_reconnect(); 
1842             
1843             if (!conn) {                                        
1844                 printf("Not connected yet\n");
1845                 return 0;
1846             }
1847         }
1848         if (!send_searchRequest(arg))
1849             return 0;
1850     }
1851     return 2;
1852 }
1853
1854 static int cmd_delete(const char *arg)
1855 {
1856     if (!conn)
1857     {
1858         printf("Not connected yet\n");
1859         return 0;
1860     }
1861     if (only_z3950())
1862         return 0;
1863     if (!send_deleteResultSetRequest(arg))
1864         return 0;
1865     return 2;
1866 }
1867
1868 static int cmd_ssub(const char *arg)
1869 {
1870     if (!(smallSetUpperBound = atoi(arg)))
1871         return 0;
1872     return 1;
1873 }
1874
1875 static int cmd_lslb(const char *arg)
1876 {
1877     if (only_z3950())
1878         return 0;
1879     if (!(largeSetLowerBound = atoi(arg)))
1880         return 0;
1881     return 1;
1882 }
1883
1884 static int cmd_mspn(const char *arg)
1885 {
1886     if (only_z3950())
1887         return 0;
1888     if (!(mediumSetPresentNumber = atoi(arg)))
1889         return 0;
1890     return 1;
1891 }
1892
1893 static int cmd_status(const char *arg)
1894 {
1895     printf("smallSetUpperBound: %d\n", smallSetUpperBound);
1896     printf("largeSetLowerBound: %d\n", largeSetLowerBound);
1897     printf("mediumSetPresentNumber: %d\n", mediumSetPresentNumber);
1898     return 1;
1899 }
1900
1901 static int cmd_setnames(const char *arg)
1902 {
1903     if (*arg == '1')         /* enable ? */
1904         setnumber = 0;
1905     else if (*arg == '0')    /* disable ? */
1906         setnumber = -1;
1907     else if (setnumber < 0)  /* no args, toggle .. */
1908         setnumber = 0;
1909     else
1910         setnumber = -1;
1911    
1912     if (setnumber >= 0)
1913         printf("Set numbering enabled.\n");
1914     else
1915         printf("Set numbering disabled.\n");
1916     return 1;
1917 }
1918
1919 /* PRESENT SERVICE ----------------------------- */
1920
1921 static void parse_show_args(const char *arg_c, char *setstring,
1922                             int *start, int *number)
1923 {
1924     char arg[40];
1925     char *p;
1926
1927     strncpy(arg, arg_c, sizeof(arg)-1);
1928     arg[sizeof(arg)-1] = '\0';
1929
1930     if ((p = strchr(arg, '+')))
1931     {
1932         *number = atoi(p + 1);
1933         *p = '\0';
1934     }
1935     if (*arg)
1936         *start = atoi(arg);
1937     if (p && (p=strchr(p+1, '+')))
1938         strcpy (setstring, p+1);
1939     else if (setnumber >= 0)
1940         sprintf(setstring, "%d", setnumber);
1941     else
1942         *setstring = '\0';
1943 }
1944
1945 static int send_presentRequest(const char *arg)
1946 {
1947     Z_APDU *apdu = zget_APDU(out, Z_APDU_presentRequest);
1948     Z_PresentRequest *req = apdu->u.presentRequest;
1949     Z_RecordComposition compo;
1950     oident prefsyn;
1951     int nos = 1;
1952     int oid[OID_SIZE];
1953     char setstring[100];
1954
1955     req->referenceId = set_refid (out);
1956
1957     parse_show_args(arg, setstring, &setno, &nos);
1958     if (*setstring)
1959         req->resultSetId = setstring;
1960
1961     req->resultSetStartPoint = &setno;
1962     req->numberOfRecordsRequested = &nos;
1963     prefsyn.proto = protocol;
1964     prefsyn.oclass = CLASS_RECSYN;
1965     prefsyn.value = recordsyntax;
1966     req->preferredRecordSyntax =
1967         odr_oiddup (out, oid_ent_to_oid(&prefsyn, oid));
1968
1969     if (schema)
1970     {
1971         oident prefschema;
1972
1973         prefschema.proto = protocol;
1974         prefschema.oclass = CLASS_SCHEMA;
1975         prefschema.value = oid_getvalbyname(schema);
1976
1977         req->recordComposition = &compo;
1978         compo.which = Z_RecordComp_complex;
1979         compo.u.complex = (Z_CompSpec *)
1980             odr_malloc(out, sizeof(*compo.u.complex));
1981         compo.u.complex->selectAlternativeSyntax = (bool_t *) 
1982             odr_malloc(out, sizeof(bool_t));
1983         *compo.u.complex->selectAlternativeSyntax = 0;
1984
1985         compo.u.complex->generic = (Z_Specification *)
1986             odr_malloc(out, sizeof(*compo.u.complex->generic));
1987         compo.u.complex->generic->which = Z_Schema_oid;
1988         compo.u.complex->generic->schema.oid = (Odr_oid *)
1989             odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1990         if (!compo.u.complex->generic->schema.oid)
1991         {
1992             /* OID wasn't a schema! Try record syntax instead. */
1993             prefschema.oclass = CLASS_RECSYN;
1994             compo.u.complex->generic->schema.oid = (Odr_oid *)
1995                 odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1996         }
1997         if (!elementSetNames)
1998             compo.u.complex->generic->elementSpec = 0;
1999         else
2000         {
2001             compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
2002                 odr_malloc(out, sizeof(Z_ElementSpec));
2003             compo.u.complex->generic->elementSpec->which =
2004                 Z_ElementSpec_elementSetName;
2005             compo.u.complex->generic->elementSpec->u.elementSetName =
2006                 elementSetNames->u.generic;
2007         }
2008         compo.u.complex->num_dbSpecific = 0;
2009         compo.u.complex->dbSpecific = 0;
2010         compo.u.complex->num_recordSyntax = 0;
2011         compo.u.complex->recordSyntax = 0;
2012     }
2013     else if (elementSetNames)
2014     {
2015         req->recordComposition = &compo;
2016         compo.which = Z_RecordComp_simple;
2017         compo.u.simple = elementSetNames;
2018     }
2019     send_apdu(apdu);
2020     printf("Sent presentRequest (%d+%d).\n", setno, nos);
2021     return 2;
2022 }
2023
2024 #if HAVE_XML2
2025 static int send_SRW_presentRequest(const char *arg)
2026 {
2027     char setstring[100];
2028     int nos = 1;
2029     Z_SRW_PDU *sr = srw_sr;
2030
2031     if (!sr)
2032         return 0;
2033     parse_show_args(arg, setstring, &setno, &nos);
2034     sr->u.request->startRecord = odr_intdup(out, setno);
2035     sr->u.request->maximumRecords = odr_intdup(out, nos);
2036     if (schema)
2037         sr->u.request->recordSchema = schema;
2038     return send_srw(sr);
2039 }
2040 #endif
2041
2042 static void close_session (void)
2043 {
2044     cs_close (conn);
2045     conn = 0;
2046     if (session_mem)
2047     {
2048         nmem_destroy (session_mem);
2049         session_mem = NULL;
2050     }
2051     sent_close = 0;
2052     odr_reset(out);
2053     odr_reset(in);
2054     odr_reset(print);
2055 }
2056
2057 void process_close(Z_Close *req)
2058 {
2059     Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
2060     Z_Close *res = apdu->u.close;
2061
2062     static char *reasons[] =
2063     {
2064         "finished",
2065         "shutdown",
2066         "system problem",
2067         "cost limit reached",
2068         "resources",
2069         "security violation",
2070         "protocolError",
2071         "lack of activity",
2072         "peer abort",
2073         "unspecified"
2074     };
2075
2076     printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
2077         req->diagnosticInformation ? req->diagnosticInformation : "NULL");
2078     if (sent_close)
2079         close_session ();
2080     else
2081     {
2082         *res->closeReason = Z_Close_finished;
2083         send_apdu(apdu);
2084         printf("Sent response.\n");
2085         sent_close = 1;
2086     }
2087 }
2088
2089 static int cmd_show(const char *arg)
2090 {
2091     if (protocol == PROTO_HTTP)
2092     {
2093 #if HAVE_XML2
2094         if (!conn)
2095             cmd_open(0);
2096         if (!send_SRW_presentRequest(arg))
2097             return 0;
2098 #else
2099         return 0;
2100 #endif
2101     }
2102     else
2103     {
2104         if (!conn)
2105         {
2106             printf("Not connected yet\n");
2107             return 0;
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 (" schema=%s", 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 && 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  */