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