Libecoli 0.11.1
Extensible COmmand LIne library
Loading...
Searching...
No Matches
readline/main.c
1/* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright 2016, Olivier MATZ <zer0@droids-corp.org>
3 */
4
34
35#include <errno.h>
36#include <stdio.h>
37#include <stdlib.h>
38
39#include <readline/history.h>
40#include <readline/readline.h>
41
42#include <ecoli.h>
43
44static struct ec_node *commands;
45
46static char *my_completion_entry(const char *s, int state)
47{
48 static struct ec_comp *c;
49 static struct ec_comp_item *item;
50 enum ec_comp_type item_type;
51 const char *item_str, *item_display;
52
53 (void)s;
54
55 /* Don't append a quote. Note: there are still some bugs when
56 * completing a quoted token. */
57 rl_completion_suppress_quote = 1;
58 rl_completer_quote_characters = "\"'";
59
60 if (state == 0) {
61 char *line;
62
63 ec_comp_free(c);
64 line = strdup(rl_line_buffer);
65 if (line == NULL)
66 return NULL;
67 line[rl_point] = '\0';
68
69 c = ec_complete(commands, line);
70 free(line);
71 if (c == NULL)
72 return NULL;
73
74 free(item);
76 if (item == NULL)
77 return NULL;
78 } else {
80 if (item == NULL)
81 return NULL;
82 }
83
84 item_str = ec_comp_item_get_str(item);
86 /* don't add the trailing space for partial completions */
87 if (state == 0) {
88 item_type = ec_comp_item_get_type(item);
89 if (item_type == EC_COMP_FULL)
90 rl_completion_suppress_append = 0;
91 else
92 rl_completion_suppress_append = 1;
93 }
94
95 return strdup(item_str);
96 } else if (rl_completion_type == '?') {
97 /* on second try only show the display part */
98 item_display = ec_comp_item_get_display(item);
99 return strdup(item_display);
100 }
101
102 return strdup(item_str);
103}
104
105static char **my_attempted_completion(const char *text, int start, int end)
106{
107 (void)start;
108 (void)end;
109
110 /* remove default file completion */
111 rl_attempted_completion_over = 1;
112
113 return rl_completion_matches(text, my_completion_entry);
114}
115
116/* this function builds the help string */
117static char *get_node_help(const struct ec_comp_item *item)
118{
119 const struct ec_comp_group *grp;
120 const struct ec_pnode *pstate;
121 const struct ec_node *node;
122 char *help = NULL;
123 const char *node_help = NULL;
124 char *node_desc = NULL;
125
126 grp = ec_comp_item_get_grp(item);
127 for (pstate = ec_comp_group_get_pstate(grp); pstate != NULL;
128 pstate = ec_pnode_get_parent(pstate)) {
129 node = ec_pnode_get_node(pstate);
130 if (node_help == NULL)
131 node_help = ec_dict_get(ec_node_attrs(node), "help");
132 if (node_desc == NULL)
133 node_desc = ec_node_desc(node);
134 }
135
136 if (node_help == NULL)
137 node_help = "-";
138 if (node_desc == NULL)
139 return NULL;
140
141 if (asprintf(&help, "%-20s %s", node_desc, node_help) < 0)
142 return NULL;
143
144 free(node_desc);
145
146 return help;
147}
148
149static int show_help(int ignore, int invoking_key)
150{
151 const struct ec_comp_group *grp, *prev_grp = NULL;
152 struct ec_comp_item *item = NULL;
153 struct ec_comp *c = NULL;
154 struct ec_pnode *p = NULL;
155 char *line = NULL;
156 size_t count = 0;
157 char **helps = NULL;
158 int match = 0;
159 int ret = 1;
160 int cols;
161
162 (void)ignore;
163 (void)invoking_key;
164
165 line = strdup(rl_line_buffer);
166 if (line == NULL)
167 goto fail;
168
169 /* check if the current line matches */
170 p = ec_parse(commands, line);
171 if (ec_pnode_matches(p))
172 match = 1;
173 ec_pnode_free(p);
174 p = NULL;
175
176 /* complete at current cursor position */
177 line[rl_point] = '\0';
178 c = ec_complete(commands, line);
179 free(line);
180 line = NULL;
181 if (c == NULL)
182 goto fail;
183
184 /* strangely, rl_display_match_list() expects first index at 1 */
185 helps = calloc(match + 1, sizeof(char *));
186 if (helps == NULL)
187 goto fail;
188 if (match)
189 helps[1] = "<return>";
190
191 /* let's display one contextual help per node */
193 char **tmp;
194
195 /* keep one help per group, skip other items */
196 grp = ec_comp_item_get_grp(item);
197 if (grp == prev_grp)
198 continue;
199
200 prev_grp = grp;
201
202 tmp = realloc(helps, (count + match + 2) * sizeof(char *));
203 if (tmp == NULL)
204 goto fail;
205 helps = tmp;
206 helps[count + match + 1] = get_node_help(item);
207 count++;
208 }
209
210 /* ensure not more than 1 entry per line */
211 rl_get_screen_size(NULL, &cols);
212 rl_display_match_list(helps, count + match, cols);
213 rl_forced_update_display();
214
215 ret = 0;
216
217fail:
218 free(item);
219 ec_pnode_free(p);
220 free(line);
221 ec_comp_free(c);
222 if (helps != NULL) {
223 while (count--)
224 free(helps[count + match + 1]);
225 }
226 free(helps);
227
228 return ret;
229}
230
231static int create_commands(void)
232{
233 struct ec_node *cmdlist = NULL, *cmd = NULL;
234
235 cmdlist = ec_node("or", EC_NO_ID);
236 if (cmdlist == NULL)
237 goto fail;
238
239 cmd = EC_NODE_SEQ(
240 EC_NO_ID,
241 ec_node_str(EC_NO_ID, "hello"),
243 "name",
244 ec_node_str("john", "john"),
245 ec_node_str(EC_NO_ID, "johnny"),
246 ec_node_str(EC_NO_ID, "mike")
247 ),
248 ec_node_option(EC_NO_ID, ec_node_int("int", 0, 10, 10))
249 );
250 if (cmd == NULL)
251 goto fail;
252 ec_dict_set(ec_node_attrs(cmd), "help", "say hello to someone several times", NULL);
254 ec_node_attrs(ec_node_find(cmd, "john")), "help", "specific help for john", NULL
255 );
257 ec_node_attrs(ec_node_find(cmd, "name")), "help", "the name of the person", NULL
258 );
259 ec_dict_set(ec_node_attrs(ec_node_find(cmd, "int")), "help", "an integer (0-10)", NULL);
260 if (ec_node_or_add(cmdlist, cmd) < 0)
261 goto fail;
262
263 cmd = EC_NODE_CMD(
264 EC_NO_ID,
265 "good morning name [count]",
266 EC_NODE_CMD("name", "bob|bobby|michael"),
267 ec_node_int("count", 0, 10, 10)
268 );
269 if (cmd == NULL)
270 goto fail;
271 ec_dict_set(ec_node_attrs(cmd), "help", "say good morning to someone several times", NULL);
272 ec_dict_set(ec_node_attrs(ec_node_find(cmd, "name")), "help", "the person to greet", NULL);
274 ec_node_attrs(ec_node_find(cmd, "count")),
275 "help",
276 "how many times to greet (0-10)",
277 NULL
278 );
279 if (ec_node_or_add(cmdlist, cmd) < 0)
280 goto fail;
281
282 cmd = EC_NODE_CMD(EC_NO_ID, "buy potatoes,carrots,pumpkins");
283 if (cmd == NULL)
284 goto fail;
285 ec_dict_set(ec_node_attrs(cmd), "help", "buy some vegetables", NULL);
286 if (ec_node_or_add(cmdlist, cmd) < 0)
287 goto fail;
288
289 cmd = EC_NODE_CMD(
290 EC_NO_ID,
291 "eat vegetables",
293 "vegetables",
295 EC_NO_ID,
296 ec_node_str(EC_NO_ID, "potatoes"),
299 ),
300 1,
301 0
302 )
303 );
304 if (cmd == NULL)
305 goto fail;
306 ec_dict_set(ec_node_attrs(cmd), "help", "eat vegetables (take some more potatoes)", NULL);
307 if (ec_node_or_add(cmdlist, cmd) < 0)
308 goto fail;
309
311 ec_dict_set(ec_node_attrs(cmd), "help", "say bye", NULL);
312 if (ec_node_or_add(cmdlist, cmd) < 0)
313 goto fail;
314
315 cmd = EC_NODE_SEQ(EC_NO_ID, ec_node_str(EC_NO_ID, "load"), ec_node("file", EC_NO_ID));
316 ec_dict_set(ec_node_attrs(cmd), "help", "load a file", NULL);
317 if (ec_node_or_add(cmdlist, cmd) < 0)
318 goto fail;
319
320 commands = ec_node_sh_lex(EC_NO_ID, cmdlist);
321 if (commands == NULL)
322 goto fail;
323
324 return 0;
325
326fail:
327 fprintf(stderr, "cannot initialize nodes\n");
328 ec_node_free(cmdlist);
329 return -1;
330}
331
332int main(void)
333{
334 struct ec_pnode *p;
335 char *line;
336
337 if (ec_init() < 0) {
338 fprintf(stderr, "cannot init ecoli: %s\n", strerror(errno));
339 return 1;
340 }
341
342 if (create_commands() < 0)
343 return 1;
344
345 rl_bind_key('?', show_help);
346 rl_attempted_completion_function = my_attempted_completion;
347
348 while (1) {
349 line = readline("> ");
350 if (line == NULL)
351 break;
352
353 p = ec_parse(commands, line);
354 ec_pnode_dump(stdout, p);
355 add_history(line);
356 ec_pnode_free(p);
357 }
358
359 ec_node_free(commands);
360 return 0;
361}
const struct ec_comp_group * ec_comp_item_get_grp(const struct ec_comp_item *item)
struct ec_comp_item * ec_comp_iter_next(struct ec_comp_item *item, enum ec_comp_type type)
const struct ec_pnode * ec_comp_group_get_pstate(const struct ec_comp_group *grp)
struct ec_comp * ec_comp(void)
struct ec_comp * ec_complete(const struct ec_node *node, const char *str)
struct ec_comp_item * ec_comp_iter_first(const struct ec_comp *comp, enum ec_comp_type type)
const char * ec_comp_item_get_display(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)
ec_comp_type
Definition complete.h:47
const char * ec_comp_item_get_str(const struct ec_comp_item *item)
#define EC_COMP_FOREACH(item, comp, type)
Definition complete.h:522
enum ec_comp_type ec_comp_item_get_type(const struct ec_comp_item *item)
@ EC_COMP_FULL
Definition complete.h:49
@ EC_COMP_PARTIAL
Definition complete.h:50
@ EC_COMP_UNKNOWN
Definition complete.h:48
void * ec_dict_get(const struct ec_dict *dict, const char *key)
int ec_dict_set(struct ec_dict *dict, const char *key, void *val, ec_dict_elt_free_t free_cb)
int ec_init(void)
#define EC_NODE_CMD(args...)
Definition node_cmd.h:30
struct ec_node * ec_node_int(const char *id, int64_t min, int64_t max, unsigned int base)
struct ec_node * ec_node_many(const char *id, struct ec_node *child, unsigned int min, unsigned int max)
struct ec_node * ec_node_once(const char *id, struct ec_node *child)
struct ec_node * ec_node_option(const char *id, struct ec_node *node)
int ec_node_or_add(struct ec_node *node, struct ec_node *child)
#define EC_NODE_OR(args...)
Definition node_or.h:23
#define EC_NODE_SEQ(args...)
Definition node_seq.h:29
struct ec_node * ec_node_sh_lex(const char *id, struct ec_node *child)
struct ec_node * ec_node_str(const char *id, const char *str)
struct ec_node * ec_node(const char *typename, const char *id)
struct ec_dict * ec_node_attrs(const struct ec_node *node)
struct ec_node * ec_node_find(struct ec_node *node, const char *id)
#define EC_NO_ID
Definition node.h:64
char * ec_node_desc(const struct ec_node *node)
void ec_node_free(struct ec_node *node)
bool ec_pnode_matches(const struct ec_pnode *pnode)
struct ec_pnode * ec_pnode_get_parent(const struct ec_pnode *pnode)
struct ec_pnode * ec_parse(const struct ec_node *node, const char *str)
void ec_pnode_dump(FILE *out, const struct ec_pnode *pnode)
void ec_pnode_free(struct ec_pnode *pnode)
const struct ec_node * ec_pnode_get_node(const struct ec_pnode *pnode)
struct ec_pnode * ec_pnode(const struct ec_node *node)