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