/* ----------------------------------------------------------------------- *
*
* 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;
}
}
}