Adding upstream version 4.00~pre61+dfsg.
[syslinux-debian/hramrach.git] / com32 / hdt / hdt-cli.c
blob639bcdb88d5f6d7f4b8ee9b10de4c03cdb5cc26b
1 /* ----------------------------------------------------------------------- *
3 * Copyright 2009 Erwan Velu - All Rights Reserved
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
26 * -----------------------------------------------------------------------
29 #include <stdlib.h>
30 #include <string.h>
31 #include <syslinux/config.h>
32 #include <getkey.h>
33 #include "hdt-cli.h"
34 #include "hdt-common.h"
36 struct cli_mode_descr *list_modes[] = {
37 &hdt_mode,
38 &dmi_mode,
39 &syslinux_mode,
40 &pxe_mode,
41 &kernel_mode,
42 &cpu_mode,
43 &pci_mode,
44 &vesa_mode,
45 &disk_mode,
46 &vpd_mode,
47 &memory_mode,
48 NULL,
52 * .aliases = {"q", "quit"} won't work since it is an array of pointers, not an
53 * array of variables. There is no easy way around it besides declaring the arrays of
54 * strings first.
56 const char *exit_aliases[] = { "q", "quit" };
57 const char *help_aliases[] = { "h", "?" };
59 /* List of aliases */
60 struct cli_alias hdt_aliases[] = {
62 .command = CLI_EXIT,
63 .nb_aliases = 2,
64 .aliases = exit_aliases,
67 .command = CLI_HELP,
68 .nb_aliases = 2,
69 .aliases = help_aliases,
73 struct cli_mode_descr *current_mode;
74 int autocomplete_backlog;
76 struct autocomplete_list {
77 char autocomplete_token[MAX_LINE_SIZE];
78 struct autocomplete_list *next;
80 struct autocomplete_list *autocomplete_head = NULL;
81 struct autocomplete_list *autocomplete_tail = NULL;
82 struct autocomplete_list *autocomplete_last_seen = NULL;
84 static void autocomplete_add_token_to_list(const char *token)
86 struct autocomplete_list *new = malloc(sizeof(struct autocomplete_list));
88 strlcpy(new->autocomplete_token, token, sizeof(new->autocomplete_token));
89 new->next = NULL;
90 autocomplete_backlog++;
92 if (autocomplete_tail != NULL)
93 autocomplete_tail->next = new;
94 if (autocomplete_head == NULL)
95 autocomplete_head = new;
96 autocomplete_tail = new;
99 static void autocomplete_destroy_list(void)
101 struct autocomplete_list *tmp = NULL;
103 while (autocomplete_head != NULL) {
104 tmp = autocomplete_head->next;
105 free(autocomplete_head);
106 autocomplete_head = tmp;
108 autocomplete_backlog = 0;
109 autocomplete_tail = NULL;
110 autocomplete_last_seen = NULL;
114 * set_mode - set the current mode of the cli
115 * @mode: mode to set
117 * Unlike cli_set_mode, this function is not used by the cli directly.
119 void set_mode(cli_mode_t mode, struct s_hardware *hardware)
121 int i = 0;
123 switch (mode) {
124 case EXIT_MODE:
125 hdt_cli.mode = mode;
126 break;
127 case HDT_MODE:
128 hdt_cli.mode = mode;
129 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_HDT);
130 break;
131 case PXE_MODE:
132 if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) {
133 printf("You are not currently using PXELINUX\n");
134 break;
136 hdt_cli.mode = mode;
137 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PXE);
138 break;
139 case KERNEL_MODE:
140 detect_pci(hardware);
141 hdt_cli.mode = mode;
142 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_KERNEL);
143 break;
144 case SYSLINUX_MODE:
145 hdt_cli.mode = mode;
146 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_SYSLINUX);
147 break;
148 case VESA_MODE:
149 hdt_cli.mode = mode;
150 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VESA);
151 break;
152 case PCI_MODE:
153 hdt_cli.mode = mode;
154 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PCI);
155 if (!hardware->pci_detection)
156 cli_detect_pci(hardware);
157 break;
158 case CPU_MODE:
159 hdt_cli.mode = mode;
160 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_CPU);
161 if (!hardware->dmi_detection)
162 detect_dmi(hardware);
163 if (!hardware->cpu_detection)
164 cpu_detect(hardware);
165 break;
166 case DMI_MODE:
167 detect_dmi(hardware);
168 if (!hardware->is_dmi_valid) {
169 printf("No valid DMI table found, exiting.\n");
170 break;
172 hdt_cli.mode = mode;
173 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DMI);
174 break;
175 case DISK_MODE:
176 detect_disks(hardware);
177 hdt_cli.mode = mode;
178 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DISK);
179 break;
180 case VPD_MODE:
181 detect_vpd(hardware);
182 if (!hardware->is_vpd_valid) {
183 printf("No valid VPD table found, exiting.\n");
184 break;
186 hdt_cli.mode = mode;
187 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VPD);
188 break;
189 case MEMORY_MODE:
190 hdt_cli.mode = mode;
191 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_MEMORY);
192 break;
193 default:
194 /* Invalid mode */
195 printf("Unknown mode, please choose among:\n");
196 while (list_modes[i]) {
197 printf("\t%s\n", list_modes[i]->name);
198 i++;
202 find_cli_mode_descr(hdt_cli.mode, &current_mode);
203 /* There is not cli_mode_descr struct for the exit mode */
204 if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) {
205 /* Shouldn't get here... */
206 printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
211 * mode_s_to_mode_t - given a mode string, return the cli_mode_t representation
213 cli_mode_t mode_s_to_mode_t(char *name)
215 int i = 0;
217 while (list_modes[i]) {
218 if (!strncmp(name, list_modes[i]->name, sizeof(list_modes[i]->name)))
219 break;
220 i++;
223 if (!list_modes[i])
224 return INVALID_MODE;
225 else
226 return list_modes[i]->mode;
230 * find_cli_mode_descr - find the cli_mode_descr struct associated to a mode
231 * @mode: mode to look for
232 * @mode_found: store the mode if found, NULL otherwise
234 * Given a mode name, return a pointer to the associated cli_mode_descr
235 * structure.
236 * Note: the current mode name is stored in hdt_cli.mode.
238 void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found)
240 int i = 0;
242 while (list_modes[i] && list_modes[i]->mode != mode)
243 i++;
245 /* Shouldn't get here... */
246 if (!list_modes[i])
247 *mode_found = NULL;
248 else
249 *mode_found = list_modes[i];
253 * expand_aliases - resolve aliases mapping
254 * @line: command line to parse
255 * @command: first token in the line
256 * @module: second token in the line
257 * @argc: number of arguments
258 * @argv: array of arguments
260 * We maintain a small list of static alises to enhance user experience.
261 * Only commands can be aliased (first token). Otherwise it can become really hairy...
263 static void expand_aliases(char *line __unused, char **command, char **module,
264 int *argc, char **argv)
266 struct cli_mode_descr *mode;
267 int i, j;
269 find_cli_mode_descr(mode_s_to_mode_t(*command), &mode);
270 if (mode != NULL && *module == NULL) {
272 * The user specified a mode instead of `set mode...', e.g.
273 * `dmi' instead of `set mode dmi'
276 /* *argv is NULL since *module is NULL */
277 *argc = 1;
278 *argv = malloc(*argc * sizeof(char *));
279 argv[0] = malloc((sizeof(*command) + 1) * sizeof(char));
280 strlcpy(argv[0], *command, sizeof(*command) + 1);
281 dprintf("CLI DEBUG: ALIAS %s ", *command);
283 strlcpy(*command, CLI_SET, sizeof(CLI_SET)); /* set */
285 *module = malloc(sizeof(CLI_MODE) * sizeof(char));
286 strlcpy(*module, CLI_MODE, sizeof(CLI_MODE)); /* mode */
288 dprintf("--> %s %s %s\n", *command, *module, argv[0]);
289 goto out;
292 /* Simple aliases mapping a single command to another one */
293 for (i = 0; i < MAX_ALIASES; i++) {
294 for (j = 0; j < hdt_aliases[i].nb_aliases; j++) {
295 if (!strncmp(*command, hdt_aliases[i].aliases[j],
296 sizeof(hdt_aliases[i].aliases[j]))) {
297 dprintf("CLI DEBUG: ALIAS %s ", *command);
298 strlcpy(*command, hdt_aliases[i].command,
299 sizeof(hdt_aliases[i].command) + 1);
300 dprintf("--> %s\n", *command);
301 goto out; /* Don't allow chaining aliases */
305 return;
307 out:
308 dprintf("CLI DEBUG: New parameters:\n");
309 dprintf("CLI DEBUG: command = %s\n", *command);
310 dprintf("CLI DEBUG: module = %s\n", *module);
311 dprintf("CLI DEBUG: argc = %d\n", *argc);
312 for (i = 0; i < *argc; i++)
313 dprintf("CLI DEBUG: argv[%d] = %s\n", i, argv[0]);
314 return;
318 * parse_command_line - low level parser for the command line
319 * @line: command line to parse
320 * @command: first token in the line
321 * @module: second token in the line
322 * @argc: number of arguments
323 * @argv: array of arguments
325 * The format of the command line is:
326 * <main command> [<module on which to operate> [<args>]]
327 * command is always malloc'ed (even for an empty line)
329 static void parse_command_line(char *line, char **command, char **module,
330 int *argc, char **argv)
332 int argc_iter = 0, args_pos = 0, token_found = 0, token_len = 0;
333 int args_len = 0;
334 char *pch = NULL, *pch_next = NULL, *tmp_pch_next = NULL;
336 *command = NULL;
337 *module = NULL;
338 *argc = 0;
340 pch = line;
341 while (pch != NULL) {
342 pch_next = strchr(pch + 1, ' ');
343 tmp_pch_next = pch_next;
346 * Skip whitespaces if the user entered
347 * 'set mode foo' for 'set mode foo'
348 * ^ ^
349 * |___|___ pch
350 * |___ pch_next <- wrong!
352 * We still keep the position into tmp_pch_next to compute
353 * the lenght of the current token.
355 while (pch_next != NULL && !strncmp(pch_next, CLI_SPACE, 1))
356 pch_next++;
358 /* End of line guaranteed to be zeroed */
359 if (pch_next == NULL) {
360 token_len = (int)(strchr(pch + 1, '\0') - pch);
361 args_len = token_len;
362 } else {
363 token_len = (int)(tmp_pch_next - pch);
364 args_len = (int)(pch_next - pch);
367 if (token_found == 0) {
368 /* Main command to execute */
369 *command = malloc((token_len + 1) * sizeof(char));
370 strlcpy(*command, pch, token_len);
371 (*command)[token_len] = '\0';
372 dprintf("CLI DEBUG: command = %s\n", *command);
373 args_pos += args_len;
374 } else if (token_found == 1) {
375 /* Module */
376 *module = malloc((token_len + 1) * sizeof(char));
377 strlcpy(*module, pch, token_len);
378 (*module)[token_len] = '\0';
379 dprintf("CLI DEBUG: module = %s\n", *module);
380 args_pos += args_len;
381 } else
382 (*argc)++;
384 token_found++;
385 pch = pch_next;
387 dprintf("CLI DEBUG: argc = %d\n", *argc);
389 /* Skip arguments handling if none is supplied */
390 if (!*argc)
391 return;
393 /* Transform the arguments string into an array */
394 *argv = malloc(*argc * sizeof(char *));
395 pch = strtok(line + args_pos, CLI_SPACE);
396 while (pch != NULL) {
397 dprintf("CLI DEBUG: argv[%d] = %s\n", argc_iter, pch);
398 argv[argc_iter] = malloc(sizeof(pch) * sizeof(char));
399 strlcpy(argv[argc_iter], pch, sizeof(pch));
400 argc_iter++;
401 pch = strtok(NULL, CLI_SPACE);
403 * strtok(NULL, CLI_SPACE) over a stream of spaces
404 * will return an empty string
406 while (pch != NULL && !strncmp(pch, "", 1))
407 pch = strtok(NULL, CLI_SPACE);
412 * find_cli_callback_descr - find a callback in a list of modules
413 * @module_name: Name of the module to find
414 * @modules_list: Lits of modules among which to find @module_name
415 * @module_found: Pointer to the matched module, NULL if not found
417 * Given a module name and a list of possible modules, find the corresponding
418 * module structure that matches the module name and store it in @module_found.
420 void find_cli_callback_descr(const char *module_name,
421 struct cli_module_descr *modules_list,
422 struct cli_callback_descr **module_found)
424 int modules_iter = 0;
426 if (modules_list == NULL)
427 goto not_found;
429 /* Find the callback to execute */
430 while (modules_list->modules[modules_iter].name &&
431 strcmp(module_name, modules_list->modules[modules_iter].name) != 0)
432 modules_iter++;
434 if (modules_list->modules[modules_iter].name) {
435 *module_found = &(modules_list->modules[modules_iter]);
436 dprintf("CLI DEBUG: module %s found\n", (*module_found)->name);
437 return;
440 not_found:
441 *module_found = NULL;
442 return;
446 * autocomplete_command - print matching commands
447 * @command: Beginning of the command
449 * Given a string @command, print all availables commands starting with
450 * @command. Commands are found within the list of commands for the current
451 * mode and the hdt mode (if the current mode is not hdt).
453 static void autocomplete_command(char *command)
455 int j = 0;
456 struct cli_callback_descr *associated_module = NULL;
458 /* First take care of the two special commands: 'show' and 'set' */
459 if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
460 printf("%s\n", CLI_SHOW);
461 autocomplete_add_token_to_list(CLI_SHOW);
463 if (strncmp(CLI_SET, command, strlen(command)) == 0) {
464 printf("%s\n", CLI_SET);
465 autocomplete_add_token_to_list(CLI_SET);
469 * Then, go through the modes for the special case
470 * '<mode>' -> 'set mode <mode>'
472 while (list_modes[j]) {
473 if (strncmp(list_modes[j]->name, command, strlen(command)) == 0) {
474 printf("%s\n", list_modes[j]->name);
475 autocomplete_add_token_to_list(list_modes[j]->name);
477 j++;
481 * Let's go now through the list of default_modules for the current mode
482 * (single token commands for the current_mode)
484 j = 0;
485 if (current_mode->default_modules && current_mode->default_modules->modules) {
486 while (current_mode->default_modules->modules[j].name) {
487 if (strncmp(current_mode->default_modules->modules[j].name,
488 command, strlen(command)) == 0) {
489 printf("%s\n", current_mode->default_modules->modules[j].name);
490 autocomplete_add_token_to_list(current_mode->default_modules->
491 modules[j].name);
493 j++;
498 * Finally, if the current_mode is not hdt, list the available
499 * default_modules of hdt (these are always available from any mode).
501 if (current_mode->mode == HDT_MODE)
502 return;
504 if (!hdt_mode.default_modules || !hdt_mode.default_modules->modules)
505 return;
507 j = 0;
508 while (hdt_mode.default_modules &&
509 hdt_mode.default_modules->modules[j].name) {
511 * Any default command that is present in hdt mode but
512 * not in the current mode is available. A default
513 * command can be redefined in the current mode though.
514 * This next call tests this use case: if it is
515 * overwritten, do not print it again.
517 find_cli_callback_descr(hdt_mode.default_modules->modules[j].name,
518 current_mode->default_modules,
519 &associated_module);
520 if (associated_module == NULL &&
521 strncmp(command,
522 hdt_mode.default_modules->modules[j].name,
523 strlen(command)) == 0) {
524 printf("%s\n", hdt_mode.default_modules->modules[j].name);
525 autocomplete_add_token_to_list(hdt_mode.default_modules->modules[j].
526 name);
528 j++;
533 * autocomplete_module - print matching modules
534 * @command: Command on the command line (not NULL)
535 * @module: Beginning of the module
537 * Given a command @command and a string @module, print all availables modules
538 * starting with @module for command @command. Commands are found within the
539 * list of commands for the current mode and the hdt mode (if the current mode
540 * is not hdt).
542 static void autocomplete_module(char *command, char *module)
544 int j = 0;
545 char autocomplete_full_line[MAX_LINE_SIZE];
547 if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
548 if (!current_mode->show_modules || !current_mode->show_modules->modules)
549 return;
551 while (current_mode->show_modules->modules[j].name) {
552 if (strncmp(current_mode->show_modules->modules[j].name,
553 module, strlen(module)) == 0) {
554 printf("%s\n", current_mode->show_modules->modules[j].name);
555 sprintf(autocomplete_full_line, "%s %s",
556 CLI_SHOW, current_mode->show_modules->modules[j].name);
557 autocomplete_add_token_to_list(autocomplete_full_line);
559 j++;
561 } else if (strncmp(CLI_SET, command, strlen(command)) == 0) {
562 j = 0;
563 if (!current_mode->set_modules || !current_mode->set_modules->modules)
564 return;
566 while (current_mode->set_modules->modules[j].name) {
567 if (strncmp(current_mode->set_modules->modules[j].name,
568 module, strlen(module)) == 0) {
569 printf("%s\n", current_mode->set_modules->modules[j].name);
570 sprintf(autocomplete_full_line, "%s %s",
571 CLI_SET, current_mode->set_modules->modules[j].name);
572 autocomplete_add_token_to_list(autocomplete_full_line);
574 j++;
580 * autocomplete - find possible matches for a command line
581 * @line: command line to parse
583 static void autocomplete(char *line)
585 int i;
586 int argc = 0;
587 char *command = NULL, *module = NULL;
588 char **argv = NULL;
590 parse_command_line(line, &command, &module, &argc, argv);
592 /* If the user specified arguments, there is nothing we can complete */
593 if (argc != 0)
594 goto out;
596 /* No argument, (the start of) a module has been specified */
597 if (module != NULL) {
598 autocomplete_module(command, module);
599 free(module);
600 goto out;
603 /* No argument, no module, (the start of) a command has been specified */
604 if (command != NULL) {
605 autocomplete_command(command);
606 free(command);
607 goto out;
610 out:
611 /* Let's not forget to clean ourselves */
612 for (i = 0; i < argc; i++)
613 free(argv[i]);
614 if (argc > 0)
615 free(argv);
616 return;
620 * exec_command - main logic to map the command line to callbacks
622 static void exec_command(char *line, struct s_hardware *hardware)
624 int argc, i = 0;
625 char *command = NULL, *module = NULL;
626 char **argv = NULL;
627 struct cli_callback_descr *current_module = NULL;
629 /* This will allocate memory for command and module */
630 parse_command_line(line, &command, &module, &argc, argv);
633 * Expand shortcuts, if needed
634 * This will allocate memory for argc/argv
636 expand_aliases(line, &command, &module, &argc, argv);
638 if (module == NULL) {
639 dprintf("CLI DEBUG: single command detected\n");
641 * A single word was specified: look at the list of default
642 * commands in the current mode to see if there is a match.
643 * If not, it may be a generic function (exit, help, ...). These
644 * are stored in the list of default commands of the hdt mode.
646 find_cli_callback_descr(command, current_mode->default_modules,
647 &current_module);
648 if (current_module != NULL)
649 current_module->exec(argc, argv, hardware);
650 else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) &&
651 current_mode->show_modules != NULL &&
652 current_mode->show_modules->default_callback != NULL)
653 current_mode->show_modules->default_callback(argc, argv, hardware);
654 else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1) &&
655 current_mode->set_modules != NULL &&
656 current_mode->set_modules->default_callback != NULL)
657 current_mode->set_modules->default_callback(argc, argv, hardware);
658 else {
659 find_cli_callback_descr(command, hdt_mode.default_modules,
660 &current_module);
661 if (current_module != NULL)
662 current_module->exec(argc, argv, hardware);
663 else
664 printf("unknown command: '%s'\n", command);
666 } else {
668 * A module has been specified! We now need to find the type of command.
670 * The syntax of the cli is the following:
671 * <type of command> <module on which to operate> <args>
672 * e.g.
673 * dmi> show system
674 * dmi> show bank 1
675 * dmi> show memory 0 1
676 * pci> show device 12
677 * hdt> set mode dmi
679 if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) {
680 dprintf("CLI DEBUG: %s command detected\n", CLI_SHOW);
681 /* Look first for a 'show' callback in the current mode */
682 find_cli_callback_descr(module, current_mode->show_modules,
683 &current_module);
684 /* Execute the callback, if found */
685 if (current_module != NULL)
686 current_module->exec(argc, argv, hardware);
687 else {
688 /* Look now for a 'show' callback in the hdt mode */
689 find_cli_callback_descr(module, hdt_mode.show_modules,
690 &current_module);
691 /* Execute the callback, if found */
692 if (current_module != NULL)
693 current_module->exec(argc, argv, hardware);
694 else
695 printf("unknown module: '%s'\n", module);
697 } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) {
698 dprintf("CLI DEBUG: %s command detected\n", CLI_SET);
699 /* Look now for a 'set' callback in the hdt mode */
700 find_cli_callback_descr(module, current_mode->set_modules,
701 &current_module);
702 /* Execute the callback, if found */
703 if (current_module != NULL)
704 current_module->exec(argc, argv, hardware);
705 else {
706 /* Look now for a 'set' callback in the hdt mode */
707 find_cli_callback_descr(module, hdt_mode.set_modules,
708 &current_module);
709 /* Execute the callback, if found */
710 if (current_module != NULL)
711 current_module->exec(argc, argv, hardware);
712 else
713 printf("unknown module: '%s'\n", module);
718 /* Let's not forget to clean ourselves */
719 if (command != NULL)
720 free(command);
721 if (module != NULL)
722 free(module);
723 for (i = 0; i < argc; i++)
724 free(argv[i]);
725 if (argc > 0)
726 free(argv);
729 static void reset_prompt(void)
731 /* No need to display the prompt if we exit */
732 if (hdt_cli.mode != EXIT_MODE) {
733 printf("%s", hdt_cli.prompt);
734 /* Reset the line */
735 hdt_cli.cursor_pos = 0;
739 void start_auto_mode(struct s_hardware *hardware)
741 char *mypch;
742 int nb_commands = 0;
743 char *commands[MAX_NB_AUTO_COMMANDS];
745 if (!quiet)
746 more_printf("\nEntering Auto mode\n");
748 /* Protecting the auto_label from the strtok modifications */
749 char *temp = strdup(hardware->auto_label);
751 /* Searching & saving all commands */
752 mypch = strtok(temp, AUTO_SEPARATOR);
753 while (mypch != NULL) {
754 if ((strlen(remove_spaces(mypch)) > 0) &&
755 (remove_spaces(mypch)[0] != AUTO_SEPARATOR[0])) {
756 nb_commands++;
757 if ((commands[nb_commands] = malloc(AUTO_COMMAND_SIZE)) != NULL) {
758 sprintf(commands[nb_commands], "%s", remove_spaces(mypch));
759 } else
760 nb_commands--;
762 mypch = strtok(NULL, AUTO_SEPARATOR);
765 /* Executing found commands */
766 for (int i = 1; i <= nb_commands; i++) {
767 if (commands[i]) {
768 if (!quiet)
769 more_printf("%s%s\n", hdt_cli.prompt, commands[i]);
770 exec_command(commands[i], hardware);
771 free(commands[i]);
775 if (!quiet)
776 more_printf("\nExiting Auto mode\n");
778 more_printf("\n");
781 void print_history(int argc, char **argv, struct s_hardware * hardware)
783 (void)argc;
784 (void)argv;
785 (void)hardware;
787 reset_more_printf();
788 for (int i = 1; i <= MAX_HISTORY_SIZE; i++) {
789 if (i == hdt_cli.history_pos) {
790 more_printf("*%d:'%s'\n", i, hdt_cli.history[i]);
791 continue;
793 if (strlen(hdt_cli.history[i]) == 0)
794 continue;
795 more_printf(" %d:'%s'\n", i, hdt_cli.history[i]);
799 /* Code that manages the cli mode */
800 void start_cli_mode(struct s_hardware *hardware)
802 int current_key = 0;
803 int future_history_pos = 1; /* position of the next position in the history */
804 int current_future_history_pos = 1; /* Temp variable */
805 bool display_history = true; /* Temp Variable */
806 char temp_command[MAX_LINE_SIZE];
808 hdt_cli.cursor_pos = 0;
809 memset(hdt_cli.history, 0, sizeof(hdt_cli.history));
810 hdt_cli.history_pos = 1;
811 hdt_cli.max_history_pos = 1;
813 /* Find the mode selected */
814 set_mode(HDT_MODE, hardware);
815 find_cli_mode_descr(hdt_cli.mode, &current_mode);
816 if (current_mode == NULL) {
817 /* Shouldn't get here... */
818 printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
819 return;
822 /* Start the auto mode if the command line is set */
823 if (strlen(hardware->auto_label) > 0) {
824 start_auto_mode(hardware);
827 printf("Entering CLI mode\n");
829 reset_prompt();
831 while (hdt_cli.mode != EXIT_MODE) {
833 /* Display the cursor */
834 display_cursor(true);
836 /* Let's put the cursor blinking until we get an input */
837 set_cursor_blink(true);
839 /* We wait endlessly for a keyboard input */
840 current_key = get_key(stdin, 0);
842 /* We have to cancel the blinking mode to prevent
843 * input text to blink */
844 set_cursor_blink(false);
846 /* Reset autocomplete buffer unless TAB is pressed */
847 if (current_key != KEY_TAB)
848 autocomplete_destroy_list();
850 switch (current_key) {
851 /* clear until then end of line */
852 case KEY_CTRL('k'):
853 /* Clear the end of the line */
854 clear_end_of_line();
855 memset(&INPUT[hdt_cli.cursor_pos], 0,
856 strlen(INPUT) - hdt_cli.cursor_pos);
857 break;
859 case KEY_CTRL('c'):
860 printf("\n");
861 reset_prompt();
862 break;
864 case KEY_LEFT:
865 if (hdt_cli.cursor_pos > 0) {
866 move_cursor_left(1);
867 hdt_cli.cursor_pos--;
869 break;
871 case KEY_RIGHT:
872 if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
873 move_cursor_right(1);
874 hdt_cli.cursor_pos++;
876 break;
878 case KEY_CTRL('e'):
879 case KEY_END:
880 /* Calling with a 0 value will make the cursor move */
881 /* So, let's move the cursor only if needed */
882 if ((strlen(INPUT) - hdt_cli.cursor_pos) > 0) {
883 /* Return to the begining of line */
884 move_cursor_right(strlen(INPUT) - hdt_cli.cursor_pos);
885 hdt_cli.cursor_pos = strlen(INPUT);
887 break;
889 case KEY_CTRL('a'):
890 case KEY_HOME:
891 /* Calling with a 0 value will make the cursor move */
892 /* So, let's move the cursor only if needed */
893 if (hdt_cli.cursor_pos > 0) {
894 /* Return to the begining of line */
895 move_cursor_left(hdt_cli.cursor_pos);
896 hdt_cli.cursor_pos = 0;
898 break;
900 case KEY_UP:
902 /* Saving future position */
903 current_future_history_pos = future_history_pos;
905 /* We have to compute the next position */
906 if (future_history_pos == 1) {
907 future_history_pos = MAX_HISTORY_SIZE;
908 } else {
909 future_history_pos--;
912 /* Does the next position is valid */
913 if (strlen(hdt_cli.history[future_history_pos]) == 0) {
914 /* Position is invalid, restoring position */
915 future_history_pos = current_future_history_pos;
916 break;
919 /* Let's make that future position the one we use */
920 memset(INPUT, 0, sizeof(INPUT));
921 strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
923 /* Clear the line */
924 clear_line();
926 /* Move to the begining of line */
927 move_cursor_to_column(0);
929 reset_prompt();
930 printf("%s", INPUT);
931 hdt_cli.cursor_pos = strlen(INPUT);
932 break;
934 case KEY_DOWN:
935 display_history = true;
937 /* Saving future position */
938 current_future_history_pos = future_history_pos;
940 if (future_history_pos == MAX_HISTORY_SIZE) {
941 future_history_pos = 1;
942 } else {
943 future_history_pos++;
946 /* Does the next position is valid */
947 if (strlen(hdt_cli.history[future_history_pos]) == 0)
948 display_history = false;
950 /* An exception is made to reach the last empty line */
951 if (future_history_pos == hdt_cli.max_history_pos)
952 display_history = true;
954 if (display_history == false) {
955 /* Position is invalid, restoring position */
956 future_history_pos = current_future_history_pos;
957 break;
960 /* Let's make that future position the one we use */
961 memset(INPUT, 0, sizeof(INPUT));
962 strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
964 /* Clear the line */
965 clear_line();
967 /* Move to the begining of line */
968 move_cursor_to_column(0);
970 reset_prompt();
971 printf("%s", INPUT);
972 hdt_cli.cursor_pos = strlen(INPUT);
973 break;
975 case KEY_TAB:
976 if (autocomplete_backlog) {
977 clear_line();
978 /* Move to the begining of line */
979 move_cursor_to_column(0);
980 reset_prompt();
981 printf("%s", autocomplete_last_seen->autocomplete_token);
982 strlcpy(INPUT,
983 autocomplete_last_seen->autocomplete_token,
984 sizeof(INPUT));
985 hdt_cli.cursor_pos = strlen(INPUT);
987 /* Cycle through the list */
988 autocomplete_last_seen = autocomplete_last_seen->next;
989 if (autocomplete_last_seen == NULL)
990 autocomplete_last_seen = autocomplete_head;
991 } else {
992 printf("\n");
993 autocomplete(skip_spaces(INPUT));
994 autocomplete_last_seen = autocomplete_head;
996 printf("%s%s", hdt_cli.prompt, INPUT);
998 break;
1000 case KEY_ENTER:
1001 printf("\n");
1002 if (strlen(remove_spaces(INPUT)) < 1) {
1003 reset_prompt();
1004 break;
1006 exec_command(remove_spaces(INPUT), hardware);
1007 hdt_cli.history_pos++;
1009 /* Did we reach the end of the history ?*/
1010 if (hdt_cli.history_pos > MAX_HISTORY_SIZE) {
1011 /* Let's return at the beginning */
1012 hdt_cli.history_pos = 1;
1015 /* Does the next position is already used ?
1016 * If yes, we are cycling in history */
1017 if (strlen(INPUT) > 0) {
1018 /* Let's clean that entry */
1019 memset(&INPUT,0,sizeof(INPUT));
1022 future_history_pos = hdt_cli.history_pos;
1023 if (hdt_cli.history_pos > hdt_cli.max_history_pos)
1024 hdt_cli.max_history_pos = hdt_cli.history_pos;
1025 reset_prompt();
1026 break;
1028 case KEY_CTRL('d'):
1029 case KEY_DELETE:
1030 /* No need to delete when input is empty */
1031 if (strlen(INPUT) == 0)
1032 break;
1033 /* Don't delete when cursor is at the end of the line */
1034 if (hdt_cli.cursor_pos >= strlen(INPUT))
1035 break;
1037 for (int c = hdt_cli.cursor_pos; c < (int)strlen(INPUT) - 1; c++)
1038 INPUT[c] = INPUT[c + 1];
1039 INPUT[strlen(INPUT) - 1] = '\0';
1041 /* Clear the end of the line */
1042 clear_end_of_line();
1044 /* Print the resulting buffer */
1045 printf("%s", INPUT + hdt_cli.cursor_pos);
1047 /* Replace the cursor at the proper place */
1048 if (strlen(INPUT + hdt_cli.cursor_pos) > 0)
1049 move_cursor_left(strlen(INPUT + hdt_cli.cursor_pos));
1050 break;
1052 case KEY_DEL:
1053 case KEY_BACKSPACE:
1054 /* Don't delete prompt */
1055 if (hdt_cli.cursor_pos == 0)
1056 break;
1058 for (int c = hdt_cli.cursor_pos - 1;
1059 c < (int)strlen(INPUT) - 1; c++)
1060 INPUT[c] = INPUT[c + 1];
1061 INPUT[strlen(INPUT) - 1] = '\0';
1063 /* Get one char back */
1064 move_cursor_left(1);
1066 /* Clear the end of the line */
1067 clear_end_of_line();
1069 /* Print the resulting buffer */
1070 printf("%s", INPUT + hdt_cli.cursor_pos - 1);
1072 /* Realing to a char before the place we were */
1073 hdt_cli.cursor_pos--;
1074 move_cursor_to_column(strlen(hdt_cli.prompt) + hdt_cli.cursor_pos +
1077 break;
1079 case KEY_F1:
1080 printf("\n");
1081 exec_command(CLI_HELP, hardware);
1082 reset_prompt();
1083 break;
1085 default:
1086 if ((current_key < 0x20) || (current_key > 0x7e))
1087 break;
1088 /* Prevent overflow */
1089 if (hdt_cli.cursor_pos > MAX_LINE_SIZE - 2)
1090 break;
1091 /* If we aren't at the end of the input line, let's insert */
1092 if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
1093 char key[2];
1094 int trailing_chars = strlen(INPUT) - hdt_cli.cursor_pos;
1095 memset(temp_command, 0, sizeof(temp_command));
1096 strlcpy(temp_command, INPUT, hdt_cli.cursor_pos);
1097 sprintf(key, "%c", current_key);
1098 strncat(temp_command, key, 1);
1099 strncat(temp_command,
1100 INPUT + hdt_cli.cursor_pos, trailing_chars);
1101 memset(INPUT, 0, sizeof(INPUT));
1102 snprintf(INPUT, sizeof(INPUT), "%s", temp_command);
1104 /* Clear the end of the line */
1105 clear_end_of_line();
1107 /* Print the resulting buffer */
1108 printf("%s", INPUT + hdt_cli.cursor_pos);
1110 /* Return where we must put the new char */
1111 move_cursor_left(trailing_chars);
1113 } else {
1114 putchar(current_key);
1115 INPUT[hdt_cli.cursor_pos] = current_key;
1117 hdt_cli.cursor_pos++;
1118 break;