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