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