ddeacaf5b1e06c7f571f7ae4f23ff301a7baa471
[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.180 2003-02-12 15:06:43 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->which = Z_Specification_oid;
1792         compo.u.complex->generic->u.oid = (Odr_oid *)
1793             odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1794         if (!compo.u.complex->generic->u.oid)
1795         {
1796             /* OID wasn't a schema! Try record syntax instead. */
1797             prefschema.oclass = CLASS_RECSYN;
1798             compo.u.complex->generic->u.oid = (Odr_oid *)
1799                 odr_oiddup(out, oid_ent_to_oid(&prefschema, oid));
1800         }
1801         if (!elementSetNames)
1802             compo.u.complex->generic->elementSpec = 0;
1803         else
1804         {
1805             compo.u.complex->generic->elementSpec = (Z_ElementSpec *)
1806                 odr_malloc(out, sizeof(Z_ElementSpec));
1807             compo.u.complex->generic->elementSpec->which =
1808                 Z_ElementSpec_elementSetName;
1809             compo.u.complex->generic->elementSpec->u.elementSetName =
1810                 elementSetNames->u.generic;
1811         }
1812         compo.u.complex->num_dbSpecific = 0;
1813         compo.u.complex->dbSpecific = 0;
1814         compo.u.complex->num_recordSyntax = 0;
1815         compo.u.complex->recordSyntax = 0;
1816     }
1817     else if (elementSetNames)
1818     {
1819         req->recordComposition = &compo;
1820         compo.which = Z_RecordComp_simple;
1821         compo.u.simple = elementSetNames;
1822     }
1823     send_apdu(apdu);
1824     printf("Sent presentRequest (%d+%d).\n", setno, nos);
1825     return 2;
1826 }
1827     
1828 static void close_session (void)
1829 {
1830     cs_close (conn);
1831     conn = 0;
1832     if (session_mem)
1833     {
1834         nmem_destroy (session_mem);
1835         session_mem = NULL;
1836     }
1837     sent_close = 0;
1838 }
1839
1840 void process_close(Z_Close *req)
1841 {
1842     Z_APDU *apdu = zget_APDU(out, Z_APDU_close);
1843     Z_Close *res = apdu->u.close;
1844
1845     static char *reasons[] =
1846     {
1847         "finished",
1848         "shutdown",
1849         "system problem",
1850         "cost limit reached",
1851         "resources",
1852         "security violation",
1853         "protocolError",
1854         "lack of activity",
1855         "peer abort",
1856         "unspecified"
1857     };
1858
1859     printf("Reason: %s, message: %s\n", reasons[*req->closeReason],
1860         req->diagnosticInformation ? req->diagnosticInformation : "NULL");
1861     if (sent_close)
1862         close_session ();
1863     else
1864     {
1865         *res->closeReason = Z_Close_finished;
1866         send_apdu(apdu);
1867         printf("Sent response.\n");
1868         sent_close = 1;
1869     }
1870 }
1871
1872 static int cmd_show(char *arg)
1873 {
1874     if (!conn)
1875     {
1876         printf("Not connected yet\n");
1877         return 0;
1878     }
1879     if (!send_presentRequest(arg))
1880         return 0;
1881     return 2;
1882 }
1883
1884 int cmd_quit(char *arg)
1885 {
1886     printf("See you later, alligator.\n");
1887     xmalloc_trav ("");
1888     exit(0);
1889     return 0;
1890 }
1891
1892 int cmd_cancel(char *arg)
1893 {
1894     Z_APDU *apdu = zget_APDU(out, Z_APDU_triggerResourceControlRequest);
1895     Z_TriggerResourceControlRequest *req =
1896         apdu->u.triggerResourceControlRequest;
1897     bool_t rfalse = 0;
1898     
1899     if (!conn)
1900     {
1901         printf("Session not initialized yet\n");
1902         return 0;
1903     }
1904     if (!ODR_MASK_GET(session->options, Z_Options_triggerResourceCtrl))
1905     {
1906         printf("Target doesn't support cancel (trigger resource ctrl)\n");
1907         return 0;
1908     }
1909     *req->requestedAction = Z_TriggerResourceCtrl_cancel;
1910     req->resultSetWanted = &rfalse;
1911
1912     send_apdu(apdu);
1913     printf("Sent cancel request\n");
1914     return 2;
1915 }
1916
1917 int send_scanrequest(const char *query, int pp, int num, const char *term)
1918 {
1919     Z_APDU *apdu = zget_APDU(out, Z_APDU_scanRequest);
1920     Z_ScanRequest *req = apdu->u.scanRequest;
1921     int oid[OID_SIZE];
1922     
1923     if (queryType == QueryType_CCL2RPN)
1924     {
1925         oident bib1;
1926         int error, pos;
1927         struct ccl_rpn_node *rpn;
1928
1929         rpn = ccl_find_str (bibset,  query, &error, &pos);
1930         if (error)
1931         {
1932             printf("CCL ERROR: %s\n", ccl_err_msg(error));
1933             return -1;
1934         }
1935         bib1.proto = PROTO_Z3950;
1936         bib1.oclass = CLASS_ATTSET;
1937         bib1.value = VAL_BIB1;
1938         req->attributeSet = oid_ent_to_oid (&bib1, oid);
1939         if (!(req->termListAndStartPoint = ccl_scan_query (out, rpn)))
1940         {
1941             printf("Couldn't convert CCL to Scan term\n");
1942             return -1;
1943         }
1944         ccl_rpn_delete (rpn);
1945     }
1946     else
1947     {
1948         YAZ_PQF_Parser pqf_parser = yaz_pqf_create ();
1949
1950         if (!(req->termListAndStartPoint =
1951               yaz_pqf_scan(pqf_parser, out, &req->attributeSet, query)))
1952         {
1953             const char *pqf_msg;
1954             size_t off;
1955             int code = yaz_pqf_error (pqf_parser, &pqf_msg, &off);
1956             printf("%*s^\n", off+7, "");
1957             printf("Prefix query error: %s (code %d)\n", pqf_msg, code);
1958             yaz_pqf_destroy (pqf_parser);
1959             return -1;
1960         }
1961         yaz_pqf_destroy (pqf_parser);
1962     }
1963     if (term && *term)
1964     {
1965         if (req->termListAndStartPoint->term &&
1966             req->termListAndStartPoint->term->which == Z_Term_general &&
1967             req->termListAndStartPoint->term->u.general)
1968         {
1969             req->termListAndStartPoint->term->u.general->buf =
1970                 (unsigned char *) odr_strdup(out, term);
1971             req->termListAndStartPoint->term->u.general->len =
1972                 req->termListAndStartPoint->term->u.general->size =
1973                 strlen(term);
1974         }
1975     }
1976     req->referenceId = set_refid (out);
1977     req->num_databaseNames = num_databaseNames;
1978     req->databaseNames = databaseNames;
1979     req->numberOfTermsRequested = &num;
1980     req->preferredPositionInResponse = &pp;
1981     send_apdu(apdu);
1982     return 2;
1983 }
1984
1985 int send_sortrequest(char *arg, int newset)
1986 {
1987     Z_APDU *apdu = zget_APDU(out, Z_APDU_sortRequest);
1988     Z_SortRequest *req = apdu->u.sortRequest;
1989     Z_SortKeySpecList *sksl = (Z_SortKeySpecList *)
1990         odr_malloc (out, sizeof(*sksl));
1991     char setstring[32];
1992
1993     if (setnumber >= 0)
1994         sprintf (setstring, "%d", setnumber);
1995     else
1996         sprintf (setstring, "default");
1997
1998     req->referenceId = set_refid (out);
1999
2000     req->num_inputResultSetNames = 1;
2001     req->inputResultSetNames = (Z_InternationalString **)
2002         odr_malloc (out, sizeof(*req->inputResultSetNames));
2003     req->inputResultSetNames[0] = odr_strdup (out, setstring);
2004
2005     if (newset && setnumber >= 0)
2006         sprintf (setstring, "%d", ++setnumber);
2007
2008     req->sortedResultSetName = odr_strdup (out, setstring);
2009
2010     req->sortSequence = yaz_sort_spec (out, arg);
2011     if (!req->sortSequence)
2012     {
2013         printf ("Missing sort specifications\n");
2014         return -1;
2015     }
2016     send_apdu(apdu);
2017     return 2;
2018 }
2019
2020 void display_term(Z_TermInfo *t)
2021 {
2022     if (t->term->which == Z_Term_general)
2023     {
2024         printf("%.*s", t->term->u.general->len, t->term->u.general->buf);
2025         sprintf(last_scan_line, "%.*s", t->term->u.general->len,
2026             t->term->u.general->buf);
2027     }
2028     else
2029         printf("Term (not general)");
2030     if (t->globalOccurrences)
2031         printf (" (%d)\n", *t->globalOccurrences);
2032     else
2033         printf ("\n");
2034 }
2035
2036 void process_scanResponse(Z_ScanResponse *res)
2037 {
2038     int i;
2039     Z_Entry **entries = NULL;
2040     int num_entries = 0;
2041    
2042     printf("Received ScanResponse\n"); 
2043     print_refid (res->referenceId);
2044     printf("%d entries", *res->numberOfEntriesReturned);
2045     if (res->positionOfTerm)
2046         printf (", position=%d", *res->positionOfTerm); 
2047     printf ("\n");
2048     if (*res->scanStatus != Z_Scan_success)
2049         printf("Scan returned code %d\n", *res->scanStatus);
2050     if (!res->entries)
2051         return;
2052     if ((entries = res->entries->entries))
2053         num_entries = res->entries->num_entries;
2054     for (i = 0; i < num_entries; i++)
2055     {
2056         int pos_term = res->positionOfTerm ? *res->positionOfTerm : -1;
2057         if (entries[i]->which == Z_Entry_termInfo)
2058         {
2059             printf("%c ", i + 1 == pos_term ? '*' : ' ');
2060             display_term(entries[i]->u.termInfo);
2061         }
2062         else
2063             display_diagrecs(&entries[i]->u.surrogateDiagnostic, 1);
2064     }
2065     if (res->entries->nonsurrogateDiagnostics)
2066         display_diagrecs (res->entries->nonsurrogateDiagnostics,
2067                           res->entries->num_nonsurrogateDiagnostics);
2068 }
2069
2070 void process_sortResponse(Z_SortResponse *res)
2071 {
2072     printf("Received SortResponse: status=");
2073     switch (*res->sortStatus)
2074     {
2075     case Z_SortStatus_success:
2076         printf ("success"); break;
2077     case Z_SortStatus_partial_1:
2078         printf ("partial"); break;
2079     case Z_SortStatus_failure:
2080         printf ("failure"); break;
2081     default:
2082         printf ("unknown (%d)", *res->sortStatus);
2083     }
2084     printf ("\n");
2085     print_refid (res->referenceId);
2086     if (res->diagnostics)
2087         display_diagrecs(res->diagnostics,
2088                          res->num_diagnostics);
2089 }
2090
2091 void process_deleteResultSetResponse (Z_DeleteResultSetResponse *res)
2092 {
2093     printf("Got deleteResultSetResponse status=%d\n",
2094            *res->deleteOperationStatus);
2095     if (res->deleteListStatuses)
2096     {
2097         int i;
2098         for (i = 0; i < res->deleteListStatuses->num; i++)
2099         {
2100             printf ("%s status=%d\n", res->deleteListStatuses->elements[i]->id,
2101                     *res->deleteListStatuses->elements[i]->status);
2102         }
2103     }
2104 }
2105
2106 int cmd_sort_generic(char *arg, int newset)
2107 {
2108     if (!conn)
2109     {
2110         printf("Session not initialized yet\n");
2111         return 0;
2112     }
2113     if (!ODR_MASK_GET(session->options, Z_Options_sort))
2114     {
2115         printf("Target doesn't support sort\n");
2116         return 0;
2117     }
2118     if (*arg)
2119     {
2120         if (send_sortrequest(arg, newset) < 0)
2121             return 0;
2122         return 2;
2123     }
2124     return 0;
2125 }
2126
2127 int cmd_sort(char *arg)
2128 {
2129     return cmd_sort_generic (arg, 0);
2130 }
2131
2132 int cmd_sort_newset (char *arg)
2133 {
2134     return cmd_sort_generic (arg, 1);
2135 }
2136
2137 int cmd_scan(char *arg)
2138 {
2139     if (!conn)
2140     {
2141                 try_reconnect();
2142                 
2143                 if (!conn) {                                                            
2144                         printf("Session not initialized yet\n");
2145                         return 0;
2146                 };
2147     }
2148     if (!ODR_MASK_GET(session->options, Z_Options_scan))
2149     {
2150         printf("Target doesn't support scan\n");
2151         return 0;
2152     }
2153     if (*arg)
2154     {
2155         strcpy (last_scan_query, arg);
2156         if (send_scanrequest(arg, 1, 20, 0) < 0)
2157             return 0;
2158     }
2159     else
2160     {
2161         if (send_scanrequest(last_scan_query, 1, 20, last_scan_line) < 0)
2162             return 0;
2163     }
2164     return 2;
2165 }
2166
2167 int cmd_schema(char *arg)
2168 {
2169     if (!arg || !*arg)
2170     {
2171         schema = VAL_NONE;
2172         return 1;
2173     }
2174     schema = oid_getvalbyname (arg);
2175     if (schema == VAL_NONE)
2176     {
2177         printf ("unknown schema\n");
2178         return 0;
2179     }
2180     return 1;
2181 }
2182
2183 int cmd_format(char *arg)
2184 {
2185     oid_value nsyntax;
2186     if (!arg || !*arg)
2187     {
2188         printf("Usage: format <recordsyntax>\n");
2189         return 0;
2190     }
2191     nsyntax = oid_getvalbyname (arg);
2192     if (strcmp(arg, "none") && nsyntax == VAL_NONE)
2193     {
2194         printf ("unknown record syntax\n");
2195         return 0;
2196     }
2197     recordsyntax = nsyntax;
2198     return 1;
2199 }
2200
2201 int cmd_elements(char *arg)
2202 {
2203     static Z_ElementSetNames esn;
2204     static char what[100];
2205
2206     if (!arg || !*arg)
2207     {
2208         elementSetNames = 0;
2209         return 1;
2210     }
2211     strcpy(what, arg);
2212     esn.which = Z_ElementSetNames_generic;
2213     esn.u.generic = what;
2214     elementSetNames = &esn;
2215     return 1;
2216 }
2217
2218 int cmd_attributeset(char *arg)
2219 {
2220     char what[100];
2221
2222     if (!arg || !*arg)
2223     {
2224         printf("Usage: attributeset <setname>\n");
2225         return 0;
2226     }
2227     sscanf(arg, "%s", what);
2228     if (p_query_attset (what))
2229     {
2230         printf("Unknown attribute set name\n");
2231         return 0;
2232     }
2233     return 1;
2234 }
2235
2236 int cmd_querytype (char *arg)
2237 {
2238     if (!strcmp (arg, "ccl"))
2239         queryType = QueryType_CCL;
2240     else if (!strcmp (arg, "prefix") || !strcmp(arg, "rpn"))
2241         queryType = QueryType_Prefix;
2242     else if (!strcmp (arg, "ccl2rpn") || !strcmp (arg, "cclrpn"))
2243         queryType = QueryType_CCL2RPN;
2244     else if (!strcmp(arg, "cql"))
2245         queryType = QueryType_CQL;        
2246     else
2247     {
2248         printf ("Querytype must be one of:\n");
2249         printf (" prefix         - Prefix query\n");
2250         printf (" ccl            - CCL query\n");
2251         printf (" ccl2rpn        - CCL query converted to RPN\n");
2252         printf (" cql            - CQL\n");
2253         return 0;
2254     }
2255     return 1;
2256 }
2257
2258 int cmd_refid (char *arg)
2259 {
2260     xfree (refid);
2261     refid = NULL;
2262     if (*arg)
2263     {
2264         refid = (char *) xmalloc (strlen(arg)+1);
2265         strcpy (refid, arg);
2266     }
2267     return 1;
2268 }
2269
2270 int cmd_close(char *arg)
2271 {
2272     Z_APDU *apdu;
2273     Z_Close *req;
2274     if (!conn)
2275         return 0;
2276
2277     apdu = zget_APDU(out, Z_APDU_close);
2278     req = apdu->u.close;
2279     *req->closeReason = Z_Close_finished;
2280     send_apdu(apdu);
2281     printf("Sent close request.\n");
2282     sent_close = 1;
2283     return 2;
2284 }
2285
2286 int cmd_packagename(char* arg)
2287 {
2288     xfree (esPackageName);
2289     esPackageName = NULL;
2290     if (*arg)
2291     {
2292         esPackageName = (char *) xmalloc (strlen(arg)+1);
2293         strcpy (esPackageName, arg);
2294     }
2295     return 1;
2296 }
2297
2298 int cmd_proxy(char* arg)
2299 {
2300     if (*arg == '\0') {
2301                 xfree (yazProxy);
2302                 yazProxy = NULL;
2303         
2304     }
2305     xfree (yazProxy);
2306     yazProxy = NULL;
2307     if (*arg)
2308     {
2309         yazProxy = (char *) xmalloc (strlen(arg)+1);
2310         strcpy (yazProxy, arg);
2311     } 
2312     return 1;
2313 }
2314
2315 int cmd_charset(char* arg)
2316 {
2317     char l1[30], l2[30];
2318
2319     *l1 = *l2 = 0;
2320     if (sscanf(arg, "%29s %29s", l1, l2) < 1)
2321     {
2322         printf("Current character set is `%s'\n", (yazCharset) ? yazCharset:NULL);
2323         return 1;
2324     }
2325     xfree (yazCharset);
2326     yazCharset = NULL;
2327     if (*l1)
2328         yazCharset = xstrdup(l1);
2329     if (*l2)
2330     {
2331         odr_set_charset (out, l1, l2);
2332         odr_set_charset (in, l2, l1);
2333     }
2334     return 1;
2335 }
2336
2337 int cmd_lang(char* arg)
2338 {
2339     if (*arg == '\0') {
2340         printf("Current language is `%s'\n", (yazLang)?yazLang:NULL);
2341         return 1;
2342     }
2343     xfree (yazLang);
2344     yazLang = NULL;
2345     if (*arg)
2346     {
2347         yazLang = (char *) xmalloc (strlen(arg)+1);
2348         strcpy (yazLang, arg);
2349     } 
2350     return 1;
2351 }
2352
2353 int cmd_source(char* arg) 
2354 {
2355     /* first should open the file and read one line at a time.. */
2356     FILE* includeFile;
2357     char line[1024], *cp;
2358
2359     {
2360         char* args_end=(arg)+strlen(arg)-1; 
2361         while(isspace(*args_end)) 
2362         {*args_end=0;
2363         --args_end;}; 
2364     }
2365
2366     REMOVE_TAILING_BLANKS(arg);
2367     
2368     if(strlen(arg)<1) {
2369         fprintf(stderr,"Error in source command use a filename\n");
2370         return -1;
2371     }
2372     
2373     includeFile = fopen (arg, "r");
2374     
2375     if(!includeFile) {
2376         fprintf(stderr,"Unable to open file %s for reading\n",arg);
2377         return -1;
2378     }
2379     
2380     while(!feof(includeFile)) {
2381         memset(line,0,sizeof(line));
2382         fgets(line,sizeof(line),includeFile);
2383         
2384         if(strlen(line) < 2) continue;
2385         if(line[0] == '#') continue;
2386         
2387         if ((cp = strrchr (line, '\n')))
2388             *cp = '\0';
2389         
2390         process_cmd_line(line);
2391     }
2392     
2393     if(fclose(includeFile)<0) {
2394         perror("unable to close include file");
2395         exit(1);
2396     }
2397     return 1;
2398 }
2399
2400 int cmd_subshell(char* args)
2401 {
2402     if(strlen(args)) 
2403         system(args);
2404     else 
2405         system(getenv("SHELL"));
2406     
2407     printf("\n");
2408     return 1;
2409 }
2410
2411 int cmd_set_apdufile(char* arg)
2412 {
2413     REMOVE_TAILING_BLANKS(arg);
2414   
2415     if(apdu_file && apdu_file != stderr) { /* don't close stdout*/
2416         perror("unable to close apdu log file");      
2417     }
2418     apdu_file=NULL;
2419   
2420     if(strlen(arg)<1) {
2421         return 1;
2422     }
2423   
2424     if(!strcmp(arg,"-")) 
2425         apdu_file=stderr;      
2426     else 
2427         apdu_file=fopen(arg, "a");
2428   
2429     if(!apdu_file) {
2430         perror("unable to open apdu log file no apdu log loaded");
2431     } else {
2432         odr_setprint(print, apdu_file); 
2433     }
2434   
2435     return 1;
2436 }
2437
2438 int cmd_set_cclfile(char* arg)
2439 {  
2440     FILE *inf;
2441
2442     REMOVE_TAILING_BLANKS(arg);
2443
2444     bibset = ccl_qual_mk (); 
2445     inf = fopen (arg, "r");
2446     if (inf)
2447     {
2448         ccl_qual_file (bibset, inf);
2449         fclose (inf);
2450     }
2451     strcpy(ccl_fields,arg);
2452     return 0;
2453 }
2454
2455
2456 int cmd_set_auto_reconnect(char* arg)
2457 {  
2458     REMOVE_TAILING_BLANKS(arg);
2459     
2460     if(strlen(arg)==0) {
2461         auto_reconnect = ! auto_reconnect;
2462     } else if(strcmp(arg,"on")==0) {
2463         auto_reconnect = 1;
2464     } else if(strcmp(arg,"off")==0) {
2465         auto_reconnect = 0;             
2466     } else {
2467         printf("Error use on or off\n");
2468         return 1;
2469     }
2470     
2471     if (auto_reconnect)
2472         printf("Set auto reconnect enabled.\n");
2473     else
2474         printf("Set auto reconnect disabled.\n");
2475     
2476     return 0;
2477 }
2478
2479 int cmd_set_marcdump(char* arg)
2480 {
2481     if(marcdump && marcdump != stderr) { /* don't close stdout*/
2482         perror("unable to close apdu log file");      
2483     }
2484     marcdump=NULL;
2485     
2486     if(strlen(arg)<1) {
2487         return 1;
2488     }
2489     
2490     if(!strcmp(arg,"-")) 
2491         marcdump=stderr;      
2492     else 
2493         marcdump=fopen(arg, "a");
2494     
2495     if(!marcdump) {
2496         perror("unable to open apdu marcdump file no marcdump done\n");
2497     }
2498     
2499     return 1;
2500 }
2501
2502 int cmd_set_proxy(char* arg)
2503 {
2504     if(yazProxy) free(yazProxy);
2505     yazProxy=NULL;
2506     
2507     if(strlen(arg) > 1) {
2508         yazProxy=strdup(arg);
2509     }
2510     return 1;
2511 }
2512
2513 /* 
2514    this command takes 3 arge {name class oid} 
2515 */
2516 int cmd_register_oid(char* args) {
2517     static struct {
2518         char* className;
2519         oid_class oclass;
2520     } oid_classes[] = {
2521         {"appctx",CLASS_APPCTX},
2522         {"absyn",CLASS_ABSYN},
2523         {"attset",CLASS_ATTSET},
2524         {"transyn",CLASS_TRANSYN},
2525         {"diagset",CLASS_DIAGSET},
2526         {"recsyn",CLASS_RECSYN},
2527         {"resform",CLASS_RESFORM},
2528         {"accform",CLASS_ACCFORM},
2529         {"extserv",CLASS_EXTSERV},
2530         {"userinfo",CLASS_USERINFO},
2531         {"elemspec",CLASS_ELEMSPEC},
2532         {"varset",CLASS_VARSET},
2533         {"schema",CLASS_SCHEMA},
2534         {"tagset",CLASS_TAGSET},
2535         {"general",CLASS_GENERAL},
2536         {0,(enum oid_class) 0}
2537     };
2538     char oname_str[101], oclass_str[101], oid_str[101];  
2539     char* name;
2540     int i;
2541     oid_class oidclass = CLASS_GENERAL;
2542     int val = 0, oid[OID_SIZE];
2543     struct oident * new_oident=NULL;
2544     
2545     if (sscanf (args, "%100[^ ] %100[^ ] %100s",
2546                 oname_str,oclass_str, oid_str) < 1) {
2547         printf("Error in regristrate command \n");
2548         return 0;
2549     }
2550     
2551     for (i = 0; oid_classes[i].className; i++) {
2552         if (!strcmp(oid_classes[i].className, oclass_str))
2553         {
2554             oidclass=oid_classes[i].oclass;
2555             break;
2556         }
2557     }
2558     
2559     if(!(oid_classes[i].className)) {
2560         printf("Unknonwn oid class %s\n",oclass_str);
2561         return 0;
2562     }
2563     
2564     i = 0;
2565     name = oid_str;
2566     val = 0;
2567     
2568     while (isdigit (*name))
2569     {
2570         val = val*10 + (*name - '0');
2571         name++;
2572         if (*name == '.')
2573         {
2574             if (i < OID_SIZE-1)
2575                 oid[i++] = val;
2576             val = 0;
2577             name++;
2578         }
2579     }
2580     oid[i] = val;
2581     oid[i+1] = -1;
2582     
2583     new_oident=oid_addent (oid,PROTO_GENERAL,oidclass,oname_str,VAL_DYNAMIC);  
2584     if(strcmp(new_oident->desc,oname_str)) {
2585         fprintf(stderr,"oid is already named as %s, regristration faild\n",
2586                 new_oident->desc);
2587     }
2588     return 1;  
2589 }
2590
2591 int cmd_push_command(char* arg) 
2592 {
2593 #if HAVE_READLINE_HISTORY_H
2594     if(strlen(arg)>1) 
2595         add_history(arg);
2596 #else 
2597     fprintf(stderr,"Not compiled with the readline/history module\n");
2598 #endif
2599     return 1;
2600 }
2601
2602 void source_rcfile() 
2603 {
2604     /*  Look for a $HOME/.yazclientrc and source it if it exists */
2605     struct stat statbuf;
2606     char buffer[1000];
2607     char* homedir=getenv("HOME");
2608     
2609     if(!homedir) return;
2610     
2611     sprintf(buffer,"%s/.yazclientrc",homedir);
2612     
2613     if(stat(buffer,&statbuf)==0) {
2614         cmd_source(buffer);
2615     }
2616     
2617     if(stat(".yazclientrc",&statbuf)==0) {
2618         cmd_source(".yazclientrc");
2619     }
2620 }
2621
2622
2623 static void initialize(void)
2624 {
2625     FILE *inf;
2626     int i;
2627     
2628     if (!(out = odr_createmem(ODR_ENCODE)) ||
2629         !(in = odr_createmem(ODR_DECODE)) ||
2630         !(print = odr_createmem(ODR_PRINT)))
2631     {
2632         fprintf(stderr, "failed to allocate ODR streams\n");
2633         exit(1);
2634     }
2635     oid_init();
2636     
2637     setvbuf(stdout, 0, _IONBF, 0);
2638     if (apdu_file)
2639         odr_setprint(print, apdu_file);
2640
2641     bibset = ccl_qual_mk (); 
2642     inf = fopen (ccl_fields, "r");
2643     if (inf)
2644     {
2645         ccl_qual_file (bibset, inf);
2646         fclose (inf);
2647     }
2648     cmd_base("Default");
2649
2650 #if HAVE_READLINE_READLINE_H
2651     rl_attempted_completion_function = (CPPFunction*)readline_completer;
2652 #endif
2653     
2654     
2655     for(i=0; i<maxOtherInfosSupported; ++i) {
2656         extraOtherInfos[i].oidval = -1;
2657     }
2658     
2659     source_rcfile();
2660 }
2661
2662
2663 #if HAVE_GETTIMEOFDAY
2664 struct timeval tv_start, tv_end;
2665 #endif
2666
2667 void wait_and_handle_responce() 
2668 {
2669     
2670     int res;
2671     char *netbuffer= 0;
2672     int netbufferlen = 0;
2673     Z_APDU *apdu;
2674     
2675     
2676     if (conn)
2677     {
2678         do
2679         {
2680             if ((res = cs_get(conn, &netbuffer, &netbufferlen)) < 0)
2681             {
2682                 printf("Target closed connection\n");
2683                 close_session ();
2684                 break;
2685             }
2686             if (!res)
2687             {
2688                 printf("Target closed connection.\n");
2689                 close_session ();
2690                 break;
2691             }
2692             odr_reset(in); /* release APDU from last round */
2693             record_last = 0;
2694             odr_setbuf(in, netbuffer, res, 0);
2695             if (!z_APDU(in, &apdu, 0, 0))
2696             {
2697                 odr_perror(in, "Decoding incoming APDU");
2698                 fprintf(stderr, "[Near %d]\n", odr_offset(in));
2699                 fprintf(stderr, "Packet dump:\n---------\n");
2700                 odr_dumpBER(stderr, netbuffer, res);
2701                 fprintf(stderr, "---------\n");
2702                 if (apdu_file)
2703                     z_APDU(print, &apdu, 0, 0);
2704                 close_session ();
2705                 break;
2706             }
2707             if (apdu_file && !z_APDU(print, &apdu, 0, 0))
2708             {
2709                 odr_perror(print, "Failed to print incoming APDU");
2710                 odr_reset(print);
2711                 continue;
2712             }
2713             switch(apdu->which)
2714             {
2715             case Z_APDU_initResponse:
2716                 process_initResponse(apdu->u.initResponse);
2717                 break;
2718             case Z_APDU_searchResponse:
2719                 process_searchResponse(apdu->u.searchResponse);
2720                 break;
2721             case Z_APDU_scanResponse:
2722                 process_scanResponse(apdu->u.scanResponse);
2723                 break;
2724             case Z_APDU_presentResponse:
2725                 print_refid (apdu->u.presentResponse->referenceId);
2726                 setno +=
2727                     *apdu->u.presentResponse->numberOfRecordsReturned;
2728                 if (apdu->u.presentResponse->records)
2729                     display_records(apdu->u.presentResponse->records);
2730                 else
2731                     printf("No records.\n");
2732                 printf ("nextResultSetPosition = %d\n",
2733                         *apdu->u.presentResponse->nextResultSetPosition);
2734                 break;
2735             case Z_APDU_sortResponse:
2736                 process_sortResponse(apdu->u.sortResponse);
2737                 break;
2738             case Z_APDU_extendedServicesResponse:
2739                 printf("Got extended services response\n");
2740                 process_ESResponse(apdu->u.extendedServicesResponse);
2741                 break;
2742             case Z_APDU_close:
2743                 printf("Target has closed the association.\n");
2744                 process_close(apdu->u.close);
2745                 break;
2746             case Z_APDU_resourceControlRequest:
2747                 process_resourceControlRequest
2748                     (apdu->u.resourceControlRequest);
2749                 break;
2750             case Z_APDU_deleteResultSetResponse:
2751                 process_deleteResultSetResponse(apdu->u.
2752                                                 deleteResultSetResponse);
2753                 break;
2754             default:
2755                 printf("Received unknown APDU type (%d).\n", 
2756                        apdu->which);
2757                 close_session ();
2758             }
2759         }
2760         while (conn && cs_more(conn));
2761 #if HAVE_GETTIMEOFDAY
2762         gettimeofday (&tv_end, 0);
2763 #if 0
2764         printf ("S/U S/U=%ld/%ld %ld/%ld",
2765                 (long) tv_start.tv_sec,
2766                 (long) tv_start.tv_usec,
2767                 (long) tv_end.tv_sec,
2768                 (long) tv_end.tv_usec);
2769 #endif
2770         printf ("Elapsed: %.6f\n",
2771                 (double) tv_end.tv_usec / 1e6 + tv_end.tv_sec -
2772                 ((double) tv_start.tv_usec / 1e6 + tv_start.tv_sec));
2773 #endif
2774     }
2775     xfree (netbuffer);
2776 }
2777
2778
2779 int cmd_cclparse(char* arg) 
2780 {
2781     int error, pos;
2782     struct ccl_rpn_node *rpn=NULL;
2783     
2784     
2785     rpn = ccl_find_str (bibset, arg, &error, &pos);
2786     
2787     if (error) {
2788         printf ("%*s^ - ", 3+strlen(last_cmd)+1+pos, " ");
2789         printf ("%s\n", ccl_err_msg (error));
2790     }
2791     else
2792     {
2793         if (rpn)
2794         {       
2795             ccl_pr_tree(rpn, stdout); 
2796         }
2797     }
2798     if (rpn)
2799         ccl_rpn_delete(rpn);
2800     
2801     printf ("\n");
2802     
2803     return 0;
2804 }
2805
2806
2807 int cmd_set_otherinfo(char* args)
2808 {
2809     char oid[101], otherinfoString[101];
2810     int otherinfoNo;
2811     int sscan_res;
2812     int oidval;
2813     
2814     sscan_res = sscanf (args, "%d %100[^ ] %100s", &otherinfoNo, oid, otherinfoString);
2815     if(sscan_res==1) {
2816         /* reset this otherinfo */
2817         if(otherinfoNo>=maxOtherInfosSupported) {
2818             printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2819         }
2820         extraOtherInfos[otherinfoNo].oidval = -1;
2821         if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value);                        
2822         return 0;
2823     }
2824     if (sscan_res<3) {
2825         printf("Error in set_otherinfo command \n");
2826         return 0;
2827     }
2828     
2829     if(otherinfoNo>=maxOtherInfosSupported) {
2830         printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2831     }
2832     
2833     
2834     oidval = oid_getvalbyname (oid);
2835     if(oidval == -1 ) {
2836         printf("Error in set_otherinfo command unknown oid %s \n",oid);
2837         return 0;
2838     }
2839     extraOtherInfos[otherinfoNo].oidval = oidval;
2840     if(extraOtherInfos[otherinfoNo].value) free(extraOtherInfos[otherinfoNo].value);
2841     extraOtherInfos[otherinfoNo].value = strdup(otherinfoString);
2842     
2843     return 0;
2844 }
2845
2846 int cmd_list_otherinfo(char* args)
2847 {
2848     int i;         
2849     
2850     if(strlen(args)>0) {
2851         i = atoi(args);
2852         if( i >= maxOtherInfosSupported ) {
2853             printf("Error otherinfo index to large (%d>%d)\n",i,maxOtherInfosSupported);
2854             return 0;
2855         }
2856
2857         if(extraOtherInfos[i].oidval != -1) 
2858             printf("  otherinfo %d %s %s\n",
2859                    i,
2860                    yaz_z3950_oid_value_to_str(
2861                        (enum oid_value) extraOtherInfos[i].oidval,
2862                        CLASS_RECSYN),
2863                    extraOtherInfos[i].value);
2864         
2865     } else {            
2866         for(i=0; i<maxOtherInfosSupported; ++i) {
2867             if(extraOtherInfos[i].oidval != -1) 
2868                 printf("  otherinfo %d %s %s\n",
2869                        i,
2870                        yaz_z3950_oid_value_to_str(
2871                            (enum oid_value) extraOtherInfos[i].oidval,
2872                            CLASS_RECSYN),
2873                        extraOtherInfos[i].value);
2874         }
2875         
2876     }
2877     return 0;
2878 }
2879
2880
2881 int cmd_list_all(char* args) {
2882     int i;
2883     
2884     /* connection options */
2885     if(conn) {
2886         printf("Connected to         : %s\n",last_open_command);
2887     } else {
2888         if(last_open_command) 
2889             printf("Not connected to     : %s\n",last_open_command);
2890         else 
2891             printf("Not connected        : \n");
2892         
2893     }
2894     if(yazProxy) printf("using proxy          : %s\n",yazProxy);                
2895     
2896     printf("auto_reconnect       : %s\n",auto_reconnect?"on":"off");
2897     
2898     if (!auth) {
2899         printf("Authentication       : none\n");
2900     } else {
2901         switch(auth->which) {
2902         case Z_IdAuthentication_idPass:
2903             printf("Authentication       : IdPass\n"); 
2904             printf("    Login User       : %s\n",auth->u.idPass->userId?auth->u.idPass->userId:"");
2905             printf("    Login Group      : %s\n",auth->u.idPass->groupId?auth->u.idPass->groupId:"");
2906             printf("    Password         : %s\n",auth->u.idPass->password?auth->u.idPass->password:"");
2907             break;
2908         case Z_IdAuthentication_open:
2909             printf("Authentication       : psOpen\n");                  
2910             printf("    Open string      : %s\n",auth->u.open); 
2911             break;
2912         default:
2913             printf("Authentication       : Unknown\n");
2914         }
2915     }
2916     if ( yazCharset ) printf("Character set        : `%s'\n", (yazCharset) ? yazCharset:NULL);
2917     
2918     /* bases */
2919     printf("Bases                : ");
2920     for (i = 0; i<num_databaseNames; i++) printf("%s ",databaseNames[i]);
2921     printf("\n");
2922     
2923     /* Query options */
2924     printf("CCL file             : %s\n",ccl_fields);
2925     printf("Query type           : %s\n",query_type_as_string(queryType));
2926     
2927     printf("Named Result Sets    : %s\n",setnumber==-1?"off":"on");
2928     
2929     /* piggy back options */
2930     printf("ssub/lslb/mspn       : %d/%d/%d\n",smallSetUpperBound,largeSetLowerBound,mediumSetPresentNumber);
2931     
2932     /* print present related options */
2933     printf("Format               : %s\n",yaz_z3950_oid_value_to_str(recordsyntax,CLASS_RECSYN));
2934     printf("Schema               : %s\n",yaz_z3950_oid_value_to_str(schema,CLASS_SCHEMA));
2935     printf("Elements             : %s\n",elementSetNames?elementSetNames->u.generic:"");
2936     
2937     /* loging options */
2938     printf("APDU log             : %s\n",apdu_file?"on":"off");
2939     printf("Record log           : %s\n",marcdump?"on":"off");
2940     
2941     /* other infos */
2942     printf("Other Info: \n");
2943     cmd_list_otherinfo("");
2944     
2945     return 0;
2946 }
2947
2948 int cmd_clear_otherinfo(char* args) 
2949 {
2950     if(strlen(args)>0) {
2951         int otherinfoNo;
2952         otherinfoNo = atoi(args);
2953         if( otherinfoNo >= maxOtherInfosSupported ) {
2954             printf("Error otherinfo index to large (%d>%d)\n",otherinfoNo,maxOtherInfosSupported);
2955             return 0;
2956         }
2957         
2958         if(extraOtherInfos[otherinfoNo].oidval != -1) {                 
2959             /* only clear if set. */
2960             extraOtherInfos[otherinfoNo].oidval=-1;
2961             free(extraOtherInfos[otherinfoNo].value);
2962         }
2963     } else {
2964         int i;
2965         
2966         for(i=0; i<maxOtherInfosSupported; ++i) {
2967             if (extraOtherInfos[i].oidval!=-1 ) {                               
2968                 extraOtherInfos[i].oidval=-1;
2969                 free(extraOtherInfos[i].value);
2970             }
2971         }
2972     }
2973     return 0;
2974 }
2975
2976 static int cmd_help (char *line);
2977
2978 typedef char *(*completerFunctionType)(const char *text, int state);
2979
2980 static struct {
2981     char *cmd;
2982     int (*fun)(char *arg);
2983     char *ad;
2984         completerFunctionType rl_completerfunction;
2985     int complete_filenames;
2986     char **local_tabcompletes;
2987 } cmd[] = {
2988     {"open", cmd_open, "('tcp'|'ssl')':<host>[':'<port>][/<db>]",NULL,0,NULL},
2989     {"quit", cmd_quit, "",NULL,0,NULL},
2990     {"find", cmd_find, "<query>",NULL,0,NULL},
2991     {"delete", cmd_delete, "<setname>",NULL,0,NULL},
2992     {"base", cmd_base, "<base-name>",NULL,0,NULL},
2993     {"show", cmd_show, "<rec#>['+'<#recs>['+'<setname>]]",NULL,0,NULL},
2994     {"scan", cmd_scan, "<term>",NULL,0,NULL},
2995     {"sort", cmd_sort, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
2996     {"sort+", cmd_sort_newset, "<sortkey> <flag> <sortkey> <flag> ...",NULL,0,NULL},
2997     {"authentication", cmd_authentication, "<acctstring>",NULL,0,NULL},
2998     {"lslb", cmd_lslb, "<largeSetLowerBound>",NULL,0,NULL},
2999     {"ssub", cmd_ssub, "<smallSetUpperBound>",NULL,0,NULL},
3000     {"mspn", cmd_mspn, "<mediumSetPresentNumber>",NULL,0,NULL},
3001     {"status", cmd_status, "",NULL,0,NULL},
3002     {"setnames", cmd_setnames, "",NULL,0,NULL},
3003     {"cancel", cmd_cancel, "",NULL,0,NULL},
3004     {"format", cmd_format, "<recordsyntax>",complete_format,0,NULL},
3005     {"schema", cmd_schema, "<schema>",complete_schema,0,NULL},
3006     {"elements", cmd_elements, "<elementSetName>",NULL,0,NULL},
3007     {"close", cmd_close, "",NULL,0,NULL},
3008     {"attributeset", cmd_attributeset, "<attrset>",complete_attributeset,0,NULL},
3009     {"querytype", cmd_querytype, "<type>",complete_querytype,0,NULL},
3010     {"refid", cmd_refid, "<id>",NULL,0,NULL},
3011     {"itemorder", cmd_itemorder, "ill|item <itemno>",NULL,0,NULL},
3012     {"update", cmd_update, "<action> <recid> [<file>]",NULL,0,NULL},
3013     {"packagename", cmd_packagename, "<packagename>",NULL,0,NULL},
3014     {"proxy", cmd_proxy, "[('tcp'|'ssl')]<host>[':'<port>]",NULL,0,NULL},
3015     {"charset", cmd_charset, "<charset_name>",NULL,0,NULL},
3016     {"lang", cmd_lang, "<language_code>",NULL,0,NULL},
3017     {".", cmd_source, "<filename>",NULL,1,NULL},
3018     {"!", cmd_subshell, "Subshell command",NULL,1,NULL},
3019     {"set_apdufile", cmd_set_apdufile, "<filename>",NULL,1,NULL},
3020     {"set_marcdump", cmd_set_marcdump," <filename>",NULL,1,NULL},
3021     {"set_cclfile", cmd_set_cclfile," <filename>",NULL,1,NULL},
3022     {"set_auto_reconnect", cmd_set_auto_reconnect," on|off",complete_auto_reconnect,1,NULL},
3023         {"set_otherinfo", cmd_set_otherinfo,"<otherinfoinddex> <oid> <string>",NULL,0,NULL},
3024     {"register_oid", cmd_register_oid,"<name> <class> <oid>",NULL,0,NULL},
3025     {"push_command", cmd_push_command,"<command>",command_generator,0,NULL},
3026         {"register_tab", cmd_register_tab,"<commandname> <tab>",command_generator,0,NULL},
3027         {"cclparse", cmd_cclparse,"<ccl find command>",NULL,0,NULL},
3028         {"list_otherinfo",cmd_list_otherinfo,"[otherinfoinddex]",NULL,0,NULL},
3029         {"list_all",cmd_list_all,"",NULL,0,NULL},
3030         {"clear_otherinfo",cmd_clear_otherinfo,"",NULL,0,NULL},
3031     /* Server Admin Functions */
3032     {"adm-reindex", cmd_adm_reindex, "<database-name>",NULL,0,NULL},
3033     {"adm-truncate", cmd_adm_truncate, "('database'|'index')<object-name>",NULL,0,NULL},
3034     {"adm-create", cmd_adm_create, "",NULL,0,NULL},
3035     {"adm-drop", cmd_adm_drop, "('database'|'index')<object-name>",NULL,0,NULL},
3036     {"adm-import", cmd_adm_import, "<record-type> <dir> <pattern>",NULL,0,NULL},
3037     {"adm-refresh", cmd_adm_refresh, "",NULL,0,NULL},
3038     {"adm-commit", cmd_adm_commit, "",NULL,0,NULL},
3039     {"adm-shutdown", cmd_adm_shutdown, "",NULL,0,NULL},
3040     {"adm-startup", cmd_adm_startup, "",NULL,0,NULL},
3041     {"help", cmd_help, "", NULL,0,NULL},
3042     {0,0,0,0,0,0}
3043 };
3044
3045 static int cmd_help (char *line)
3046 {
3047     int i;
3048     char topic[21];
3049     
3050     *topic = 0;
3051     sscanf (line, "%20s", topic);
3052
3053     if (*topic == 0)
3054         printf("Commands:\n");
3055     for (i = 0; cmd[i].cmd; i++)
3056         if (*topic == 0 || strcmp (topic, cmd[i].cmd) == 0)
3057             printf("   %s %s\n", cmd[i].cmd, cmd[i].ad);
3058     if (strcmp (topic, "find") == 0)
3059     {
3060         printf ("RPN:\n");
3061         printf (" \"term\"                        Simple Term\n");
3062         printf (" @attr [attset] type=value op  Attribute\n");
3063         printf (" @and opl opr                  And\n");
3064         printf (" @or opl opr                   Or\n");
3065         printf (" @not opl opr                  And-Not\n");
3066         printf (" @set set                      Result set\n");
3067         printf ("\n");
3068         printf ("Bib-1 attribute types\n");
3069         printf ("1=Use:         ");
3070         printf ("4=Title 7=ISBN 8=ISSN 30=Date 62=Abstract 1003=Author 1016=Any\n");
3071         printf ("2=Relation:    ");
3072         printf ("1<   2<=  3=  4>=  5>  6!=  102=Relevance\n");
3073         printf ("3=Position:    ");
3074         printf ("1=First in Field  2=First in subfield  3=Any position\n");
3075         printf ("4=Structure:   ");
3076         printf ("1=Phrase  2=Word  3=Key  4=Year  5=Date  6=WordList\n");
3077         printf ("5=Truncation:  ");
3078         printf ("1=Right  2=Left  3=L&R  100=No  101=#  102=Re-1  103=Re-2\n");
3079         printf ("6=Completeness:");
3080         printf ("1=Incomplete subfield  2=Complete subfield  3=Complete field\n");
3081     }
3082     return 1;
3083 }
3084
3085 int cmd_register_tab(char* arg) {
3086         
3087     char command[101], tabargument[101];
3088     int i;
3089     int num_of_tabs;
3090     char** tabslist;
3091     
3092     if (sscanf (arg, "%100s %100s", command, tabargument) < 1) {
3093         return 0;
3094     }
3095     
3096     /* locate the amdn in the list */
3097     for (i = 0; cmd[i].cmd; i++) {
3098         if (!strncmp(cmd[i].cmd, command, strlen(command))) {
3099             break;
3100         }
3101     }
3102     
3103     if(!cmd[i].cmd) { 
3104         fprintf(stderr,"Unknown command %s\n",command);
3105         return 1;
3106     }
3107     
3108         
3109     if(!cmd[i].local_tabcompletes)
3110         cmd[i].local_tabcompletes = (char **) calloc(1,sizeof(char**));
3111     
3112     num_of_tabs=0;              
3113     
3114     tabslist = cmd[i].local_tabcompletes;
3115     for(;tabslist && *tabslist;tabslist++) {
3116         num_of_tabs++;
3117     }
3118     
3119     cmd[i].local_tabcompletes =  (char **)
3120         realloc(cmd[i].local_tabcompletes,(num_of_tabs+2)*sizeof(char**));
3121     tabslist=cmd[i].local_tabcompletes;
3122     tabslist[num_of_tabs]=strdup(tabargument);
3123     tabslist[num_of_tabs+1]=NULL;
3124     return 1;
3125 }
3126
3127
3128 void process_cmd_line(char* line)
3129 {  
3130     int i,res;
3131     char word[32], arg[1024];
3132     
3133 #if HAVE_GETTIMEOFDAY
3134     gettimeofday (&tv_start, 0);
3135 #endif
3136     
3137     if ((res = sscanf(line, "%31s %1023[^;]", word, arg)) <= 0)
3138     {
3139         strcpy(word, last_cmd);
3140         *arg = '\0';
3141     }
3142     else if (res == 1)
3143         *arg = 0;
3144     strcpy(last_cmd, word);
3145     
3146     /* removed tailing spaces from the arg command */
3147     { 
3148         char* p;
3149         char* lastnonspace=NULL;
3150         p = arg;
3151         
3152         for(;*p; ++p) {
3153             if(!isspace(*p)) {
3154                 lastnonspace = p;
3155             }
3156         }
3157         if(lastnonspace) 
3158             *(++lastnonspace) = 0;
3159     }
3160     
3161     
3162     for (i = 0; cmd[i].cmd; i++)
3163         if (!strncmp(cmd[i].cmd, word, strlen(word)))
3164         {
3165             res = (*cmd[i].fun)(arg);
3166             break;
3167         }
3168     
3169     if (!cmd[i].cmd) /* dump our help-screen */
3170     {
3171         printf("Unknown command: %s.\n", word);
3172         printf("use help for list of commands\n");
3173         /* cmd_help (""); */
3174         res = 1;
3175     }
3176     
3177     if(apdu_file) fflush(apdu_file);
3178     
3179     if (res >= 2)
3180         wait_and_handle_responce();
3181     
3182     if(apdu_file)
3183         fflush(apdu_file);
3184     if(marcdump)
3185         fflush(marcdump);
3186 }
3187
3188
3189 char *command_generator(const char *text, int state) 
3190 {
3191     static int idx; 
3192     if (state==0) {
3193         idx = 0;
3194     }
3195     for( ; cmd[idx].cmd; ++idx) {
3196         if (!strncmp(cmd[idx].cmd,text,strlen(text))) {
3197             ++idx;  /* skip this entry on the next run */
3198             return strdup(cmd[idx-1].cmd);
3199         }
3200     }
3201     return NULL;
3202 }
3203
3204
3205 /* 
3206    This function only known how to complete on the first word
3207 */
3208 char ** readline_completer(char *text, int start, int end) {
3209 #if HAVE_READLINE_READLINE_H
3210
3211         completerFunctionType completerToUse;
3212         
3213     if(start == 0) {
3214 #if HAVE_READLINE_RL_COMPLETION_MATCHES
3215         char** res=rl_completion_matches(text,
3216                                       command_generator); 
3217 #else
3218         char** res=completion_matches(text,
3219                                       (CPFunction*)command_generator); 
3220 #endif
3221         rl_attempted_completion_over = 1;
3222         return res;
3223     } else {
3224         char arg[1024],word[32];
3225         int i=0 ,res;
3226         if ((res = sscanf(rl_line_buffer, "%31s %1023[^;]", word, arg)) <= 0) {     
3227             rl_attempted_completion_over = 1;
3228             return NULL;
3229         }
3230         
3231         for (i = 0; cmd[i].cmd; i++) {
3232             if (!strncmp(cmd[i].cmd, word, strlen(word))) {
3233                 break;
3234             }
3235         }
3236         
3237         if(!cmd[i].cmd) return NULL;
3238         
3239         
3240         curret_global_list = cmd[i].local_tabcompletes;
3241         
3242         completerToUse = cmd[i].rl_completerfunction;
3243         if(completerToUse==NULL)  /* if no pr. command completer is defined use the default completer */
3244             completerToUse = default_completer;
3245         
3246         if(completerToUse) {
3247 #ifdef HAVE_READLINE_RL_COMPLETION_MATCHES
3248             char** res=
3249                 rl_completion_matches(text,
3250                                       completerToUse);
3251 #else
3252             char** res=
3253                 completion_matches(text,
3254                                    (CPFunction*)completerToUse);
3255 #endif
3256             if(!cmd[i].complete_filenames) 
3257                 rl_attempted_completion_over = 1;
3258             return res;
3259         } else {
3260             if(!cmd[i].complete_filenames) 
3261                 rl_attempted_completion_over = 1;
3262             return 0;
3263         }
3264     }
3265 #else 
3266     return 0;
3267 #endif 
3268 }
3269
3270
3271 static void client(void)
3272 {
3273     char line[1024];
3274
3275     line[1023] = '\0';
3276
3277 #if HAVE_GETTIMEOFDAY
3278     gettimeofday (&tv_start, 0);
3279 #endif
3280
3281     while (1)
3282     {
3283         char *line_in = NULL;
3284 #if HAVE_READLINE_READLINE_H
3285         if (isatty(0))
3286         {
3287             line_in=readline(C_PROMPT);
3288             if (!line_in)
3289                 break;
3290 #if HAVE_READLINE_HISTORY_H
3291             if (*line_in)
3292                 add_history(line_in);
3293 #endif
3294             strncpy(line, line_in, 1023);
3295             free (line_in);
3296         }
3297 #endif 
3298         if (!line_in)
3299         {
3300             char *end_p;
3301             printf (C_PROMPT);
3302             fflush(stdout);
3303             if (!fgets(line, 1023, stdin))
3304                 break;
3305             if ((end_p = strchr (line, '\n')))
3306                 *end_p = '\0';
3307         }
3308         process_cmd_line(line);
3309     }
3310 }
3311
3312 int main(int argc, char **argv)
3313 {
3314     char *prog = *argv;
3315     char *open_command = 0;
3316     char *auth_command = 0;
3317     char *arg;
3318     int ret;
3319     
3320 #if HAVE_LOCALE_H
3321     if (!setlocale(LC_CTYPE, ""))
3322         fprintf (stderr, "setlocale failed\n");
3323 #endif
3324 #if HAVE_LANGINFO_H
3325     codeset = nl_langinfo(CODESET);
3326 #endif
3327
3328     while ((ret = options("k:c:a:m:v:p:u:t:", argv, argc, &arg)) != -2)
3329     {
3330         switch (ret)
3331         {
3332         case 0:
3333             if (!open_command)
3334             {
3335                 open_command = (char *) xmalloc (strlen(arg)+6);
3336                 strcpy (open_command, "open ");
3337                 strcat (open_command, arg);
3338             }
3339             break;
3340         case 'k':
3341             kilobytes = atoi(arg);
3342             break;
3343         case 'm':
3344             if (!(marcdump = fopen (arg, "a")))
3345             {
3346                 perror (arg);
3347                 exit (1);
3348             }
3349             break;
3350         case 't':
3351             codeset = arg;
3352             break;
3353         case 'c':
3354             strncpy (ccl_fields, arg, sizeof(ccl_fields)-1);
3355             ccl_fields[sizeof(ccl_fields)-1] = '\0';
3356             break;
3357         case 'a':
3358             if (!strcmp(arg, "-"))
3359                 apdu_file=stderr;
3360             else
3361                 apdu_file=fopen(arg, "a");
3362             break;
3363         case 'p':
3364             yazProxy=strdup(arg);
3365             break;
3366         case 'u':
3367             if (!auth_command)
3368             {
3369                 auth_command = (char *) xmalloc (strlen(arg)+6);
3370                 strcpy (auth_command, "auth ");
3371                 strcat (auth_command, arg);
3372             }
3373             break;
3374         case 'v':
3375             yaz_log_init (yaz_log_mask_str(arg), "", NULL);
3376             break;
3377         default:
3378             fprintf (stderr, "Usage: %s [-m <marclog>] [ -a <apdulog>] "
3379                      "[-c cclfields]\n      [-p <proxy-addr>] [-u <auth>] "
3380                      "[-k size] [<server-addr>]\n",
3381                      prog);
3382             exit (1);
3383         }      
3384     }
3385     initialize();
3386     if (auth_command)
3387     {
3388 #ifdef HAVE_GETTIMEOFDAY
3389         gettimeofday (&tv_start, 0);
3390 #endif
3391         process_cmd_line (auth_command);
3392 #if HAVE_READLINE_HISTORY_H
3393         add_history(auth_command);
3394 #endif
3395         xfree(auth_command);
3396     }
3397     if (open_command)
3398     {
3399 #ifdef HAVE_GETTIMEOFDAY
3400         gettimeofday (&tv_start, 0);
3401 #endif
3402         process_cmd_line (open_command);
3403 #if HAVE_READLINE_HISTORY_H
3404         add_history(open_command);
3405 #endif
3406         xfree(open_command);
3407     }
3408     client ();
3409     exit (0);
3410 }
3411
3412 /*
3413  * Local variables:
3414  * tab-width: 8
3415  * c-basic-offset: 4
3416  * End:
3417  * vim600: sw=4 ts=8 fdm=marker
3418  * vim<600: sw=4 ts=8
3419  */