#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <ecoli_init.h>
+#include <ecoli_strvec.h>
#include <ecoli_node.h>
+#include <ecoli_parse.h>
+#include <ecoli_complete.h>
#include <ecoli_yaml.h>
+#include <ecoli_editline.h>
+#include <ecoli_node_sh_lex.h>
+
+static char *input_file;
+static char *output_file;
+static bool complete;
+
+static const char short_options[] =
+ "h" /* help */
+ "i:" /* input-file */
+ "o:" /* output-file */
+ "c" /* complete */
+ ;
+
+#define OPT_HELP "help"
+#define OPT_INPUT_FILE "input-file"
+#define OPT_OUTPUT_FILE "output-file"
+#define OPT_COMPLETE "complete"
+
+static const struct option long_options[] = {
+ {OPT_HELP, 0, NULL, 'h'},
+ {OPT_INPUT_FILE, 1, NULL, 'i'},
+ {OPT_OUTPUT_FILE, 1, NULL, 'o'},
+ {OPT_COMPLETE, 0, NULL, 'c'},
+ {NULL, 0, NULL, 0}
+};
+
+static void usage(const char *prgname)
+{
+ fprintf(stderr, "%s -o <file.sh> -i <file.yaml>\n"
+ " -h\n"
+ " --"OPT_HELP"\n"
+ " Show this help.\n"
+ " -i <input-file>\n"
+ " --"OPT_INPUT_FILE"=<file>\n"
+ " Set the yaml input file describing the grammar.\n"
+ " -o <output-file>\n"
+ " --"OPT_OUTPUT_FILE"=<file>\n"
+ " Set the output file.\n"
+ " -c\n"
+ " --"OPT_COMPLETE"\n"
+ " Output the completion list."
+ , prgname);
+}
+
+static int parse_args(int argc, char **argv)
+{
+ int ret, opt;
+
+ while ((opt = getopt_long(argc, argv, short_options,
+ long_options, NULL)) != EOF) {
+
+ switch (opt) {
+ case 'h': /* help */
+ usage(argv[0]);
+ exit(0);
+
+ case 'i': /* input-file */
+ input_file = strdup(optarg);
+ break;
+
+ case 'o': /* output-file */
+ output_file = strdup(optarg);
+ break;
+
+ case 'c': /* complete */
+ complete = 1;
+ break;
+
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+
+ }
+
+ if (input_file == NULL) {
+ fprintf(stderr, "No input file\n");
+ usage(argv[0]);
+ return -1;
+ }
+ if (output_file == NULL) {
+ fprintf(stderr, "No output file\n");
+ usage(argv[0]);
+ return -1;
+ }
+
+ ret = optind - 1;
+ optind = 1;
+
+ return ret;
+}
+
+static int
+__dump_as_shell(FILE *f, const struct ec_parse *parse, size_t *seq)
+{
+ const struct ec_node *node = ec_parse_get_node(parse);
+ struct ec_parse *child;
+ size_t cur_seq, i, len;
+ const char *s;
+
+ (*seq)++;
+ cur_seq = *seq;
+
+ // XXX protect strings
+
+
+ fprintf(f, "ec_node%zu_id='%s'\n", cur_seq, ec_node_id(node));
+ fprintf(f, "ec_node%zu_type='%s'\n", cur_seq,
+ ec_node_type_name(ec_node_type(node)));
+
+ len = ec_strvec_len(ec_parse_strvec(parse));
+ fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len);
+ for (i = 0; i < len; i++) {
+ s = ec_strvec_val(ec_parse_strvec(parse), i);
+ fprintf(f, "ec_node%zu_str%zu='%s'\n", cur_seq, i, s);
+ }
+
+ if (ec_parse_get_first_child(parse) != NULL) {
+ fprintf(f, "ec_node%zu_first_child='ec_node%zu'\n",
+ cur_seq, cur_seq + 1);
+ }
+
+ EC_PARSE_FOREACH_CHILD(child, parse) {
+ fprintf(f, "ec_node%zu_parent='ec_node%zu'\n",
+ *seq + 1, cur_seq);
+ __dump_as_shell(f, child, seq);
+ }
+
+ if (ec_parse_get_next(parse) != NULL) {
+ fprintf(f, "ec_node%zu_next='ec_node%zu'\n",
+ cur_seq, *seq + 1);
+ }
+
+ return 0;
+}
+
+static int
+dump_as_shell(const struct ec_parse *parse)
+{
+ FILE *f;
+ size_t seq = 0;
+ int ret;
+
+ f = fopen(output_file, "w");
+ if (f == NULL)
+ return -1;
+
+ ret = __dump_as_shell(f, parse, &seq);
+
+ fclose(f);
+
+ return ret;
+}
+
+static int
+interact(struct ec_node *node)
+{
+ struct ec_editline *editline = NULL;
+ struct ec_parse *parse = NULL;
+ struct ec_node *shlex = NULL;
+ char *line = NULL;
+
+ shlex = ec_node_sh_lex(EC_NO_ID, ec_node_clone(node)); //XXX
+ if (shlex == NULL) {
+ fprintf(stderr, "Failed to add lexer node\n");
+ goto fail;
+ }
+
+ editline = ec_editline("ecoli", stdin, stdout, stderr, 0);
+ if (editline == NULL) {
+ fprintf(stderr, "Failed to initialize editline\n");
+ goto fail;
+ }
+
+ parse = ec_editline_parse(editline, shlex);
+ if (parse == NULL)
+ goto fail;
+
+ if (!ec_parse_matches(parse))
+ goto fail;
+
+ //ec_parse_dump(stdout, parse);
+
+ if (dump_as_shell(parse) < 0) {
+ fprintf(stderr, "Failed to dump the parsed result\n");
+ goto fail;
+ }
+
+ ec_parse_free(parse);
+ ec_editline_free(editline);
+ ec_node_free(shlex);
+ return 0;
+
+fail:
+ ec_parse_free(parse);
+ ec_editline_free(editline);
+ free(line);
+ ec_node_free(shlex);
+ return -1;
+}
+
+static int
+complete_words(const struct ec_node *node, int argc, char *argv[])
+{
+ struct ec_comp *comp = NULL;
+ struct ec_strvec *strvec = NULL;
+ struct ec_comp_iter *iter = NULL;
+ struct ec_comp_item *item = NULL;
+ size_t count;
+
+ if (argc <= 1)
+ goto fail;
+ strvec = ec_strvec_from_array((const char * const *)&argv[1],
+ argc - 1);
+ if (strvec == NULL)
+ goto fail;
+
+ comp = ec_node_complete_strvec(node, strvec);
+ if (comp == NULL)
+ goto fail;
+
+ count = ec_comp_count(comp, EC_COMP_UNKNOWN | EC_COMP_FULL |
+ EC_COMP_PARTIAL);
+
+ iter = ec_comp_iter(comp,
+ EC_COMP_UNKNOWN | EC_COMP_FULL | EC_COMP_PARTIAL);
+ if (iter == NULL)
+ goto fail;
+
+ while ((item = ec_comp_iter_next(iter)) != NULL) {
+
+ /* only one match, display it fully */
+ if (count == 1) {
+ printf("%s\n", ec_comp_item_get_str(item));
+ break;
+ }
+
+ /* else show the 'display' part only */
+ printf("%s\n", ec_comp_item_get_display(item));
+ }
+
+ ec_comp_iter_free(iter);
+ ec_comp_free(comp);
+ ec_strvec_free(strvec);
+ return 0;
+
+fail:
+ ec_comp_free(comp);
+ ec_strvec_free(strvec);
+ return -1;
+}
int
main(int argc, char *argv[])
{
struct ec_node *node = NULL;
+ int ret;
- if (argc != 2) {
- fprintf(stderr, "Invalid args\n");
+ ret = parse_args(argc, argv);
+ if (ret < 0)
goto fail;
+
+ argc -= ret;
+ argv += ret;
+
+ if (ec_init() < 0) {
+ fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
+ return 1;
}
- node = ec_yaml_import(argv[1]);
+
+ node = ec_yaml_import(input_file);
if (node == NULL) {
fprintf(stderr, "Failed to parse file\n");
goto fail;
}
- ec_node_dump(stdout, node);
+ //ec_node_dump(stdout, node);
+
+ if (complete) {
+ if (complete_words(node, argc, argv) < 0)
+ goto fail;
+ } else {
+ if (interact(node) < 0)
+ goto fail;
+ }
+
ec_node_free(node);
return 0;