1142 lines
32 KiB
C
1142 lines
32 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* 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;
|
|
}
|
|
}
|
|
}
|