diff options
Diffstat (limited to 'contrib/syslinux/latest/com32/hdt/hdt-cli.c')
-rw-r--r-- | contrib/syslinux/latest/com32/hdt/hdt-cli.c | 1121 |
1 files changed, 1121 insertions, 0 deletions
diff --git a/contrib/syslinux/latest/com32/hdt/hdt-cli.c b/contrib/syslinux/latest/com32/hdt/hdt-cli.c new file mode 100644 index 0000000..639bcdb --- /dev/null +++ b/contrib/syslinux/latest/com32/hdt/hdt-cli.c @@ -0,0 +1,1121 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2009 Erwan Velu - All Rights Reserved + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall + * be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * ----------------------------------------------------------------------- + */ + +#include <stdlib.h> +#include <string.h> +#include <syslinux/config.h> +#include <getkey.h> +#include "hdt-cli.h" +#include "hdt-common.h" + +struct cli_mode_descr *list_modes[] = { + &hdt_mode, + &dmi_mode, + &syslinux_mode, + &pxe_mode, + &kernel_mode, + &cpu_mode, + &pci_mode, + &vesa_mode, + &disk_mode, + &vpd_mode, + &memory_mode, + NULL, +}; + +/* + * .aliases = {"q", "quit"} won't work since it is an array of pointers, not an + * array of variables. There is no easy way around it besides declaring the arrays of + * strings first. + */ +const char *exit_aliases[] = { "q", "quit" }; +const char *help_aliases[] = { "h", "?" }; + +/* List of aliases */ +struct cli_alias hdt_aliases[] = { + { + .command = CLI_EXIT, + .nb_aliases = 2, + .aliases = exit_aliases, + }, + { + .command = CLI_HELP, + .nb_aliases = 2, + .aliases = help_aliases, + }, +}; + +struct cli_mode_descr *current_mode; +int autocomplete_backlog; + +struct autocomplete_list { + char autocomplete_token[MAX_LINE_SIZE]; + struct autocomplete_list *next; +}; +struct autocomplete_list *autocomplete_head = NULL; +struct autocomplete_list *autocomplete_tail = NULL; +struct autocomplete_list *autocomplete_last_seen = NULL; + +static void autocomplete_add_token_to_list(const char *token) +{ + struct autocomplete_list *new = malloc(sizeof(struct autocomplete_list)); + + strlcpy(new->autocomplete_token, token, sizeof(new->autocomplete_token)); + new->next = NULL; + autocomplete_backlog++; + + if (autocomplete_tail != NULL) + autocomplete_tail->next = new; + if (autocomplete_head == NULL) + autocomplete_head = new; + autocomplete_tail = new; +} + +static void autocomplete_destroy_list(void) +{ + struct autocomplete_list *tmp = NULL; + + while (autocomplete_head != NULL) { + tmp = autocomplete_head->next; + free(autocomplete_head); + autocomplete_head = tmp; + } + autocomplete_backlog = 0; + autocomplete_tail = NULL; + autocomplete_last_seen = NULL; +} + +/** + * set_mode - set the current mode of the cli + * @mode: mode to set + * + * Unlike cli_set_mode, this function is not used by the cli directly. + **/ +void set_mode(cli_mode_t mode, struct s_hardware *hardware) +{ + int i = 0; + + switch (mode) { + case EXIT_MODE: + hdt_cli.mode = mode; + break; + case HDT_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_HDT); + break; + case PXE_MODE: + if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) { + printf("You are not currently using PXELINUX\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PXE); + break; + case KERNEL_MODE: + detect_pci(hardware); + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_KERNEL); + break; + case SYSLINUX_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_SYSLINUX); + break; + case VESA_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VESA); + break; + case PCI_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PCI); + if (!hardware->pci_detection) + cli_detect_pci(hardware); + break; + case CPU_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_CPU); + if (!hardware->dmi_detection) + detect_dmi(hardware); + if (!hardware->cpu_detection) + cpu_detect(hardware); + break; + case DMI_MODE: + detect_dmi(hardware); + if (!hardware->is_dmi_valid) { + printf("No valid DMI table found, exiting.\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DMI); + break; + case DISK_MODE: + detect_disks(hardware); + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DISK); + break; + case VPD_MODE: + detect_vpd(hardware); + if (!hardware->is_vpd_valid) { + printf("No valid VPD table found, exiting.\n"); + break; + } + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VPD); + break; + case MEMORY_MODE: + hdt_cli.mode = mode; + snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_MEMORY); + break; + default: + /* Invalid mode */ + printf("Unknown mode, please choose among:\n"); + while (list_modes[i]) { + printf("\t%s\n", list_modes[i]->name); + i++; + } + } + + find_cli_mode_descr(hdt_cli.mode, ¤t_mode); + /* There is not cli_mode_descr struct for the exit mode */ + if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) { + /* Shouldn't get here... */ + printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + } +} + +/** + * mode_s_to_mode_t - given a mode string, return the cli_mode_t representation + **/ +cli_mode_t mode_s_to_mode_t(char *name) +{ + int i = 0; + + while (list_modes[i]) { + if (!strncmp(name, list_modes[i]->name, sizeof(list_modes[i]->name))) + break; + i++; + } + + if (!list_modes[i]) + return INVALID_MODE; + else + return list_modes[i]->mode; +} + +/** + * find_cli_mode_descr - find the cli_mode_descr struct associated to a mode + * @mode: mode to look for + * @mode_found: store the mode if found, NULL otherwise + * + * Given a mode name, return a pointer to the associated cli_mode_descr + * structure. + * Note: the current mode name is stored in hdt_cli.mode. + **/ +void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found) +{ + int i = 0; + + while (list_modes[i] && list_modes[i]->mode != mode) + i++; + + /* Shouldn't get here... */ + if (!list_modes[i]) + *mode_found = NULL; + else + *mode_found = list_modes[i]; +} + +/** + * expand_aliases - resolve aliases mapping + * @line: command line to parse + * @command: first token in the line + * @module: second token in the line + * @argc: number of arguments + * @argv: array of arguments + * + * We maintain a small list of static alises to enhance user experience. + * Only commands can be aliased (first token). Otherwise it can become really hairy... + **/ +static void expand_aliases(char *line __unused, char **command, char **module, + int *argc, char **argv) +{ + struct cli_mode_descr *mode; + int i, j; + + find_cli_mode_descr(mode_s_to_mode_t(*command), &mode); + if (mode != NULL && *module == NULL) { + /* + * The user specified a mode instead of `set mode...', e.g. + * `dmi' instead of `set mode dmi' + */ + + /* *argv is NULL since *module is NULL */ + *argc = 1; + *argv = malloc(*argc * sizeof(char *)); + argv[0] = malloc((sizeof(*command) + 1) * sizeof(char)); + strlcpy(argv[0], *command, sizeof(*command) + 1); + dprintf("CLI DEBUG: ALIAS %s ", *command); + + strlcpy(*command, CLI_SET, sizeof(CLI_SET)); /* set */ + + *module = malloc(sizeof(CLI_MODE) * sizeof(char)); + strlcpy(*module, CLI_MODE, sizeof(CLI_MODE)); /* mode */ + + dprintf("--> %s %s %s\n", *command, *module, argv[0]); + goto out; + } + + /* Simple aliases mapping a single command to another one */ + for (i = 0; i < MAX_ALIASES; i++) { + for (j = 0; j < hdt_aliases[i].nb_aliases; j++) { + if (!strncmp(*command, hdt_aliases[i].aliases[j], + sizeof(hdt_aliases[i].aliases[j]))) { + dprintf("CLI DEBUG: ALIAS %s ", *command); + strlcpy(*command, hdt_aliases[i].command, + sizeof(hdt_aliases[i].command) + 1); + dprintf("--> %s\n", *command); + goto out; /* Don't allow chaining aliases */ + } + } + } + return; + +out: + dprintf("CLI DEBUG: New parameters:\n"); + dprintf("CLI DEBUG: command = %s\n", *command); + dprintf("CLI DEBUG: module = %s\n", *module); + dprintf("CLI DEBUG: argc = %d\n", *argc); + for (i = 0; i < *argc; i++) + dprintf("CLI DEBUG: argv[%d] = %s\n", i, argv[0]); + return; +} + +/** + * parse_command_line - low level parser for the command line + * @line: command line to parse + * @command: first token in the line + * @module: second token in the line + * @argc: number of arguments + * @argv: array of arguments + * + * The format of the command line is: + * <main command> [<module on which to operate> [<args>]] + * command is always malloc'ed (even for an empty line) + **/ +static void parse_command_line(char *line, char **command, char **module, + int *argc, char **argv) +{ + int argc_iter = 0, args_pos = 0, token_found = 0, token_len = 0; + int args_len = 0; + char *pch = NULL, *pch_next = NULL, *tmp_pch_next = NULL; + + *command = NULL; + *module = NULL; + *argc = 0; + + pch = line; + while (pch != NULL) { + pch_next = strchr(pch + 1, ' '); + tmp_pch_next = pch_next; + + /* + * Skip whitespaces if the user entered + * 'set mode foo' for 'set mode foo' + * ^ ^ + * |___|___ pch + * |___ pch_next <- wrong! + * + * We still keep the position into tmp_pch_next to compute + * the lenght of the current token. + */ + while (pch_next != NULL && !strncmp(pch_next, CLI_SPACE, 1)) + pch_next++; + + /* End of line guaranteed to be zeroed */ + if (pch_next == NULL) { + token_len = (int)(strchr(pch + 1, '\0') - pch); + args_len = token_len; + } else { + token_len = (int)(tmp_pch_next - pch); + args_len = (int)(pch_next - pch); + } + + if (token_found == 0) { + /* Main command to execute */ + *command = malloc((token_len + 1) * sizeof(char)); + strlcpy(*command, pch, token_len); + (*command)[token_len] = '\0'; + dprintf("CLI DEBUG: command = %s\n", *command); + args_pos += args_len; + } else if (token_found == 1) { + /* Module */ + *module = malloc((token_len + 1) * sizeof(char)); + strlcpy(*module, pch, token_len); + (*module)[token_len] = '\0'; + dprintf("CLI DEBUG: module = %s\n", *module); + args_pos += args_len; + } else + (*argc)++; + + token_found++; + pch = pch_next; + } + dprintf("CLI DEBUG: argc = %d\n", *argc); + + /* Skip arguments handling if none is supplied */ + if (!*argc) + return; + + /* Transform the arguments string into an array */ + *argv = malloc(*argc * sizeof(char *)); + pch = strtok(line + args_pos, CLI_SPACE); + while (pch != NULL) { + dprintf("CLI DEBUG: argv[%d] = %s\n", argc_iter, pch); + argv[argc_iter] = malloc(sizeof(pch) * sizeof(char)); + strlcpy(argv[argc_iter], pch, sizeof(pch)); + argc_iter++; + pch = strtok(NULL, CLI_SPACE); + /* + * strtok(NULL, CLI_SPACE) over a stream of spaces + * will return an empty string + */ + while (pch != NULL && !strncmp(pch, "", 1)) + pch = strtok(NULL, CLI_SPACE); + } +} + +/** + * find_cli_callback_descr - find a callback in a list of modules + * @module_name: Name of the module to find + * @modules_list: Lits of modules among which to find @module_name + * @module_found: Pointer to the matched module, NULL if not found + * + * Given a module name and a list of possible modules, find the corresponding + * module structure that matches the module name and store it in @module_found. + **/ +void find_cli_callback_descr(const char *module_name, + struct cli_module_descr *modules_list, + struct cli_callback_descr **module_found) +{ + int modules_iter = 0; + + if (modules_list == NULL) + goto not_found; + + /* Find the callback to execute */ + while (modules_list->modules[modules_iter].name && + strcmp(module_name, modules_list->modules[modules_iter].name) != 0) + modules_iter++; + + if (modules_list->modules[modules_iter].name) { + *module_found = &(modules_list->modules[modules_iter]); + dprintf("CLI DEBUG: module %s found\n", (*module_found)->name); + return; + } + +not_found: + *module_found = NULL; + return; +} + +/** + * autocomplete_command - print matching commands + * @command: Beginning of the command + * + * Given a string @command, print all availables commands starting with + * @command. Commands are found within the list of commands for the current + * mode and the hdt mode (if the current mode is not hdt). + **/ +static void autocomplete_command(char *command) +{ + int j = 0; + struct cli_callback_descr *associated_module = NULL; + + /* First take care of the two special commands: 'show' and 'set' */ + if (strncmp(CLI_SHOW, command, strlen(command)) == 0) { + printf("%s\n", CLI_SHOW); + autocomplete_add_token_to_list(CLI_SHOW); + } + if (strncmp(CLI_SET, command, strlen(command)) == 0) { + printf("%s\n", CLI_SET); + autocomplete_add_token_to_list(CLI_SET); + } + + /* + * Then, go through the modes for the special case + * '<mode>' -> 'set mode <mode>' + */ + while (list_modes[j]) { + if (strncmp(list_modes[j]->name, command, strlen(command)) == 0) { + printf("%s\n", list_modes[j]->name); + autocomplete_add_token_to_list(list_modes[j]->name); + } + j++; + } + + /* + * Let's go now through the list of default_modules for the current mode + * (single token commands for the current_mode) + */ + j = 0; + if (current_mode->default_modules && current_mode->default_modules->modules) { + while (current_mode->default_modules->modules[j].name) { + if (strncmp(current_mode->default_modules->modules[j].name, + command, strlen(command)) == 0) { + printf("%s\n", current_mode->default_modules->modules[j].name); + autocomplete_add_token_to_list(current_mode->default_modules-> + modules[j].name); + } + j++; + } + } + + /* + * Finally, if the current_mode is not hdt, list the available + * default_modules of hdt (these are always available from any mode). + */ + if (current_mode->mode == HDT_MODE) + return; + + if (!hdt_mode.default_modules || !hdt_mode.default_modules->modules) + return; + + j = 0; + while (hdt_mode.default_modules && + hdt_mode.default_modules->modules[j].name) { + /* + * Any default command that is present in hdt mode but + * not in the current mode is available. A default + * command can be redefined in the current mode though. + * This next call tests this use case: if it is + * overwritten, do not print it again. + */ + find_cli_callback_descr(hdt_mode.default_modules->modules[j].name, + current_mode->default_modules, + &associated_module); + if (associated_module == NULL && + strncmp(command, + hdt_mode.default_modules->modules[j].name, + strlen(command)) == 0) { + printf("%s\n", hdt_mode.default_modules->modules[j].name); + autocomplete_add_token_to_list(hdt_mode.default_modules->modules[j]. + name); + } + j++; + } +} + +/** + * autocomplete_module - print matching modules + * @command: Command on the command line (not NULL) + * @module: Beginning of the module + * + * Given a command @command and a string @module, print all availables modules + * starting with @module for command @command. Commands are found within the + * list of commands for the current mode and the hdt mode (if the current mode + * is not hdt). + **/ +static void autocomplete_module(char *command, char *module) +{ + int j = 0; + char autocomplete_full_line[MAX_LINE_SIZE]; + + if (strncmp(CLI_SHOW, command, strlen(command)) == 0) { + if (!current_mode->show_modules || !current_mode->show_modules->modules) + return; + + while (current_mode->show_modules->modules[j].name) { + if (strncmp(current_mode->show_modules->modules[j].name, + module, strlen(module)) == 0) { + printf("%s\n", current_mode->show_modules->modules[j].name); + sprintf(autocomplete_full_line, "%s %s", + CLI_SHOW, current_mode->show_modules->modules[j].name); + autocomplete_add_token_to_list(autocomplete_full_line); + } + j++; + } + } else if (strncmp(CLI_SET, command, strlen(command)) == 0) { + j = 0; + if (!current_mode->set_modules || !current_mode->set_modules->modules) + return; + + while (current_mode->set_modules->modules[j].name) { + if (strncmp(current_mode->set_modules->modules[j].name, + module, strlen(module)) == 0) { + printf("%s\n", current_mode->set_modules->modules[j].name); + sprintf(autocomplete_full_line, "%s %s", + CLI_SET, current_mode->set_modules->modules[j].name); + autocomplete_add_token_to_list(autocomplete_full_line); + } + j++; + } + } +} + +/** + * autocomplete - find possible matches for a command line + * @line: command line to parse + **/ +static void autocomplete(char *line) +{ + int i; + int argc = 0; + char *command = NULL, *module = NULL; + char **argv = NULL; + + parse_command_line(line, &command, &module, &argc, argv); + + /* If the user specified arguments, there is nothing we can complete */ + if (argc != 0) + goto out; + + /* No argument, (the start of) a module has been specified */ + if (module != NULL) { + autocomplete_module(command, module); + free(module); + goto out; + } + + /* No argument, no module, (the start of) a command has been specified */ + if (command != NULL) { + autocomplete_command(command); + free(command); + goto out; + } + +out: + /* Let's not forget to clean ourselves */ + for (i = 0; i < argc; i++) + free(argv[i]); + if (argc > 0) + free(argv); + return; +} + +/** + * exec_command - main logic to map the command line to callbacks + **/ +static void exec_command(char *line, struct s_hardware *hardware) +{ + int argc, i = 0; + char *command = NULL, *module = NULL; + char **argv = NULL; + struct cli_callback_descr *current_module = NULL; + + /* This will allocate memory for command and module */ + parse_command_line(line, &command, &module, &argc, argv); + + /* + * Expand shortcuts, if needed + * This will allocate memory for argc/argv + */ + expand_aliases(line, &command, &module, &argc, argv); + + if (module == NULL) { + dprintf("CLI DEBUG: single command detected\n"); + /* + * A single word was specified: look at the list of default + * commands in the current mode to see if there is a match. + * If not, it may be a generic function (exit, help, ...). These + * are stored in the list of default commands of the hdt mode. + */ + find_cli_callback_descr(command, current_mode->default_modules, + ¤t_module); + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) && + current_mode->show_modules != NULL && + current_mode->show_modules->default_callback != NULL) + current_mode->show_modules->default_callback(argc, argv, hardware); + else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1) && + current_mode->set_modules != NULL && + current_mode->set_modules->default_callback != NULL) + current_mode->set_modules->default_callback(argc, argv, hardware); + else { + find_cli_callback_descr(command, hdt_mode.default_modules, + ¤t_module); + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else + printf("unknown command: '%s'\n", command); + } + } else { + /* + * A module has been specified! We now need to find the type of command. + * + * The syntax of the cli is the following: + * <type of command> <module on which to operate> <args> + * e.g. + * dmi> show system + * dmi> show bank 1 + * dmi> show memory 0 1 + * pci> show device 12 + * hdt> set mode dmi + */ + if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) { + dprintf("CLI DEBUG: %s command detected\n", CLI_SHOW); + /* Look first for a 'show' callback in the current mode */ + find_cli_callback_descr(module, current_mode->show_modules, + ¤t_module); + /* Execute the callback, if found */ + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else { + /* Look now for a 'show' callback in the hdt mode */ + find_cli_callback_descr(module, hdt_mode.show_modules, + ¤t_module); + /* Execute the callback, if found */ + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else + printf("unknown module: '%s'\n", module); + } + } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) { + dprintf("CLI DEBUG: %s command detected\n", CLI_SET); + /* Look now for a 'set' callback in the hdt mode */ + find_cli_callback_descr(module, current_mode->set_modules, + ¤t_module); + /* Execute the callback, if found */ + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else { + /* Look now for a 'set' callback in the hdt mode */ + find_cli_callback_descr(module, hdt_mode.set_modules, + ¤t_module); + /* Execute the callback, if found */ + if (current_module != NULL) + current_module->exec(argc, argv, hardware); + else + printf("unknown module: '%s'\n", module); + } + } + } + + /* Let's not forget to clean ourselves */ + if (command != NULL) + free(command); + if (module != NULL) + free(module); + for (i = 0; i < argc; i++) + free(argv[i]); + if (argc > 0) + free(argv); +} + +static void reset_prompt(void) +{ + /* No need to display the prompt if we exit */ + if (hdt_cli.mode != EXIT_MODE) { + printf("%s", hdt_cli.prompt); + /* Reset the line */ + hdt_cli.cursor_pos = 0; + } +} + +void start_auto_mode(struct s_hardware *hardware) +{ + char *mypch; + int nb_commands = 0; + char *commands[MAX_NB_AUTO_COMMANDS]; + + if (!quiet) + more_printf("\nEntering Auto mode\n"); + + /* Protecting the auto_label from the strtok modifications */ + char *temp = strdup(hardware->auto_label); + + /* Searching & saving all commands */ + mypch = strtok(temp, AUTO_SEPARATOR); + while (mypch != NULL) { + if ((strlen(remove_spaces(mypch)) > 0) && + (remove_spaces(mypch)[0] != AUTO_SEPARATOR[0])) { + nb_commands++; + if ((commands[nb_commands] = malloc(AUTO_COMMAND_SIZE)) != NULL) { + sprintf(commands[nb_commands], "%s", remove_spaces(mypch)); + } else + nb_commands--; + } + mypch = strtok(NULL, AUTO_SEPARATOR); + } + + /* Executing found commands */ + for (int i = 1; i <= nb_commands; i++) { + if (commands[i]) { + if (!quiet) + more_printf("%s%s\n", hdt_cli.prompt, commands[i]); + exec_command(commands[i], hardware); + free(commands[i]); + } + } + + if (!quiet) + more_printf("\nExiting Auto mode\n"); + + more_printf("\n"); +} + +void print_history(int argc, char **argv, struct s_hardware * hardware) +{ + (void)argc; + (void)argv; + (void)hardware; + + reset_more_printf(); + for (int i = 1; i <= MAX_HISTORY_SIZE; i++) { + if (i == hdt_cli.history_pos) { + more_printf("*%d:'%s'\n", i, hdt_cli.history[i]); + continue; + } + if (strlen(hdt_cli.history[i]) == 0) + continue; + more_printf(" %d:'%s'\n", i, hdt_cli.history[i]); + } +} + +/* Code that manages the cli mode */ +void start_cli_mode(struct s_hardware *hardware) +{ + int current_key = 0; + int future_history_pos = 1; /* position of the next position in the history */ + int current_future_history_pos = 1; /* Temp variable */ + bool display_history = true; /* Temp Variable */ + char temp_command[MAX_LINE_SIZE]; + + hdt_cli.cursor_pos = 0; + memset(hdt_cli.history, 0, sizeof(hdt_cli.history)); + hdt_cli.history_pos = 1; + hdt_cli.max_history_pos = 1; + + /* Find the mode selected */ + set_mode(HDT_MODE, hardware); + find_cli_mode_descr(hdt_cli.mode, ¤t_mode); + if (current_mode == NULL) { + /* Shouldn't get here... */ + printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode); + return; + } + + /* Start the auto mode if the command line is set */ + if (strlen(hardware->auto_label) > 0) { + start_auto_mode(hardware); + } + + printf("Entering CLI mode\n"); + + reset_prompt(); + + while (hdt_cli.mode != EXIT_MODE) { + + /* Display the cursor */ + display_cursor(true); + + /* Let's put the cursor blinking until we get an input */ + set_cursor_blink(true); + + /* We wait endlessly for a keyboard input */ + current_key = get_key(stdin, 0); + + /* We have to cancel the blinking mode to prevent + * input text to blink */ + set_cursor_blink(false); + + /* Reset autocomplete buffer unless TAB is pressed */ + if (current_key != KEY_TAB) + autocomplete_destroy_list(); + + switch (current_key) { + /* clear until then end of line */ + case KEY_CTRL('k'): + /* Clear the end of the line */ + clear_end_of_line(); + memset(&INPUT[hdt_cli.cursor_pos], 0, + strlen(INPUT) - hdt_cli.cursor_pos); + break; + + case KEY_CTRL('c'): + printf("\n"); + reset_prompt(); + break; + + case KEY_LEFT: + if (hdt_cli.cursor_pos > 0) { + move_cursor_left(1); + hdt_cli.cursor_pos--; + } + break; + + case KEY_RIGHT: + if (hdt_cli.cursor_pos < (int)strlen(INPUT)) { + move_cursor_right(1); + hdt_cli.cursor_pos++; + } + break; + + case KEY_CTRL('e'): + case KEY_END: + /* Calling with a 0 value will make the cursor move */ + /* So, let's move the cursor only if needed */ + if ((strlen(INPUT) - hdt_cli.cursor_pos) > 0) { + /* Return to the begining of line */ + move_cursor_right(strlen(INPUT) - hdt_cli.cursor_pos); + hdt_cli.cursor_pos = strlen(INPUT); + } + break; + + case KEY_CTRL('a'): + case KEY_HOME: + /* Calling with a 0 value will make the cursor move */ + /* So, let's move the cursor only if needed */ + if (hdt_cli.cursor_pos > 0) { + /* Return to the begining of line */ + move_cursor_left(hdt_cli.cursor_pos); + hdt_cli.cursor_pos = 0; + } + break; + + case KEY_UP: + + /* Saving future position */ + current_future_history_pos = future_history_pos; + + /* We have to compute the next position */ + if (future_history_pos == 1) { + future_history_pos = MAX_HISTORY_SIZE; + } else { + future_history_pos--; + } + + /* Does the next position is valid */ + if (strlen(hdt_cli.history[future_history_pos]) == 0) { + /* Position is invalid, restoring position */ + future_history_pos = current_future_history_pos; + break; + } + + /* Let's make that future position the one we use */ + memset(INPUT, 0, sizeof(INPUT)); + strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT)); + + /* Clear the line */ + clear_line(); + + /* Move to the begining of line */ + move_cursor_to_column(0); + + reset_prompt(); + printf("%s", INPUT); + hdt_cli.cursor_pos = strlen(INPUT); + break; + + case KEY_DOWN: + display_history = true; + + /* Saving future position */ + current_future_history_pos = future_history_pos; + + if (future_history_pos == MAX_HISTORY_SIZE) { + future_history_pos = 1; + } else { + future_history_pos++; + } + + /* Does the next position is valid */ + if (strlen(hdt_cli.history[future_history_pos]) == 0) + display_history = false; + + /* An exception is made to reach the last empty line */ + if (future_history_pos == hdt_cli.max_history_pos) + display_history = true; + + if (display_history == false) { + /* Position is invalid, restoring position */ + future_history_pos = current_future_history_pos; + break; + } + + /* Let's make that future position the one we use */ + memset(INPUT, 0, sizeof(INPUT)); + strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT)); + + /* Clear the line */ + clear_line(); + + /* Move to the begining of line */ + move_cursor_to_column(0); + + reset_prompt(); + printf("%s", INPUT); + hdt_cli.cursor_pos = strlen(INPUT); + break; + + case KEY_TAB: + if (autocomplete_backlog) { + clear_line(); + /* Move to the begining of line */ + move_cursor_to_column(0); + reset_prompt(); + printf("%s", autocomplete_last_seen->autocomplete_token); + strlcpy(INPUT, + autocomplete_last_seen->autocomplete_token, + sizeof(INPUT)); + hdt_cli.cursor_pos = strlen(INPUT); + + /* Cycle through the list */ + autocomplete_last_seen = autocomplete_last_seen->next; + if (autocomplete_last_seen == NULL) + autocomplete_last_seen = autocomplete_head; + } else { + printf("\n"); + autocomplete(skip_spaces(INPUT)); + autocomplete_last_seen = autocomplete_head; + + printf("%s%s", hdt_cli.prompt, INPUT); + } + break; + + case KEY_ENTER: + printf("\n"); + if (strlen(remove_spaces(INPUT)) < 1) { + reset_prompt(); + break; + } + exec_command(remove_spaces(INPUT), hardware); + hdt_cli.history_pos++; + + /* Did we reach the end of the history ?*/ + if (hdt_cli.history_pos > MAX_HISTORY_SIZE) { + /* Let's return at the beginning */ + hdt_cli.history_pos = 1; + } + + /* Does the next position is already used ? + * If yes, we are cycling in history */ + if (strlen(INPUT) > 0) { + /* Let's clean that entry */ + memset(&INPUT,0,sizeof(INPUT)); + } + + future_history_pos = hdt_cli.history_pos; + if (hdt_cli.history_pos > hdt_cli.max_history_pos) + hdt_cli.max_history_pos = hdt_cli.history_pos; + reset_prompt(); + break; + + case KEY_CTRL('d'): + case KEY_DELETE: + /* No need to delete when input is empty */ + if (strlen(INPUT) == 0) + break; + /* Don't delete when cursor is at the end of the line */ + if (hdt_cli.cursor_pos >= strlen(INPUT)) + break; + + for (int c = hdt_cli.cursor_pos; c < (int)strlen(INPUT) - 1; c++) + INPUT[c] = INPUT[c + 1]; + INPUT[strlen(INPUT) - 1] = '\0'; + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", INPUT + hdt_cli.cursor_pos); + + /* Replace the cursor at the proper place */ + if (strlen(INPUT + hdt_cli.cursor_pos) > 0) + move_cursor_left(strlen(INPUT + hdt_cli.cursor_pos)); + break; + + case KEY_DEL: + case KEY_BACKSPACE: + /* Don't delete prompt */ + if (hdt_cli.cursor_pos == 0) + break; + + for (int c = hdt_cli.cursor_pos - 1; + c < (int)strlen(INPUT) - 1; c++) + INPUT[c] = INPUT[c + 1]; + INPUT[strlen(INPUT) - 1] = '\0'; + + /* Get one char back */ + move_cursor_left(1); + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", INPUT + hdt_cli.cursor_pos - 1); + + /* Realing to a char before the place we were */ + hdt_cli.cursor_pos--; + move_cursor_to_column(strlen(hdt_cli.prompt) + hdt_cli.cursor_pos + + 1); + + break; + + case KEY_F1: + printf("\n"); + exec_command(CLI_HELP, hardware); + reset_prompt(); + break; + + default: + if ((current_key < 0x20) || (current_key > 0x7e)) + break; + /* Prevent overflow */ + if (hdt_cli.cursor_pos > MAX_LINE_SIZE - 2) + break; + /* If we aren't at the end of the input line, let's insert */ + if (hdt_cli.cursor_pos < (int)strlen(INPUT)) { + char key[2]; + int trailing_chars = strlen(INPUT) - hdt_cli.cursor_pos; + memset(temp_command, 0, sizeof(temp_command)); + strlcpy(temp_command, INPUT, hdt_cli.cursor_pos); + sprintf(key, "%c", current_key); + strncat(temp_command, key, 1); + strncat(temp_command, + INPUT + hdt_cli.cursor_pos, trailing_chars); + memset(INPUT, 0, sizeof(INPUT)); + snprintf(INPUT, sizeof(INPUT), "%s", temp_command); + + /* Clear the end of the line */ + clear_end_of_line(); + + /* Print the resulting buffer */ + printf("%s", INPUT + hdt_cli.cursor_pos); + + /* Return where we must put the new char */ + move_cursor_left(trailing_chars); + + } else { + putchar(current_key); + INPUT[hdt_cli.cursor_pos] = current_key; + } + hdt_cli.cursor_pos++; + break; + } + } +} |