| /* ----------------------------------------------------------------------- * |
| * |
| * 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 <acpi/acpi.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, |
| &acpi_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) { |
| more_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: |
| 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); |
| break; |
| case CPU_MODE: |
| hdt_cli.mode = mode; |
| snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_CPU); |
| break; |
| case DMI_MODE: |
| if (!hardware->is_dmi_valid) { |
| more_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: |
| hdt_cli.mode = mode; |
| snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DISK); |
| break; |
| case VPD_MODE: |
| if (!hardware->is_vpd_valid) { |
| more_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; |
| case ACPI_MODE: |
| hdt_cli.mode = mode; |
| snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_ACPI); |
| break; |
| default: |
| /* Invalid mode */ |
| more_printf("Unknown mode, please choose among:\n"); |
| while (list_modes[i]) { |
| more_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... */ |
| more_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 parse: 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 parse: module = %s\n", *module); |
| args_pos += args_len; |
| } else |
| (*argc)++; |
| |
| token_found++; |
| pch = pch_next; |
| } |
| dprintf("CLI DEBUG parse: 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 parse: argv[%d] = %s\n", argc_iter, pch); |
| argv[argc_iter] = malloc(strlen(pch) * sizeof(char)); |
| strlcpy(argv[argc_iter], pch, strlen(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); |
| |
| dprintf("CLI DEBUG autocomplete: before checking args\n"); |
| /* 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); |
| |
| dprintf("CLI DEBUG exec: Checking for aliases\n"); |
| /* |
| * Expand shortcuts, if needed |
| * This will allocate memory for argc/argv |
| */ |
| expand_aliases(line, &command, &module, &argc, argv); |
| |
| find_cli_callback_descr(command, current_mode->default_modules, |
| ¤t_module); |
| |
| if ((module == NULL) || (current_module->nomodule == true)) { |
| dprintf("CLI DEBUG exec : 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. |
| */ |
| |
| /* First of all it the command doesn't need module, let's rework the arguments */ |
| if ((current_module->nomodule == true) && ( module != NULL)) { |
| dprintf("CLI_DEBUG exec: Reworking arguments with argc=%d\n",argc); |
| char **new_argv=NULL; |
| new_argv=malloc((argc + 2)*sizeof(char *)); |
| for (int argc_iter=0; argc_iter<argc; argc_iter++) { |
| dprintf("CLI_DEBUG exec rework : copy %d to %d (%s)\n",argc_iter,argc_iter+1,argv[argc_iter]); |
| new_argv[argc_iter+1] = malloc(strlen(argv[argc_iter])); |
| strlcpy(new_argv[argc_iter+1], argv[argc_iter], strlen(argv[argc_iter])); |
| free(argv[argc_iter]); |
| } |
| new_argv[0] = malloc(strlen(module)*sizeof(char)); |
| strlcpy(new_argv[0], module, strlen(module)); |
| argc++; |
| free(argv); |
| argv=new_argv; |
| } |
| |
| 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 |
| more_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 exec: %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 { |
| dprintf("CLI DEBUG exec: Looking for callback\n"); |
| /* 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 exec : %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]; |
| |
| 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); |
| } |
| |
| free(temp); |
| |
| /* 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... */ |
| more_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); |
| } |
| |
| more_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; |
| } |
| } |
| } |