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