Libecoli  0.11.3
Extensible COmmand LIne library
parse-yaml.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2018, Olivier MATZ <zer0@droids-corp.org>
3  */
4 
29 #include <errno.h>
30 #include <getopt.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <ecoli.h>
36 
37 static char *input_file;
38 static char *output_file;
39 static bool complete;
40 
41 static const char short_options[] = "h" /* help */
42  "i:" /* input-file */
43  "o:" /* output-file */
44  "c" /* complete */
45  ;
46 
47 #define OPT_HELP "help"
48 #define OPT_INPUT_FILE "input-file"
49 #define OPT_OUTPUT_FILE "output-file"
50 #define OPT_COMPLETE "complete"
51 
52 static const struct option long_options[] = {
53  {OPT_HELP, 0, NULL, 'h'},
54  {OPT_INPUT_FILE, 1, NULL, 'i'},
55  {OPT_OUTPUT_FILE, 1, NULL, 'o'},
56  {OPT_COMPLETE, 0, NULL, 'c'},
57  {NULL, 0, NULL, 0}
58 };
59 
60 static void usage(const char *prgname)
61 {
62  fprintf(stderr,
63  "%s -o <file.sh> -i <file.yaml>\n"
64  " -h\n"
65  " --" OPT_HELP "\n"
66  " Show this help.\n"
67  " -i <input-file>\n"
68  " --" OPT_INPUT_FILE "=<file>\n"
69  " Set the yaml input file describing the grammar.\n"
70  " -o <output-file>\n"
71  " --" OPT_OUTPUT_FILE "=<file>\n"
72  " Set the output file.\n"
73  " -c\n"
74  " --" OPT_COMPLETE "\n"
75  " Output the completion list.\n",
76  prgname);
77 }
78 
79 static int parse_args(int argc, char **argv)
80 {
81  int ret, opt;
82 
83  while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != EOF) {
84  switch (opt) {
85  case 'h': /* help */
86  usage(argv[0]);
87  exit(0);
88 
89  case 'i': /* input-file */
90  input_file = strdup(optarg);
91  break;
92 
93  case 'o': /* output-file */
94  output_file = strdup(optarg);
95  break;
96 
97  case 'c': /* complete */
98  complete = 1;
99  break;
100 
101  default:
102  usage(argv[0]);
103  return -1;
104  }
105  }
106 
107  if (input_file == NULL) {
108  fprintf(stderr, "No input file\n");
109  usage(argv[0]);
110  return -1;
111  }
112  if (output_file == NULL) {
113  fprintf(stderr, "No output file\n");
114  usage(argv[0]);
115  return -1;
116  }
117 
118  ret = optind - 1;
119  optind = 1;
120 
121  return ret;
122 }
123 
124 static int __dump_as_shell(FILE *f, const struct ec_pnode *parse, size_t *seq)
125 {
126  const struct ec_node *node = ec_pnode_get_node(parse);
127  struct ec_pnode *child;
128  size_t cur_seq, i, len;
129  char *quoted;
130 
131  (*seq)++;
132  cur_seq = *seq;
133 
134  quoted = ec_str_quote(ec_node_id(node), '\'', true);
135  fprintf(f, "ec_node%zu_id=%s\n", cur_seq, quoted);
136  free(quoted);
137 
138  quoted = ec_str_quote(ec_node_type_name(ec_node_type(node)), '\'', true);
139  fprintf(f, "ec_node%zu_type=%s\n", cur_seq, quoted);
140  free(quoted);
141 
142  len = ec_strvec_len(ec_pnode_get_strvec(parse));
143  fprintf(f, "ec_node%zu_strvec_len=%zu\n", cur_seq, len);
144  for (i = 0; i < len; i++) {
145  quoted = ec_str_quote(ec_strvec_val(ec_pnode_get_strvec(parse), i), '\'', true);
146  fprintf(f, "ec_node%zu_str%zu=%s\n", cur_seq, i, quoted);
147  free(quoted);
148  }
149 
150  if (ec_pnode_get_first_child(parse) != NULL) {
151  fprintf(f, "ec_node%zu_first_child='ec_node%zu'\n", cur_seq, cur_seq + 1);
152  }
153 
154  EC_PNODE_FOREACH_CHILD (child, parse) {
155  fprintf(f, "ec_node%zu_parent='ec_node%zu'\n", *seq + 1, cur_seq);
156  __dump_as_shell(f, child, seq);
157  }
158 
159  if (ec_pnode_next(parse) != NULL) {
160  fprintf(f, "ec_node%zu_next='ec_node%zu'\n", cur_seq, *seq + 1);
161  }
162 
163  return 0;
164 }
165 
166 static int dump_as_shell(const struct ec_pnode *parse)
167 {
168  FILE *f;
169  size_t seq = 0;
170  int ret;
171 
172  f = fopen(output_file, "w");
173  if (f == NULL)
174  return -1;
175 
176  ret = __dump_as_shell(f, parse, &seq);
177 
178  fclose(f);
179 
180  return ret;
181 }
182 
183 static int interact(struct ec_node *node)
184 {
185  struct ec_editline *editline = NULL;
186  struct ec_pnode *parse = NULL;
187  struct ec_node *shlex = NULL;
188 
189  shlex = ec_node_sh_lex(EC_NO_ID, ec_node_clone(node));
190  if (shlex == NULL) {
191  fprintf(stderr, "Failed to add lexer node\n");
192  goto fail;
193  }
194 
195  editline = ec_editline("parse-yaml", stdin, stdout, stderr, 0);
196  if (editline == NULL) {
197  fprintf(stderr, "Failed to initialize editline\n");
198  goto fail;
199  }
200 
201  if (ec_editline_set_node(editline, shlex) < 0) {
202  fprintf(stderr, "Failed to set editline ec_node\n");
203  goto fail;
204  }
205 
206  parse = ec_editline_parse(editline);
207  if (parse == NULL)
208  goto fail;
209 
210  if (!ec_pnode_matches(parse))
211  goto fail;
212 
213  if (dump_as_shell(parse) < 0) {
214  fprintf(stderr, "Failed to dump the parsed result\n");
215  goto fail;
216  }
217 
218  ec_pnode_free(parse);
219  ec_editline_free(editline);
220  ec_node_free(shlex);
221  return 0;
222 
223 fail:
224  ec_pnode_free(parse);
225  ec_editline_free(editline);
226  ec_node_free(shlex);
227  return -1;
228 }
229 
230 static int complete_words(const struct ec_node *node, int argc, char *argv[])
231 {
232  struct ec_comp *comp = NULL;
233  struct ec_strvec *strvec = NULL;
234  struct ec_comp_item *item = NULL;
235  size_t count;
236 
237  if (argc <= 1)
238  goto fail;
239  strvec = ec_strvec_from_array((const char *const *)&argv[1], argc - 1);
240  if (strvec == NULL)
241  goto fail;
242 
243  comp = ec_complete_strvec(node, strvec);
244  if (comp == NULL)
245  goto fail;
246 
248 
250  /* only one match, display it fully */
251  if (count == 1) {
252  printf("%s\n", ec_comp_item_get_str(item));
253  break;
254  }
255 
256  /* else show the 'display' part only */
257  printf("%s\n", ec_comp_item_get_display(item));
258  }
259 
260  ec_comp_free(comp);
261  ec_strvec_free(strvec);
262  return 0;
263 
264 fail:
265  ec_comp_free(comp);
266  ec_strvec_free(strvec);
267  return -1;
268 }
269 
270 int main(int argc, char *argv[])
271 {
272  struct ec_node *node = NULL;
273  int ret;
274 
275  ret = parse_args(argc, argv);
276  if (ret < 0)
277  goto fail;
278 
279  argc -= ret;
280  argv += ret;
281 
282  if (ec_init() < 0) {
283  fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
284  return 1;
285  }
286 
287  node = ec_yaml_import(input_file);
288  if (node == NULL) {
289  fprintf(stderr, "Failed to parse file\n");
290  goto fail;
291  }
292 
293  if (complete) {
294  if (complete_words(node, argc, argv) < 0)
295  goto fail;
296  } else {
297  if (interact(node) < 0)
298  goto fail;
299  }
300 
301  ec_node_free(node);
302 
303  return 0;
304 
305 fail:
306  ec_node_free(node);
307  return 1;
308 }
struct ec_comp * ec_comp(void)
const char * ec_comp_item_get_display(const struct ec_comp_item *item)
struct ec_comp * ec_complete_strvec(const struct ec_node *node, const struct ec_strvec *strvec)
const char * ec_comp_item_get_str(const struct ec_comp_item *item)
size_t ec_comp_count(const struct ec_comp *comp, enum ec_comp_type type)
void ec_comp_free(struct ec_comp *comp)
#define EC_COMP_FOREACH(item, comp, type)
Definition: complete.h:522
@ EC_COMP_FULL
Definition: complete.h:49
@ EC_COMP_PARTIAL
Definition: complete.h:50
@ EC_COMP_UNKNOWN
Definition: complete.h:48
struct ec_pnode * ec_editline_parse(struct ec_editline *editline)
void ec_editline_free(struct ec_editline *editline)
struct ec_editline * ec_editline(const char *prog, FILE *f_in, FILE *f_out, FILE *f_err, enum ec_editline_init_flags flags)
int ec_editline_set_node(struct ec_editline *editline, const struct ec_node *node)
int ec_init(void)
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
const char * ec_node_id(const struct ec_node *node)
#define EC_NO_ID
Definition: node.h:64
const struct ec_node_type * ec_node_type(const struct ec_node *node)
struct ec_node * ec_node_clone(struct ec_node *node)
const char * ec_node_type_name(const struct ec_node_type *type)
struct ec_node * ec_node(const char *typename, const char *id)
void ec_node_free(struct ec_node *node)
bool ec_pnode_matches(const struct ec_pnode *pnode)
void ec_pnode_free(struct ec_pnode *pnode)
#define EC_PNODE_FOREACH_CHILD(child, pnode)
Definition: parse.h:286
struct ec_pnode * ec_pnode_next(const struct ec_pnode *pnode)
struct ec_pnode * ec_pnode_get_first_child(const struct ec_pnode *pnode)
struct ec_pnode * ec_pnode(const struct ec_node *node)
const struct ec_node * ec_pnode_get_node(const struct ec_pnode *pnode)
const struct ec_strvec * ec_pnode_get_strvec(const struct ec_pnode *pnode)
char * ec_str_quote(const char *str, char quote, bool force)
void ec_strvec_free(struct ec_strvec *strvec)
struct ec_strvec * ec_strvec_from_array(const char *const *strarr, size_t n)
struct ec_strvec * ec_strvec(void)
size_t ec_strvec_len(const struct ec_strvec *strvec)
const char * ec_strvec_val(const struct ec_strvec *strvec, size_t idx)
struct ec_node * ec_yaml_import(const char *filename)