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