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