1 /* This file is part of the YAZ toolkit.
2 * Copyright (C) Index Data
3 * See the file LICENSE for details.
6 \brief ZOOM C command line tool (shell)
19 #include <yaz/wrbuf.h>
21 #include <yaz/backtrace.h>
22 #include <yaz/options.h>
24 #if HAVE_READLINE_READLINE_H
25 #include <readline/readline.h>
27 #if HAVE_READLINE_HISTORY_H
28 #include <readline/history.h>
49 static void process_events(struct zoom_sh *sh)
55 for (number = 0, db = sh->list; db; db = db->next)
58 c = xmalloc(sizeof(*c) * number);
60 for (i = 0, db = sh->list; db; db = db->next)
64 yaz_log(YLOG_DEBUG, "process_events");
65 while ((i = ZOOM_event(number, c)) != 0)
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,
72 ZOOM_get_event_str(event));
77 static int next_token_chars(const char **cpp, const char **t_start,
78 const char *tok_chars)
81 const char *cp = *cpp;
88 while (*cp && *cp != '"')
99 while (*cp && !strchr(tok_chars, *cp))
108 return len; /* return -1 if no token was read .. */
111 static int next_token(const char **cpp, const char **t_start)
113 return next_token_chars(cpp, t_start, "\r\n ");
117 static WRBUF next_token_new_wrbuf(const char **cpp)
121 int len = next_token(cpp, &start);
126 wrbuf_write(w, start, len);
130 static int is_command(const char *cmd_str, const char *this_str, int this_len)
132 int cmd_len = strlen(cmd_str);
133 if (cmd_len != this_len)
135 if (memcmp(cmd_str, this_str, cmd_len))
140 static int cmd_set(struct zoom_sh *sh, const char **args)
146 if (!(key = next_token_new_wrbuf(args)))
148 printf("missing argument for set\n");
151 val_len = next_token_chars(args, &val_buf, "");
153 ZOOM_options_setl(sh->options, wrbuf_cstr(key), val_buf, val_len);
155 ZOOM_options_set(sh->options, wrbuf_cstr(key), 0);
160 static int cmd_get(struct zoom_sh *sh, const char **args)
163 if (!(key = next_token_new_wrbuf(args)))
165 printf("missing argument for get\n");
170 const char *val = ZOOM_options_get(sh->options, wrbuf_cstr(key));
171 printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
177 static int cmd_shell(struct zoom_sh *sh, const char **args)
179 int ret = system(*args);
181 printf("system command returned %d\n", ret);
185 static int cmd_rget(struct zoom_sh *sh, const char **args)
188 if (!(key = next_token_new_wrbuf(args)))
190 printf("missing argument for get\n");
195 struct zoom_db *db = sh->list;
196 for (; db; db = db->next)
201 ZOOM_resultset_option_get(db->res, wrbuf_cstr(key));
202 printf("%s = %s\n", wrbuf_cstr(key), val ? val : "<null>");
210 static int cmd_close(struct zoom_sh *sh, const char **args)
212 struct zoom_db **dbp;
214 host = next_token_new_wrbuf(args);
216 for (dbp = &sh->list; *dbp; )
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))))
225 ZOOM_connection_destroy(db->con);
226 ZOOM_resultset_destroy(db->res);
237 static void display_records(ZOOM_connection c,
239 size_t start, size_t count, const char *type)
242 for (i = 0; i < count; i++)
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);
248 if (ZOOM_record_error(rec, 0, 0, 0))
253 int error = ZOOM_record_error(rec, &msg, &addinfo, &diagset);
255 printf("%lld %s: %s (%s:%d) %s\n", (long long) pos,
256 (db ? db : "unknown"),
257 msg, diagset, error, addinfo ? addinfo : "none");
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 */
268 printf("%lld database=%s syntax=%s schema=%s\n",
269 (long long) pos, (db ? db : "unknown"), syntax,
270 schema ? schema : "unknown");
273 if (fwrite(render, 1, len, stdout) != (size_t) len)
275 printf("write to stdout failed\n");
284 static int cmd_show(struct zoom_sh *sh, const char **args)
286 size_t start = 0, count = 1;
287 const char *type = "render";
288 WRBUF render_str = 0;
295 if ((tmp = next_token_new_wrbuf(args)))
297 start = atoi(wrbuf_cstr(tmp));
301 if ((tmp = next_token_new_wrbuf(args)))
303 count = atoi(wrbuf_cstr(tmp));
306 render_str = next_token_new_wrbuf(args);
309 type = wrbuf_cstr(render_str);
311 for (db = sh->list; db; db = db->next)
313 ZOOM_resultset_records(db->res, 0, start, count);
316 for (db = sh->list; db; db = db->next)
319 const char *errmsg, *addinfo, *dset;
320 /* display errors if any */
323 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo, &dset)))
325 printf("%s error: %s (%s:%d) %s\n",
326 ZOOM_connection_option_get(db->con, "host"), errmsg,
327 dset, error, addinfo);
332 /* OK, no major errors. Display records... */
333 display_records(db->con, db->res, start, count, type);
337 wrbuf_destroy(render_str);
341 static void display_facets(ZOOM_facet_field *facets, int count)
344 printf("Facets: \n");
345 for (i = 0; i < count; i++)
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++)
353 const char *term = ZOOM_facet_field_get_term(facets[i], j, &freq);
354 printf(" %s(%d) \n", term, freq);
359 static int cmd_facets(struct zoom_sh *sh, const char **args)
366 for (db = sh->list; db; db = db->next)
369 const char *errmsg, *addinfo, *dset;
370 /* display errors if any */
373 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo,
376 printf("%s error: %s (%s:%d) %s\n",
377 ZOOM_connection_option_get(db->con, "host"), errmsg,
378 dset, error, addinfo);
383 int num_facets = ZOOM_resultset_facets_size(db->res);
385 ZOOM_facet_field *facets = ZOOM_resultset_facets(db->res);
386 display_facets(facets, num_facets);
393 static int cmd_suggestions(struct zoom_sh *sh, const char **args)
400 for (db = sh->list; db; db = db->next)
403 const char *errmsg, *addinfo, *dset;
404 /* display errors if any */
407 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo,
410 printf("%s error: %s (%s:%d) %s\n",
411 ZOOM_connection_option_get(db->con, "host"), errmsg,
412 dset, error, addinfo);
417 const char *suggestions =
418 ZOOM_resultset_option_get(db->res, "suggestions");
420 printf("Suggestions: \n%s\n", suggestions);
426 static int cmd_ext(struct zoom_sh *sh, const char **args)
432 WRBUF ext_type_str = next_token_new_wrbuf(args);
436 printf("es: missing type "
437 "(itemorder, create, drop, commit, update, xmlupdate)\n");
440 for (number = 0, db = sh->list; db; db = db->next)
444 p = xmalloc(sizeof(*p) * number);
446 for (i = 0, db = sh->list; db; db = db->next)
449 p[i] = ZOOM_connection_package(db->con, 0);
450 ZOOM_package_send(p[i], ext_type_str ? wrbuf_cstr(ext_type_str):0);
456 for (i = 0, db = sh->list; db; db = db->next)
459 const char *errmsg, *addinfo, *dset;
460 /* display errors if any */
463 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo,
466 printf("%s error: %s (%s:%d) %s\n",
467 ZOOM_connection_option_get(db->con, "host"), errmsg,
468 dset, error, addinfo);
475 v = ZOOM_package_option_get(p[i], "targetReference");
477 printf("targetReference: %s\n", v);
478 v = ZOOM_package_option_get(p[i], "xmlUpdateDoc");
480 printf("xmlUpdateDoc: %s\n", v);
482 ZOOM_package_destroy(p[i]);
486 wrbuf_destroy(ext_type_str);
491 static int cmd_debug(struct zoom_sh *sh, const char **args)
493 yaz_log_init_level(YLOG_ALL);
497 static void display_search_result(struct zoom_db *db)
502 v = ZOOM_resultset_option_get(db->res, "searchresult.size");
503 if (v && (num = atoi(v)))
506 printf("SearchResult-1:");
507 for (i = 0; i < num; i++)
515 sprintf(str, "searchresult.%d.id", i);
516 v = ZOOM_resultset_option_get(db->res, str);
520 sprintf(str, "searchresult.%d.subquery.term", i);
521 v = ZOOM_resultset_option_get(db->res, str);
523 printf(" term=%s", v);
525 sprintf(str, "searchresult.%d.count", i);
526 v = ZOOM_resultset_option_get(db->res, str);
528 printf(" cnt=%s", v);
534 static int cmd_search(struct zoom_sh *sh, const char **args)
537 const char *query_str = *args;
541 s = ZOOM_query_create();
542 while (*query_str == ' ')
544 if (memcmp(query_str, "cql:", 4) == 0)
546 ZOOM_query_cql(s, query_str + 4);
548 else if (ZOOM_query_prefix(s, query_str))
550 printf("Bad PQF: %s\n", query_str);
551 ZOOM_query_destroy(s);
554 if (sh->strategy && wrbuf_len(sh->strategy) && wrbuf_len(sh->criteria))
556 int r = ZOOM_query_sortby2(s, wrbuf_cstr(sh->strategy),
557 wrbuf_cstr(sh->criteria));
561 printf("Bad sortby strategy: %s\n", wrbuf_cstr(sh->strategy));
563 printf("Bad sortby criteria: %s\n", wrbuf_cstr(sh->criteria));
564 ZOOM_query_destroy(s);
567 printf("sortby added\n");
569 for (db = sh->list; db; db = db->next)
573 ZOOM_resultset_destroy(db->res);
574 db->res = ZOOM_connection_search(db->con, s);
577 ZOOM_query_destroy(s);
581 for (db = sh->list; db; db = db->next)
584 const char *errmsg, *addinfo, *dset;
585 /* display errors if any */
588 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo,
591 printf("%s error: %s (%s:%d) %s\n",
592 ZOOM_connection_option_get(db->con, "host"), errmsg,
593 dset, error, addinfo);
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);
603 printf("%s: %lld hits\n", ZOOM_connection_option_get(db->con,
605 (long long int) ZOOM_resultset_size(db->res));
607 facet_num = ZOOM_resultset_facets_size(db->res);
610 ZOOM_facet_field *facets = ZOOM_resultset_facets(db->res);
612 for (facet_idx = 0; facet_idx < facet_num; facet_idx++)
614 const char *name = ZOOM_facet_field_name(facets[facet_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++ )
622 ZOOM_facet_field_get_term(facets[facet_idx], term_idx, &freq);
623 printf("term: %s %d\n", term, freq);
627 display_search_result(db);
629 display_records(db->con, db->res, start, count, "render");
635 static int cmd_scan(struct zoom_sh *sh, const char **args)
637 const char *query_str = *args;
638 ZOOM_query query = ZOOM_query_create();
644 while (*query_str == ' ')
647 if (memcmp(query_str, "cql:", 4) == 0)
649 ZOOM_query_cql(query, query_str + 4);
651 else if (ZOOM_query_prefix(query, query_str))
653 printf("Bad PQF: %s\n", query_str);
654 ZOOM_query_destroy(query);
658 for (number = 0, db = sh->list; db; db = db->next)
662 s = xmalloc(sizeof(*s) * number);
664 for (i = 0, db = sh->list; db; db = db->next)
666 s[i++] = ZOOM_connection_scan1(db->con, query);
668 ZOOM_query_destroy(query);
672 for (i = 0, db = sh->list; db; db = db->next)
675 const char *errmsg, *addinfo, *dset;
676 /* display errors if any */
679 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo,
682 printf("%s error: %s (%s:%d) %s\n",
683 ZOOM_connection_option_get(db->con, "host"), errmsg,
684 dset, error, addinfo);
689 size_t p, sz = ZOOM_scanset_size(s[i]);
690 for (p = 0; p < sz; p++)
694 const char *term = ZOOM_scanset_display_term(s[i], p,
696 printf("%.*s %lld\n", (int) len, term, (long long int) occ);
698 ZOOM_scanset_destroy(s[i]);
706 static int cmd_sortby(struct zoom_sh *sh, const char **args)
709 const char *criteria;
710 if (!(strategy = next_token_new_wrbuf(args)))
712 printf("missing argument argument: strategy and criteria\n");
716 while (*criteria == ' ')
718 wrbuf_destroy(sh->strategy);
719 sh->strategy = strategy;
721 wrbuf_rewind(sh->criteria);
722 wrbuf_puts(sh->criteria, criteria);
726 static int cmd_sort(struct zoom_sh *sh, const char **args)
728 const char *sort_spec = *args;
732 while (*sort_spec == ' ')
735 for (db = sh->list; db; db = db->next)
737 ZOOM_resultset_sort(db->res, "yaz", sort_spec);
742 static int cmd_help(struct zoom_sh *sh, const char **args)
744 printf("connect <zurl>\n");
745 printf("search <pqf>\n");
746 printf("sortby <strategy> <criteria>\n");
747 printf("show [<start> [<count> [<type]]]\n");
749 printf("scan <term>\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");
757 printf("options:\n");
760 printf(" databaseName\n");
761 printf(" preferredRecordSyntax\n");
763 printf(" elementSetName\n");
764 printf(" maximumRecordSize\n");
765 printf(" preferredRecordSize\n");
767 printf(" piggyback\n");
770 printf(" password\n");
771 printf(" implementationName\n");
772 printf(" charset\n");
774 printf(" timeout\n");
776 printf(" extraArgs\n");
777 printf(" suggestions\n");
781 static int cmd_connect(struct zoom_sh *sh, const char **args)
785 const char *errmsg, *addinfo, *dset;
787 WRBUF host = next_token_new_wrbuf(args);
790 printf("missing host after connect\n");
793 for (db = sh->list; db; db = db->next)
796 if (db->con && (h = ZOOM_connection_option_get(db->con, "host")) &&
797 !strcmp(h, wrbuf_cstr(host)))
799 ZOOM_connection_destroy(db->con);
803 if (!db) /* no match .. */
805 db = xmalloc(sizeof(*db));
810 db->con = ZOOM_connection_create(sh->options);
812 ZOOM_connection_connect(db->con, wrbuf_cstr(host), 0);
816 if ((error = ZOOM_connection_error_x(db->con, &errmsg, &addinfo, &dset)))
818 printf("%s error: %s (%s:%d) %s\n",
819 ZOOM_connection_option_get(db->con, "host"), errmsg,
820 dset, error, addinfo);
827 /** \brief parse and execute zoomsh command
829 \param buf command string and arguments
831 \retval 1 failure to execute
832 \retval -1 EOF (no more commands or quit seen)
834 static int cmd_parse(struct zoom_sh *sh, const char **buf)
840 cmd_len = next_token(buf, &cmd_str);
843 if (is_command("quit", cmd_str, cmd_len))
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);
883 printf("unknown command %.*s\n", cmd_len, cmd_str);
889 static int shell(struct zoom_sh *sh, int exit_on_error)
896 const char *bp = buf;
898 #if HAVE_READLINE_READLINE_H
901 line_in = readline("ZOOM>");
908 #if HAVE_READLINE_HISTORY_H
910 add_history(line_in);
912 if (strlen(line_in) > sizeof(buf)-1)
914 printf("Input line too long\n");
922 if (!line_in) /* no line buffer via readline or not enabled at all */
926 printf("ZOOM>"); fflush(stdout);
928 if (!fgets(buf, sizeof(buf)-1, stdin))
934 if ((cp = strchr(buf, '\n')))
936 res = cmd_parse(sh, &bp);
939 if (!exit_on_error && res > 0)
945 static int zoomsh(int argc, char **argv)
947 int res = 0; /* -1: EOF; 0 = OK, > 0 ERROR */
948 int exit_on_error = 0;
953 sh.options = ZOOM_options_create();
955 sh.criteria = wrbuf_alloc();
961 int option_ret = options("a:ev:", argv, argc, &arg);
962 const char *bp = arg;
966 res = cmd_parse(&sh, &bp);
967 /* returns res == -1 on quit */
968 if (!exit_on_error && res > 0)
969 res = 0; /* hide error */
971 case YAZ_OPTIONS_EOF:
972 res = shell(&sh, exit_on_error);
975 ZOOM_options_set(sh.options, "apdufile", arg);
981 mask = yaz_log_mask_str(arg);
982 yaz_log_init_level(mask);
985 fprintf(stderr, "zoomsh: [-a apdulog] [-e] [-v level] [commands]\n");
990 for (db = sh.list; db; )
992 struct zoom_db *n = db->next;
993 ZOOM_connection_destroy(db->con);
994 ZOOM_resultset_destroy(db->res);
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 */
1006 int main(int argc, char **argv)
1010 yaz_enable_panic_backtrace(*argv);
1011 ret = zoomsh(argc, argv);
1017 * c-file-style: "Stroustrup"
1018 * indent-tabs-mode: nil
1020 * vim: shiftwidth=4 tabstop=8 expandtab