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