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