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