607a6ff1de4050815988eb5cdce3a8903c176a4b
[yaz-moved-to-github.git] / zoom / zoomsh.c
1 /* This file is part of the YAZ toolkit.
2  * Copyright (C) Index Data
3  * See the file LICENSE for details.
4  */
5 /** \file zoomsh.c
6     \brief ZOOM C command line tool (shell)
7 */
8 #if HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <yaz/wrbuf.h>
16 #include <yaz/log.h>
17 #include <yaz/options.h>
18
19 #if HAVE_READLINE_READLINE_H
20 #include <readline/readline.h>
21 #endif
22 #if HAVE_READLINE_HISTORY_H
23 #include <readline/history.h>
24 #endif
25
26 #include <yaz/log.h>
27 #include <yaz/zoom.h>
28
29 #define MAX_CON 100
30
31 static void process_events(ZOOM_connection *c)
32 {
33     int i;
34
35     yaz_log(YLOG_DEBUG, "process_events");
36     while ((i = ZOOM_event(MAX_CON, c)) != 0)
37     {
38         int peek = ZOOM_connection_peek_event(c[i-1]);
39         int event = ZOOM_connection_last_event(c[i-1]);
40         yaz_log(YLOG_DEBUG, "no = %d peek = %d event = %d %s", i-1,
41                 peek,
42                 event,
43                 ZOOM_get_event_str(event));
44     }
45 }
46
47 static int next_token_chars(const char **cpp, const char **t_start,
48                             const char *tok_chars)
49 {
50     int len = 0;
51     const char *cp = *cpp;
52     while (*cp == ' ')
53         cp++;
54     if (*cp == '"')
55     {
56         cp++;
57         *t_start = cp;
58         while (*cp && *cp != '"')
59         {
60             cp++;
61             len++;
62         }
63         if (*cp)
64             cp++;
65     }
66     else
67     {
68         *t_start = cp;
69         while (*cp && !strchr(tok_chars, *cp))
70         {
71             cp++;
72             len++;
73         }
74         if (len == 0)
75             len = -1;
76     }
77     *cpp = cp;
78     return len;  /* return -1 if no token was read .. */
79 }
80
81 static int next_token(const char **cpp, const char **t_start)
82 {
83     return next_token_chars(cpp, t_start, "\r\n ");
84 }
85
86
87 static WRBUF next_token_new_wrbuf(const char **cpp)
88 {
89     WRBUF w = 0;
90     const char *start;
91     int len = next_token(cpp, &start);
92     if (len < 0)
93         return 0;
94     w = wrbuf_alloc();
95     if (len > 0)
96         wrbuf_write(w, start, len);
97     return w;
98 }
99
100 static int is_command(const char *cmd_str, const char *this_str, int this_len)
101 {
102     int cmd_len = strlen(cmd_str);
103     if (cmd_len != this_len)
104         return 0;
105     if (memcmp(cmd_str, this_str, cmd_len))
106         return 0;
107     return 1;
108 }
109
110 static int cmd_set(ZOOM_connection *c, ZOOM_resultset *r,
111                    ZOOM_options options,
112                    const char **args)
113 {
114     WRBUF key;
115     const char *val_buf;
116     int val_len;
117
118     if (!(key = next_token_new_wrbuf(args)))
119     {
120         printf("missing argument for set\n");
121         return 1;
122     }
123     val_len = next_token_chars(args, &val_buf, "");
124     if (val_len != -1)
125         ZOOM_options_setl(options, wrbuf_cstr(key), val_buf, val_len);
126     else
127         ZOOM_options_set(options, wrbuf_cstr(key), 0);
128     wrbuf_destroy(key);
129     return 0;
130 }
131
132 static int cmd_get(ZOOM_connection *c, ZOOM_resultset *r,
133                    ZOOM_options options,
134                    const char **args)
135 {
136     WRBUF key;
137     if (!(key = next_token_new_wrbuf(args)))
138     {
139         printf("missing argument for get\n");
140         return 1;
141     }
142     else
143     {
144         const char *val = ZOOM_options_get(options, wrbuf_cstr(key));
145         printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
146         wrbuf_destroy(key);
147     }
148     return 0;
149 }
150
151 static int cmd_shell(ZOOM_connection *c, ZOOM_resultset *r,
152                      ZOOM_options options, const char **args)
153 {
154     int ret = system(*args);
155     if (ret)
156          printf("system command returned %d\n", ret);
157     return 0;
158 }
159
160 static int cmd_rget(ZOOM_connection *c, ZOOM_resultset *r,
161                     ZOOM_options options,
162                      const char **args)
163 {
164     WRBUF key;
165     if (!(key = next_token_new_wrbuf(args)))
166     {
167         printf("missing argument for get\n");
168         return 1;
169     }
170     else
171     {
172         int i;
173         for (i = 0; i<MAX_CON; i++)
174         {
175             const char *val;
176             if (!r[i])
177                 continue;
178
179             val = ZOOM_resultset_option_get(r[i], wrbuf_cstr(key));
180             printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
181         }
182         wrbuf_destroy(key);
183     }
184     return 0;
185 }
186
187 static int cmd_close(ZOOM_connection *c, ZOOM_resultset *r,
188                      ZOOM_options options,
189                      const char **args)
190 {
191     WRBUF host;
192     int i;
193     host = next_token_new_wrbuf(args);
194     for (i = 0; i<MAX_CON; i++)
195     {
196         const char *h;
197         if (!c[i])
198             continue;
199         if (!host)
200         {
201             ZOOM_connection_destroy(c[i]);
202             c[i] = 0;
203         }
204         else if ((h = ZOOM_connection_option_get(c[i], "host"))
205                  && !strcmp(h, wrbuf_cstr(host)))
206         {
207             ZOOM_connection_destroy(c[i]);
208             c[i] = 0;
209         }
210     }
211     if (host)
212         wrbuf_destroy(host);
213     return 0;
214 }
215
216 static void display_records(ZOOM_connection c,
217                             ZOOM_resultset r,
218                             size_t start, size_t count, const char *type)
219 {
220     size_t i;
221     for (i = 0; i < count; i++)
222     {
223         size_t pos = i + start;
224         ZOOM_record rec = ZOOM_resultset_record(r, pos);
225         const char *db = ZOOM_record_get(rec, "database", 0);
226
227         if (ZOOM_record_error(rec, 0, 0, 0))
228         {
229             const char *msg;
230             const char *addinfo;
231             const char *diagset;
232             int error = ZOOM_record_error(rec, &msg, &addinfo, &diagset);
233
234             printf("%lld %s: %s (%s:%d) %s\n", (long long) pos,
235                    (db ? db : "unknown"),
236                    msg, diagset, error, addinfo ? addinfo : "none");
237         }
238         else
239         {
240             int len;
241             const char *render = ZOOM_record_get(rec, type, &len);
242             const char *syntax = ZOOM_record_get(rec, "syntax", 0);
243             const char *schema = ZOOM_record_get(rec, "schema", 0);
244             /* if rec is non-null, we got a record for display */
245             if (rec)
246             {
247                 printf("%lld database=%s syntax=%s schema=%s\n",
248                        (long long) pos, (db ? db : "unknown"), syntax,
249                        schema ? schema : "unknown");
250                 if (render)
251                 {
252                     if (fwrite(render, 1, len, stdout) != (size_t) len)
253                     {
254                         printf("write to stdout failed\n");
255                     }
256                 }
257                 printf("\n");
258             }
259         }
260     }
261 }
262
263 static int cmd_show(ZOOM_connection *c, ZOOM_resultset *r,
264                     ZOOM_options options,
265                     const char **args)
266 {
267     int i;
268     size_t start = 0, count = 1;
269     const char *type = "render";
270     WRBUF render_str = 0;
271     int ret = 0;
272
273     {
274         WRBUF tmp;
275
276         if ((tmp = next_token_new_wrbuf(args)))
277         {
278             start = atoi(wrbuf_cstr(tmp));
279             wrbuf_destroy(tmp);
280         }
281
282         if ((tmp = next_token_new_wrbuf(args)))
283         {
284             count = atoi(wrbuf_cstr(tmp));
285             wrbuf_destroy(tmp);
286         }
287         render_str = next_token_new_wrbuf(args);
288     }
289     if (render_str)
290         type = wrbuf_cstr(render_str);
291
292     for (i = 0; i < MAX_CON; i++)
293         ZOOM_resultset_records(r[i], 0, start, count);
294     process_events(c);
295
296     for (i = 0; i < MAX_CON; i++)
297     {
298         int error;
299         const char *errmsg, *addinfo, *dset;
300         /* display errors if any */
301         if (!c[i])
302             continue;
303         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
304         {
305             printf("%s error: %s (%s:%d) %s\n",
306                    ZOOM_connection_option_get(c[i], "host"), errmsg,
307                    dset, error, addinfo);
308             ret = 1;
309         }
310         else if (r[i])
311         {
312             /* OK, no major errors. Display records... */
313             display_records(c[i], r[i], start, count, type);
314         }
315     }
316     if (render_str)
317         wrbuf_destroy(render_str);
318     return ret;
319 }
320
321 static void display_facets(ZOOM_facet_field *facets, int count) {
322     int index;
323     printf("Facets: \n");
324     for (index = 0; index <  count; index++) {
325         int term_index;
326         const char *facet_name = ZOOM_facet_field_name(facets[index]);
327         printf("  %s: \n", facet_name);
328         for (term_index = 0; term_index < ZOOM_facet_field_term_count(facets[index]); term_index++) {
329             int freq = 0;
330             const char *term = ZOOM_facet_field_get_term(facets[index], term_index, &freq);
331             printf("    %s(%d) \n", term,  freq);
332         }
333     }
334 }
335
336 static int cmd_facets(ZOOM_connection *c, ZOOM_resultset *r,
337                       ZOOM_options options,
338                       const char **args)
339 {
340     int i;
341     int ret = 0;
342
343     process_events(c);
344
345     for (i = 0; i < MAX_CON; i++)
346     {
347         int error;
348         const char *errmsg, *addinfo, *dset;
349         /* display errors if any */
350         if (!c[i])
351             continue;
352         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
353         {
354             printf("%s error: %s (%s:%d) %s\n",
355                    ZOOM_connection_option_get(c[i], "host"), errmsg,
356                    dset, error, addinfo);
357             ret = 1;
358         }
359         else if (r[i])
360         {
361             int num_facets = ZOOM_resultset_facets_size(r[i]);
362             if (num_facets) {
363                 ZOOM_facet_field  *facets = ZOOM_resultset_facets(r[i]);
364                 display_facets(facets, num_facets);
365             }
366         }
367     }
368     return ret;
369 }
370
371 static int cmd_suggestions(ZOOM_connection *c, ZOOM_resultset *r, ZOOM_options options, const char **args)
372 {
373     int i;
374     int ret = 0;
375
376     process_events(c);
377
378     for (i = 0; i < MAX_CON; i++)
379     {
380         int error;
381         const char *errmsg, *addinfo, *dset;
382         /* display errors if any */
383         if (!c[i])
384             continue;
385         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
386         {
387             printf("%s error: %s (%s:%d) %s\n",
388                    ZOOM_connection_option_get(c[i], "host"), errmsg,
389                    dset, error, addinfo);
390             ret = 1;
391         }
392         else if (r[i])
393         {
394             const char *suggestions = ZOOM_resultset_option_get(r[i], "suggestions");
395             if (suggestions) {
396                 printf("Suggestions: \n%s\n", suggestions);
397             }
398         }
399     }
400     return ret;
401 }
402
403
404 static int cmd_ext(ZOOM_connection *c, ZOOM_resultset *r,
405                    ZOOM_options options,
406                    const char **args)
407 {
408     ZOOM_package p[MAX_CON];
409     int i;
410     int ret = 0;
411     WRBUF ext_type_str = next_token_new_wrbuf(args);
412
413     if (!ext_type_str)
414     {
415         printf("es: missing type "
416                "(itemorder, create, drop, commit, update, xmlupdate)\n");
417         return 1;
418     }
419     for (i = 0; i<MAX_CON; i++)
420     {
421         if (c[i])
422         {
423             p[i] = ZOOM_connection_package(c[i], 0);
424             ZOOM_package_send(p[i], ext_type_str ? wrbuf_cstr(ext_type_str):0);
425         }
426         else
427             p[i] = 0;
428     }
429
430     process_events(c);
431
432     for (i = 0; i<MAX_CON; i++)
433     {
434         int error;
435         const char *errmsg, *addinfo, *dset;
436         /* display errors if any */
437         if (!p[i])
438             continue;
439         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
440         {
441             printf("%s error: %s (%s:%d) %s\n",
442                    ZOOM_connection_option_get(c[i], "host"), errmsg,
443                    dset, error, addinfo);
444             ret = 1;
445         }
446         else if (p[i])
447         {
448             const char *v;
449             printf("ok\n");
450             v = ZOOM_package_option_get(p[i], "targetReference");
451             if (v)
452                 printf("targetReference: %s\n", v);
453             v = ZOOM_package_option_get(p[i], "xmlUpdateDoc");
454             if (v)
455                 printf("xmlUpdateDoc: %s\n", v);
456         }
457         ZOOM_package_destroy(p[i]);
458     }
459     if (ext_type_str)
460         wrbuf_destroy(ext_type_str);
461     return ret;
462 }
463
464 static int cmd_debug(ZOOM_connection *c, ZOOM_resultset *r,
465                      ZOOM_options options,
466                      const char **args)
467 {
468     yaz_log_init_level(YLOG_ALL);
469     return 0;
470 }
471
472 static int cmd_search(ZOOM_connection *c, ZOOM_resultset *r,
473                       ZOOM_options options,
474                       const char **args)
475 {
476     ZOOM_query s;
477     const char *query_str = *args;
478     int i;
479     int ret = 0;
480
481     s = ZOOM_query_create();
482     while (*query_str == ' ')
483         query_str++;
484     if (memcmp(query_str, "cql:", 4) == 0)
485     {
486         ZOOM_query_cql(s, query_str + 4);
487     }
488     else if (ZOOM_query_prefix(s, query_str))
489     {
490         printf("Bad PQF: %s\n", query_str);
491         ZOOM_query_destroy(s);
492         return 1;
493     }
494     for (i = 0; i<MAX_CON; i++)
495     {
496
497         if (c[i])
498         {
499             ZOOM_resultset_destroy(r[i]);
500             r[i] = 0;
501         }
502         if (c[i])
503             r[i] = ZOOM_connection_search(c[i], s);
504     }
505     ZOOM_query_destroy(s);
506
507     process_events(c);
508
509     for (i = 0; i<MAX_CON; i++)
510     {
511         int error;
512         const char *errmsg, *addinfo, *dset;
513         /* display errors if any */
514         if (!c[i])
515             continue;
516         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
517         {
518             printf("%s error: %s (%s:%d) %s\n",
519                    ZOOM_connection_option_get(c[i], "host"), errmsg,
520                    dset, error, addinfo);
521             ret = 1;
522         }
523         else if (r[i])
524         {
525             /* OK, no major errors. Look at the result count */
526             int start = ZOOM_options_get_int(options, "start", 0);
527             int count = ZOOM_options_get_int(options, "count", 0);
528             int facet_num;
529
530             printf("%s: %lld hits\n", ZOOM_connection_option_get(c[i], "host"),
531                    (long long int) ZOOM_resultset_size(r[i]));
532
533             facet_num = ZOOM_resultset_facets_size(r[i]);
534             if (facet_num)
535             {
536                 ZOOM_facet_field *facets = ZOOM_resultset_facets(r[i]);
537                 int facet_idx;
538                 for (facet_idx = 0; facet_idx < facet_num; facet_idx++)
539                 {
540                     const char *name = ZOOM_facet_field_name(facets[facet_idx]);
541                     size_t term_idx;
542                     size_t term_num = ZOOM_facet_field_term_count(facets[facet_idx]);
543                     printf("facet: %s\n", name);
544                     for (term_idx = 0; term_idx < term_num; term_idx++ )
545                     {
546                         int freq;
547                         const char *term =
548                             ZOOM_facet_field_get_term(facets[facet_idx], term_idx, &freq);
549                         printf("term: %s %d\n", term, freq);
550                     }
551                 }
552             }
553             /* and display */
554             display_records(c[i], r[i], start, count, "render");
555         }
556     }
557     return ret;
558 }
559
560 static int cmd_scan(ZOOM_connection *c, ZOOM_resultset *r,
561                     ZOOM_options options,
562                     const char **args)
563 {
564     const char *query_str = *args;
565     ZOOM_query query = ZOOM_query_create();
566     int i;
567     int ret = 0;
568     ZOOM_scanset s[MAX_CON];
569
570     while (*query_str == ' ')
571         query_str++;
572
573     if (memcmp(query_str, "cql:", 4) == 0)
574     {
575         ZOOM_query_cql(query, query_str + 4);
576     }
577     else if (ZOOM_query_prefix(query, query_str))
578     {
579         printf("Bad PQF: %s\n", query_str);
580         ZOOM_query_destroy(query);
581         return 1;
582     }
583
584     for (i = 0; i<MAX_CON; i++)
585     {
586         if (c[i])
587             s[i] = ZOOM_connection_scan1(c[i], query);
588         else
589             s[i] = 0;
590     }
591     ZOOM_query_destroy(query);
592
593     process_events(c);
594
595     for (i = 0; i<MAX_CON; i++)
596     {
597         int error;
598         const char *errmsg, *addinfo, *dset;
599         /* display errors if any */
600         if (!c[i])
601             continue;
602         if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
603         {
604             printf("%s error: %s (%s:%d) %s\n",
605                    ZOOM_connection_option_get(c[i], "host"), errmsg,
606                    dset, error, addinfo);
607             ret = 1;
608         }
609         if (s[i])
610         {
611             size_t p, sz = ZOOM_scanset_size(s[i]);
612             for (p = 0; p < sz; p++)
613             {
614                 size_t occ = 0;
615                 size_t len = 0;
616                 const char *term = ZOOM_scanset_display_term(s[i], p,
617                                                              &occ, &len);
618                 printf("%.*s %lld\n", (int) len, term, (long long int) occ);
619             }
620             ZOOM_scanset_destroy(s[i]);
621         }
622     }
623     return ret;
624 }
625
626 static int cmd_sort(ZOOM_connection *c, ZOOM_resultset *r,
627                     ZOOM_options options,
628                     const char **args)
629 {
630     const char *sort_spec = *args;
631     int i;
632     int ret = 0;
633
634     while (*sort_spec == ' ')
635         sort_spec++;
636
637     for (i = 0; i<MAX_CON; i++)
638     {
639         if (r[i])
640             ZOOM_resultset_sort(r[i], "yaz", sort_spec);
641     }
642     process_events(c);
643     return ret;
644 }
645
646 static int cmd_help(ZOOM_connection *c, ZOOM_resultset *r,
647                     ZOOM_options options,
648                     const char **args)
649 {
650     printf("connect <zurl>\n");
651     printf("search <pqf>\n");
652     printf("show [<start> [<count> [<type]]]\n");
653     printf("facets\n");
654     printf("scan <term>\n");
655     printf("quit\n");
656     printf("close <zurl>\n");
657     printf("ext <type>\n");
658     printf("set <option> [<value>]\n");
659     printf("get <option>\n");
660     printf("shell cmdline\n");
661     printf("\n");
662     printf("options:\n");
663     printf(" start\n");
664     printf(" count\n");
665     printf(" databaseName\n");
666     printf(" preferredRecordSyntax\n");
667     printf(" proxy\n");
668     printf(" elementSetName\n");
669     printf(" maximumRecordSize\n");
670     printf(" preferredRecordSize\n");
671     printf(" async\n");
672     printf(" piggyback\n");
673     printf(" group\n");
674     printf(" user\n");
675     printf(" password\n");
676     printf(" implementationName\n");
677     printf(" charset\n");
678     printf(" lang\n");
679     printf(" timeout\n");
680     printf(" facets\n");
681     printf(" extraArgs\n");
682     printf(" suggestions\n");
683     return 0;
684 }
685
686 static int cmd_connect(ZOOM_connection *c, ZOOM_resultset *r,
687                        ZOOM_options options,
688                        const char **args)
689 {
690     int ret = 0;
691     int error;
692     const char *errmsg, *addinfo, *dset;
693     int j, i;
694     WRBUF host = next_token_new_wrbuf(args);
695     if (!host)
696     {
697         printf("missing host after connect\n");
698         return 1;
699     }
700     for (j = -1, i = 0; i<MAX_CON; i++)
701     {
702         const char *h;
703         if (c[i] && (h = ZOOM_connection_option_get(c[i], "host")) &&
704             !strcmp(h, wrbuf_cstr(host)))
705         {
706             ZOOM_connection_destroy(c[i]);
707             break;
708         }
709         else if (c[i] == 0 && j == -1)
710             j = i;
711     }
712     if (i == MAX_CON)  /* no match .. */
713     {
714         if (j == -1)
715         {
716             printf("no more connection available\n");
717             wrbuf_destroy(host);
718             return 1;
719         }
720         i = j;   /* OK, use this one is available */
721     }
722     c[i] = ZOOM_connection_create(options);
723     ZOOM_connection_connect(c[i], wrbuf_cstr(host), 0);
724
725     if ((error = ZOOM_connection_error_x(c[i], &errmsg, &addinfo, &dset)))
726     {
727         printf("%s error: %s (%s:%d) %s\n",
728                ZOOM_connection_option_get(c[i], "host"), errmsg,
729                dset, error, addinfo);
730         ret = 1;
731     }
732     wrbuf_destroy(host);
733     return ret;
734 }
735
736 /** \brief parse and execute zoomsh command
737     \param c connections
738     \param r result sets
739     \param options ZOOM options
740     \param buf command string and arguments
741     \retval 0 OK
742     \retval 1 failure to execute
743     \retval -1 EOF (no more commands or quit seen)
744 */
745 static int cmd_parse(ZOOM_connection *c, ZOOM_resultset *r,
746                      ZOOM_options options,
747                      const char **buf)
748 {
749     int cmd_len;
750     const char *cmd_str;
751     int ret = 0;
752
753     cmd_len = next_token(buf, &cmd_str);
754     if (cmd_len < 0)
755         return 0;
756     if (is_command("quit", cmd_str, cmd_len))
757         return -1;
758     else if (is_command("set", cmd_str, cmd_len))
759         ret = cmd_set(c, r, options, buf);
760     else if (is_command("get", cmd_str, cmd_len))
761         ret = cmd_get(c, r, options, buf);
762     else if (is_command("rget", cmd_str, cmd_len))
763         ret = cmd_rget(c, r, options, buf);
764     else if (is_command("connect", cmd_str, cmd_len))
765         ret = cmd_connect(c, r, options, buf);
766     else if (is_command("open", cmd_str, cmd_len))
767         ret = cmd_connect(c, r, options, buf);
768     else if (is_command("search", cmd_str, cmd_len))
769         ret = cmd_search(c, r, options, buf);
770     else if (is_command("facets", cmd_str, cmd_len))
771         ret = cmd_facets(c, r, options, buf);
772     else if (is_command("find", cmd_str, cmd_len))
773         ret = cmd_search(c, r, options, buf);
774     else if (is_command("show", cmd_str, cmd_len))
775         ret = cmd_show(c, r, options, buf);
776     else if (is_command("suggestions", cmd_str, cmd_len))
777         ret = cmd_suggestions(c, r, options, buf);
778     else if (is_command("close", cmd_str, cmd_len))
779         ret = cmd_close(c, r, options, buf);
780     else if (is_command("help", cmd_str, cmd_len))
781         ret = cmd_help(c, r, options, buf);
782     else if (is_command("ext", cmd_str, cmd_len))
783         ret = cmd_ext(c, r, options, buf);
784     else if (is_command("debug", cmd_str, cmd_len))
785         ret = cmd_debug(c, r, options, buf);
786     else if (is_command("scan", cmd_str, cmd_len))
787         ret = cmd_scan(c, r, options, buf);
788     else if (is_command("sort", cmd_str, cmd_len))
789         ret = cmd_sort(c, r, options, buf);
790     else if (is_command("shell", cmd_str, cmd_len))
791         ret = cmd_shell(c, r, options, buf);
792     else
793     {
794         printf("unknown command %.*s\n", cmd_len, cmd_str);
795         ret = 1;
796     }
797     return ret;
798 }
799
800 static int shell(ZOOM_connection *c, ZOOM_resultset *r,
801                  ZOOM_options options, int exit_on_error)
802 {
803     int res = 0;
804     while (res == 0)
805     {
806         char buf[100000];
807         char *cp;
808         const char *bp = buf;
809 #if HAVE_READLINE_READLINE_H
810         char* line_in;
811         line_in = readline("ZOOM>");
812         if (!line_in)
813         {
814             res = -1;
815             break;
816         }
817 #if HAVE_READLINE_HISTORY_H
818         if (*line_in)
819             add_history(line_in);
820 #endif
821         if (strlen(line_in) > sizeof(buf)-1)
822         {
823             printf("Input line too long\n");
824             res = 1;
825             break;
826         }
827         strcpy(buf,line_in);
828         free(line_in);
829 #else
830         printf("ZOOM>"); fflush(stdout);
831         if (!fgets(buf, sizeof(buf)-1, stdin))
832         {
833             res = -1;
834             break;
835         }
836 #endif
837         if ((cp = strchr(buf, '\n')))
838             *cp = '\0';
839         res = cmd_parse(c, r, options, &bp);
840         if (res == -1)
841             break;
842         if (!exit_on_error && res > 0)
843             res = 0;
844     }
845     return res;
846 }
847
848 static int zoomsh(int argc, char **argv)
849 {
850     ZOOM_options zoom_options = ZOOM_options_create();
851     int i, res = 0; /* -1: EOF; 0 = OK, > 0 ERROR */
852     int exit_on_error = 0;
853     ZOOM_connection z39_con[MAX_CON];
854     ZOOM_resultset  z39_res[MAX_CON];
855
856     for (i = 0; i<MAX_CON; i++)
857     {
858         z39_con[i] = 0;
859         z39_res[i] = 0;
860     }
861     while (res == 0)
862     {
863         int mask;
864         char *arg = 0;
865         int option_ret = options("ev:", argv, argc, &arg);
866         const char *bp = arg;
867         switch (option_ret)
868         {
869         case 0:
870             res = cmd_parse(z39_con, z39_res, zoom_options, &bp);
871             /* returns res == -1 on quit */
872             if (!exit_on_error && res > 0)
873                 res = 0;  /* hide error */
874             break;
875         case YAZ_OPTIONS_EOF:
876             res = shell(z39_con, z39_res, zoom_options, exit_on_error);
877             break;
878         case 'e':
879             exit_on_error = 1;
880             break;
881         case 'v':
882             mask = yaz_log_mask_str(arg);
883             yaz_log_init_level(mask);
884             break;
885         default:
886             fprintf(stderr, "zoomsh: [-e] [-v] [commands]\n");
887             res = 1;
888         }
889     }
890
891     for (i = 0; i<MAX_CON; i++)
892     {
893         ZOOM_connection_destroy(z39_con[i]);
894         ZOOM_resultset_destroy(z39_res[i]);
895     }
896     ZOOM_options_destroy(zoom_options);
897     if (res == -1) /* quit .. which is not an error */
898         res = 0;
899     return res;
900 }
901
902 int main(int argc, char **argv)
903 {
904     int ret = zoomsh(argc, argv);
905     exit(ret);
906 }
907 /*
908  * Local variables:
909  * c-basic-offset: 4
910  * c-file-style: "Stroustrup"
911  * indent-tabs-mode: nil
912  * End:
913  * vim: shiftwidth=4 tabstop=8 expandtab
914  */
915