/src/barebox/commands/menu.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-only |
2 | | // SPDX-FileCopyrightText: © 2009-2010 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com> |
3 | | |
4 | | #include <common.h> |
5 | | #include <command.h> |
6 | | #include <readkey.h> |
7 | | #include <menu.h> |
8 | | #include <getopt.h> |
9 | | #include <errno.h> |
10 | | #include <libbb.h> |
11 | | #include <linux/err.h> |
12 | | |
13 | | typedef enum { |
14 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
15 | | action_add, |
16 | | action_remove, |
17 | | action_select, |
18 | | #endif |
19 | | action_list, |
20 | | action_show, |
21 | | } menu_action; |
22 | | |
23 | | struct cmd_menu { |
24 | | char *menu; |
25 | | menu_action action; |
26 | | char *description; |
27 | | int auto_select; |
28 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
29 | | int entry; |
30 | | int re_entrant; |
31 | | char *command; |
32 | | char *submenu; |
33 | | int num; |
34 | | menu_entry_type type; |
35 | | int box_state; |
36 | | #endif |
37 | | }; |
38 | | |
39 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
40 | 0 | #define OPTS "m:earlc:d:RsSn:u:A:b:B:" |
41 | 0 | #define is_entry(x) ((x)->entry) |
42 | | #else |
43 | | #define OPTS "m:lsA:d:" |
44 | | #define is_entry(x) (0) |
45 | | #endif |
46 | | |
47 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
48 | | /* |
49 | | * menu -e -a -m <menu> -c <command> [-R] [-b 0|1 ] -d <description> |
50 | | * menu -e -a -m <menu> -u submenu -d [-b 0|1] <description> |
51 | | */ |
52 | | static int do_menu_entry_add(struct cmd_menu *cm) |
53 | 0 | { |
54 | 0 | struct menu_entry *me; |
55 | 0 | struct menu *m; |
56 | |
|
57 | 0 | if (!cm->menu || (!cm->command && !cm->submenu) || !cm->description) |
58 | 0 | return -EINVAL; |
59 | | |
60 | 0 | m = menu_get_by_name(cm->menu); |
61 | |
|
62 | 0 | if (!m) { |
63 | 0 | eprintf("Menu '%s' not found\n", cm->menu); |
64 | 0 | return -EINVAL; |
65 | 0 | } |
66 | | |
67 | 0 | if (cm->submenu) |
68 | 0 | me = menu_add_submenu(m, cm->submenu, cm->description); |
69 | 0 | else |
70 | 0 | me = menu_add_command_entry(m, cm->description, cm->command, |
71 | 0 | cm->type); |
72 | 0 | if (IS_ERR(me)) |
73 | 0 | return PTR_ERR(me); |
74 | | |
75 | 0 | me->box_state = cm->box_state > 0 ? 1 : 0; |
76 | |
|
77 | 0 | if (!cm->submenu) |
78 | 0 | me->non_re_ent = !cm->re_entrant; |
79 | |
|
80 | 0 | return 0; |
81 | 0 | } |
82 | | |
83 | | /* |
84 | | * menu -e -r -m <name> -n <num> |
85 | | */ |
86 | | static int do_menu_entry_remove(struct cmd_menu *cm) |
87 | 0 | { |
88 | 0 | struct menu *m; |
89 | 0 | struct menu_entry *me; |
90 | |
|
91 | 0 | if (!cm->menu || cm->num < 0) |
92 | 0 | return -EINVAL; |
93 | | |
94 | 0 | m = menu_get_by_name(cm->menu); |
95 | |
|
96 | 0 | if (!m) { |
97 | 0 | eprintf("Menu '%s' not found\n", cm->menu); |
98 | 0 | return -EINVAL; |
99 | 0 | } |
100 | | |
101 | 0 | me = menu_entry_get_by_num(m, cm->num); |
102 | |
|
103 | 0 | if (!me) { |
104 | 0 | eprintf("Entry '%i' not found\n", cm->num); |
105 | 0 | return -EINVAL; |
106 | 0 | } |
107 | | |
108 | 0 | menu_remove_entry(m, me); |
109 | |
|
110 | 0 | menu_entry_free(me); |
111 | |
|
112 | 0 | return 0; |
113 | 0 | } |
114 | | |
115 | | /* |
116 | | * menu -a -m <name> -d <description> |
117 | | */ |
118 | | static int do_menu_add(struct cmd_menu *cm) |
119 | 0 | { |
120 | 0 | struct menu *m; |
121 | 0 | int ret = -ENOMEM; |
122 | |
|
123 | 0 | if (!cm->menu || !cm->description) |
124 | 0 | return -EINVAL; |
125 | | |
126 | 0 | m = menu_alloc(); |
127 | |
|
128 | 0 | if (!m) |
129 | 0 | goto free; |
130 | | |
131 | 0 | m->name = strdup_const(cm->menu); |
132 | 0 | if (!m->name) |
133 | 0 | goto free; |
134 | | |
135 | 0 | menu_add_title(m, cm->description); |
136 | |
|
137 | 0 | ret = menu_add(m); |
138 | |
|
139 | 0 | if (ret) |
140 | 0 | goto free; |
141 | | |
142 | 0 | return 0; |
143 | | |
144 | 0 | free: |
145 | 0 | eprintf("Menu '%s' add fail", cm->menu); |
146 | 0 | if (ret == -EEXIST) |
147 | 0 | eprintf(" already exist"); |
148 | 0 | eprintf("\n"); |
149 | |
|
150 | 0 | menu_free(m); |
151 | |
|
152 | 0 | return ret; |
153 | 0 | } |
154 | | /* |
155 | | * menu -r -m <name> |
156 | | */ |
157 | | static int do_menu_remove(struct cmd_menu *cm) |
158 | 0 | { |
159 | 0 | struct menu *m; |
160 | |
|
161 | 0 | m = menu_get_by_name(cm->menu); |
162 | |
|
163 | 0 | if (!m) { |
164 | 0 | eprintf("Menu '%s' not found\n", cm->menu); |
165 | 0 | return -EINVAL; |
166 | 0 | } |
167 | | |
168 | 0 | menu_remove(m); |
169 | |
|
170 | 0 | menu_free(m); |
171 | |
|
172 | 0 | return 0; |
173 | 0 | } |
174 | | |
175 | | /* |
176 | | * menu -m <menu> -S -n <entry num starting at 1> |
177 | | */ |
178 | | static int do_menu_select(struct cmd_menu *cm) |
179 | 0 | { |
180 | 0 | struct menu *m; |
181 | |
|
182 | 0 | if (cm->num < 0) |
183 | 0 | return -EINVAL; |
184 | | |
185 | 0 | m = menu_get_by_name(cm->menu); |
186 | |
|
187 | 0 | if (!m) { |
188 | 0 | eprintf("Menu '%s' not found\n", cm->menu); |
189 | 0 | return -EINVAL; |
190 | 0 | } |
191 | | |
192 | 0 | if (menu_set_selected(m, cm->num) < 0) { |
193 | 0 | eprintf("Entry '%d' not found\n", cm->num); |
194 | 0 | return -EINVAL; |
195 | 0 | } |
196 | | |
197 | 0 | return 0; |
198 | 0 | } |
199 | | #endif |
200 | | |
201 | | /* |
202 | | * menu -s -m <menu> [-A <auto select delay>] [-d <display] |
203 | | */ |
204 | | static int do_menu_show(struct cmd_menu *cm) |
205 | 0 | { |
206 | 0 | struct menu *m; |
207 | |
|
208 | 0 | if (cm->menu) |
209 | 0 | m = menu_get_by_name(cm->menu); |
210 | 0 | else |
211 | 0 | m = menu_get_by_name("boot"); |
212 | |
|
213 | 0 | if (!m) |
214 | 0 | return -EINVAL; |
215 | | |
216 | 0 | if (cm->auto_select != -EINVAL) { |
217 | 0 | menu_set_auto_select(m, cm->auto_select); |
218 | |
|
219 | 0 | free_const(m->auto_display); |
220 | |
|
221 | 0 | m->auto_display = strdup_const(cm->description); |
222 | 0 | } |
223 | |
|
224 | 0 | return menu_show(m); |
225 | 0 | } |
226 | | |
227 | | static void print_entries(struct menu *m) |
228 | 0 | { |
229 | 0 | struct menu_entry *me; |
230 | |
|
231 | 0 | list_for_each_entry(me, &m->entries, list) |
232 | 0 | printf("%d: %s\n", me->num, me->display); |
233 | 0 | } |
234 | | |
235 | | /* |
236 | | * menu -l |
237 | | * menu -e -l [menu] |
238 | | */ |
239 | | static int do_menu_list(struct cmd_menu *cm) |
240 | 0 | { |
241 | 0 | struct menu* m = NULL; |
242 | 0 | struct menu* menus = menu_get_menus(); |
243 | |
|
244 | 0 | if (!menus) |
245 | 0 | return 0; |
246 | | |
247 | 0 | if (is_entry(cm)) { |
248 | 0 | if (cm->menu) |
249 | 0 | m = menu_get_by_name(cm->menu); |
250 | |
|
251 | 0 | if (m) { |
252 | 0 | print_entries(m); |
253 | 0 | return 0; |
254 | 0 | } |
255 | 0 | } |
256 | | |
257 | 0 | list_for_each_entry(m, &menus->list, list) { |
258 | 0 | printf("%s: ", m->name); |
259 | 0 | if (m->display_lines) { |
260 | 0 | static char outstr[256]; |
261 | 0 | int i; |
262 | |
|
263 | 0 | printf("\n"); |
264 | 0 | for (i = 0; i < m->display_lines; i++) |
265 | | /* Conform to menu rendering logic */ |
266 | 0 | if (IS_ENABLED(CONFIG_SHELL_HUSH)) { |
267 | 0 | process_escape_sequence(m->display[i], outstr, 256); |
268 | 0 | printf("\t%s\n", outstr); |
269 | 0 | } else { |
270 | 0 | printf("\t%s\n", m->display[i]); |
271 | 0 | } |
272 | 0 | } else { |
273 | 0 | printf("%s\n", m->name); |
274 | 0 | } |
275 | 0 | if (is_entry(cm)) |
276 | 0 | print_entries(m); |
277 | 0 | } |
278 | |
|
279 | 0 | return 0; |
280 | 0 | } |
281 | | |
282 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
283 | | static int do_menu_entry(struct cmd_menu *cm) |
284 | 0 | { |
285 | 0 | switch(cm->action) { |
286 | 0 | case action_list: |
287 | 0 | return do_menu_list(cm); |
288 | 0 | case action_remove: |
289 | 0 | return do_menu_entry_remove(cm); |
290 | 0 | case action_add: |
291 | 0 | return do_menu_entry_add(cm); |
292 | 0 | case action_select: |
293 | 0 | case action_show: |
294 | 0 | break; |
295 | 0 | } |
296 | 0 | return -EINVAL; |
297 | 0 | } |
298 | | #else |
299 | | static int do_menu_entry(struct cmd_menu *cm) |
300 | | { |
301 | | return -EINVAL; |
302 | | } |
303 | | #endif |
304 | | |
305 | | static int do_menu(int argc, char *argv[]) |
306 | 0 | { |
307 | 0 | struct cmd_menu cm; |
308 | 0 | int opt; |
309 | 0 | int ret = -EINVAL; |
310 | |
|
311 | 0 | if (!argc) |
312 | 0 | return COMMAND_ERROR_USAGE; |
313 | | |
314 | 0 | memset(&cm, 0, sizeof(struct cmd_menu)); |
315 | 0 | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
316 | 0 | cm.num = -EINVAL; |
317 | 0 | cm.auto_select = -EINVAL; |
318 | 0 | #endif |
319 | |
|
320 | 0 | cm.action = action_show; |
321 | |
|
322 | 0 | while((opt = getopt(argc, argv, OPTS)) > 0) { |
323 | 0 | switch(opt) { |
324 | 0 | case 'm': |
325 | 0 | cm.menu = optarg; |
326 | 0 | break; |
327 | 0 | case 'l': |
328 | 0 | cm.action = action_list; |
329 | 0 | break; |
330 | 0 | case 's': |
331 | 0 | cm.action = action_show; |
332 | 0 | break; |
333 | 0 | case 'A': |
334 | 0 | cm.auto_select = simple_strtoul(optarg, NULL, 10); |
335 | 0 | break; |
336 | 0 | case 'd': |
337 | 0 | cm.description = optarg; |
338 | 0 | break; |
339 | 0 | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
340 | 0 | case 'e': |
341 | 0 | cm.entry = 1; |
342 | 0 | break; |
343 | 0 | case 'a': |
344 | 0 | cm.action = action_add; |
345 | 0 | break; |
346 | 0 | case 'r': |
347 | 0 | cm.action = action_remove; |
348 | 0 | break; |
349 | 0 | case 'c': |
350 | 0 | cm.command = optarg; |
351 | 0 | break; |
352 | 0 | case 'u': |
353 | 0 | cm.submenu = optarg; |
354 | 0 | break; |
355 | 0 | case 'R': |
356 | 0 | cm.re_entrant = 1; |
357 | 0 | break; |
358 | 0 | case 'S': |
359 | 0 | cm.action = action_select; |
360 | 0 | break; |
361 | 0 | case 'n': |
362 | 0 | cm.num = simple_strtoul(optarg, NULL, 10); |
363 | 0 | break; |
364 | 0 | case 'b': |
365 | 0 | cm.type = MENU_ENTRY_BOX; |
366 | 0 | cm.box_state = simple_strtoul(optarg, NULL, 10); |
367 | 0 | break; |
368 | 0 | case 'B': |
369 | 0 | cm.command = optarg; |
370 | 0 | break; |
371 | 0 | #endif |
372 | 0 | default: |
373 | 0 | return 1; |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 0 | if (is_entry(&cm)) { |
378 | 0 | ret = do_menu_entry(&cm); |
379 | 0 | goto end; |
380 | 0 | } |
381 | | |
382 | 0 | switch(cm.action) { |
383 | 0 | case action_list: |
384 | 0 | ret = do_menu_list(&cm); |
385 | 0 | break; |
386 | 0 | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
387 | 0 | case action_remove: |
388 | 0 | ret = do_menu_remove(&cm); |
389 | 0 | break; |
390 | 0 | case action_add: |
391 | 0 | ret = do_menu_add(&cm); |
392 | 0 | break; |
393 | 0 | case action_select: |
394 | 0 | ret = do_menu_select(&cm); |
395 | 0 | break; |
396 | 0 | #endif |
397 | 0 | case action_show: |
398 | 0 | ret = do_menu_show(&cm); |
399 | 0 | break; |
400 | 0 | } |
401 | | |
402 | 0 | end: |
403 | 0 | if (ret) |
404 | 0 | return 0; |
405 | | |
406 | 0 | return 1; |
407 | 0 | } |
408 | | |
409 | | BAREBOX_CMD_HELP_START(menu) |
410 | | BAREBOX_CMD_HELP_TEXT("Manage Menu:") |
411 | | BAREBOX_CMD_HELP_OPT ("-m", "menu") |
412 | | BAREBOX_CMD_HELP_OPT ("-l", "list") |
413 | | BAREBOX_CMD_HELP_OPT ("-s", "show") |
414 | | BAREBOX_CMD_HELP_TEXT("") |
415 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
416 | | BAREBOX_CMD_HELP_TEXT("Advanced menu management:") |
417 | | BAREBOX_CMD_HELP_OPT ("-e", "menu entry") |
418 | | BAREBOX_CMD_HELP_OPT ("-a", "add") |
419 | | BAREBOX_CMD_HELP_OPT ("-r", "remove") |
420 | | BAREBOX_CMD_HELP_OPT ("-S", "select") |
421 | | BAREBOX_CMD_HELP_TEXT("") |
422 | | #endif |
423 | | BAREBOX_CMD_HELP_TEXT("Show menu:") |
424 | | BAREBOX_CMD_HELP_OPT ("-A", "auto select delay") |
425 | | BAREBOX_CMD_HELP_OPT ("-d", "auto select description") |
426 | | BAREBOX_CMD_HELP_TEXT("\tmenu -s -m MENU [-A delay] [-d auto_display]") |
427 | | BAREBOX_CMD_HELP_TEXT("") |
428 | | BAREBOX_CMD_HELP_TEXT("List menu:") |
429 | | BAREBOX_CMD_HELP_TEXT("\tmenu -l\n") |
430 | | BAREBOX_CMD_HELP_TEXT("") |
431 | | #if defined(CONFIG_CMD_MENU_MANAGEMENT) |
432 | | BAREBOX_CMD_HELP_TEXT("Add a menu:") |
433 | | BAREBOX_CMD_HELP_TEXT("\tmenu -a -m NAME -d DESC") |
434 | | BAREBOX_CMD_HELP_TEXT("") |
435 | | BAREBOX_CMD_HELP_TEXT("Remove a menu:") |
436 | | BAREBOX_CMD_HELP_TEXT("\tmenu -r -m NAME") |
437 | | BAREBOX_CMD_HELP_TEXT("") |
438 | | BAREBOX_CMD_HELP_TEXT("Add an entry:") |
439 | | BAREBOX_CMD_HELP_TEXT("\t(-R for do no exit the menu after executing the command)") |
440 | | BAREBOX_CMD_HELP_TEXT("\t(-b for box style 1 for selected)") |
441 | | BAREBOX_CMD_HELP_TEXT("\t(and optional -c for the command to run when we change the state)") |
442 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -a -m MENU -c COMMAND [-R] [-b 0|1] -d DESC") |
443 | | BAREBOX_CMD_HELP_TEXT("") |
444 | | BAREBOX_CMD_HELP_TEXT("Add a submenu entry:") |
445 | | BAREBOX_CMD_HELP_TEXT("\t(-R is not needed)") |
446 | | BAREBOX_CMD_HELP_TEXT("\t(-b for box style 1 for selected)") |
447 | | BAREBOX_CMD_HELP_TEXT("\t(and -c is not needed)") |
448 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -a -m MENU -u submenu -d [-b 0|1] DESC") |
449 | | BAREBOX_CMD_HELP_TEXT("") |
450 | | BAREBOX_CMD_HELP_TEXT("Remove an entry:") |
451 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -r -m NAME -n ENTRY") |
452 | | BAREBOX_CMD_HELP_TEXT("") |
453 | | BAREBOX_CMD_HELP_TEXT("Select an entry:") |
454 | | BAREBOX_CMD_HELP_TEXT("\tmenu -m <menu> -S -n ENTRY") |
455 | | BAREBOX_CMD_HELP_TEXT("") |
456 | | BAREBOX_CMD_HELP_TEXT("List menu:") |
457 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -l [menu]") |
458 | | BAREBOX_CMD_HELP_TEXT("") |
459 | | BAREBOX_CMD_HELP_TEXT("Menu examples:") |
460 | | BAREBOX_CMD_HELP_TEXT("\tmenu -a -m boot -d \"Boot Menu\"") |
461 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -a -m boot -c boot -d \"Boot\"") |
462 | | BAREBOX_CMD_HELP_TEXT("\tmenu -e -a -m boot -c reset -d \"Reset\"") |
463 | | #else |
464 | | BAREBOX_CMD_HELP_TEXT("Menu example:") |
465 | | #endif |
466 | | BAREBOX_CMD_HELP_TEXT("\tmenu -s -m boot") |
467 | | BAREBOX_CMD_HELP_END |
468 | | |
469 | | BAREBOX_CMD_START(menu) |
470 | | .cmd = do_menu, |
471 | | BAREBOX_CMD_DESC("create and display menus") |
472 | | BAREBOX_CMD_GROUP(CMD_GRP_CONSOLE) |
473 | | BAREBOX_CMD_HELP(cmd_menu_help) |
474 | | BAREBOX_CMD_END |