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