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