|
|
|
@ -14,15 +14,19 @@ console_t CONSOLE;
|
|
|
|
|
|
|
|
|
|
void consoleInit() {
|
|
|
|
|
memoryZero(&CONSOLE, sizeof(console_t));
|
|
|
|
|
pthread_mutex_init(&CONSOLE.lock, NULL); // Initialize the mutex
|
|
|
|
|
|
|
|
|
|
// Register the get and set command.
|
|
|
|
|
CONSOLE.cmdGet = consoleRegCmd("get", consoleCmdGet);
|
|
|
|
|
CONSOLE.cmdSet = consoleRegCmd("set", consoleCmdSet);
|
|
|
|
|
CONSOLE.cmdGet = consoleRegCmd("get", cmdGet);
|
|
|
|
|
CONSOLE.cmdSet = consoleRegCmd("set", cmdSet);
|
|
|
|
|
consoleRegCmd("echo", cmdEcho);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
|
|
|
|
|
pthread_mutex_lock(&CONSOLE.lock); // Lock
|
|
|
|
|
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
|
|
|
|
|
consoleCmdInit(cmd, name, function);
|
|
|
|
|
pthread_mutex_unlock(&CONSOLE.lock); // Unlock
|
|
|
|
|
return cmd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -31,8 +35,10 @@ consolevar_t * consoleRegVar(
|
|
|
|
|
const char_t *value,
|
|
|
|
|
consolevarchanged_t event
|
|
|
|
|
) {
|
|
|
|
|
pthread_mutex_lock(&CONSOLE.lock); // Lock
|
|
|
|
|
consolevar_t *var = &CONSOLE.variables[CONSOLE.variableCount++];
|
|
|
|
|
consoleVarInitListener(var, name, value, event);
|
|
|
|
|
pthread_mutex_unlock(&CONSOLE.lock); // Unlock
|
|
|
|
|
return var;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -62,163 +68,245 @@ void consolePrint(const char_t *message, ...) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void consoleExec(const char_t *line) {
|
|
|
|
|
pthread_mutex_lock(&CONSOLE.lock); // Lock
|
|
|
|
|
assertNotNull(line, "line must not be NULL");
|
|
|
|
|
assertTrue(
|
|
|
|
|
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
|
|
|
|
|
"Too many commands in the buffer."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if(line[0] == '\0') return;
|
|
|
|
|
|
|
|
|
|
char_t buffer[CONSOLE_LINE_MAX + 1];
|
|
|
|
|
size_t i = 0, j = 0;
|
|
|
|
|
char_t c;
|
|
|
|
|
consoleexecstate_t state = CONSOLE_EXEC_STATE_INITIAL;
|
|
|
|
|
consolecmdexec_t *exec = NULL;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
c = line[i++];
|
|
|
|
|
while(state != CONSOLE_EXEC_STATE_FULLY_PARSED) {
|
|
|
|
|
c = line[i];
|
|
|
|
|
|
|
|
|
|
// Handle command separation by semicolon or end of line
|
|
|
|
|
if(c == ';' || c == '\0') {
|
|
|
|
|
// Null-terminate the current command and trim whitespace
|
|
|
|
|
buffer[j] = '\0';
|
|
|
|
|
stringTrim(buffer);
|
|
|
|
|
switch(state) {
|
|
|
|
|
case CONSOLE_EXEC_STATE_INITIAL:
|
|
|
|
|
assertTrue(j == 0, "Buffer not empty?");
|
|
|
|
|
|
|
|
|
|
// Skip empty commands
|
|
|
|
|
if(buffer[0] == '\0') {
|
|
|
|
|
j = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure the exec buffer is not full
|
|
|
|
|
assertTrue(
|
|
|
|
|
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
|
|
|
|
|
"Too many commands in the buffer."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Create a new command execution
|
|
|
|
|
consolecmdexec_t *exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount++];
|
|
|
|
|
memoryZero(exec, sizeof(consolecmdexec_t));
|
|
|
|
|
|
|
|
|
|
// Parse command and arguments
|
|
|
|
|
char_t *token = strtok(buffer, " ");
|
|
|
|
|
while(token != NULL) {
|
|
|
|
|
assertTrue(
|
|
|
|
|
exec->argc < CONSOLE_CMD_ARGC_MAX,
|
|
|
|
|
"Too many arguments in the command."
|
|
|
|
|
);
|
|
|
|
|
stringCopy(exec->argv[exec->argc++], token, CONSOLE_LINE_MAX);
|
|
|
|
|
token = strtok(NULL, " ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// First, see if there's a matching command.
|
|
|
|
|
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
|
|
|
|
|
if(stringCompare(CONSOLE.commands[k].name, exec->argv[0]) != 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
exec->cmd = &CONSOLE.commands[k];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If match found continue
|
|
|
|
|
if(exec->cmd != NULL) {
|
|
|
|
|
j = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no match found, see if there's a matching variable.
|
|
|
|
|
consolevar_t *var = NULL;
|
|
|
|
|
|
|
|
|
|
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
|
|
|
|
|
if(stringCompare(CONSOLE.variables[k].name, exec->argv[0]) != 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var = &CONSOLE.variables[k];
|
|
|
|
|
if(c == '\0') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If no variable found, error out.
|
|
|
|
|
if(!var) {
|
|
|
|
|
consolePrint("Error: Command/Variable '%s' not found.", exec->argv[0]);
|
|
|
|
|
CONSOLE.execBufferCount--;
|
|
|
|
|
j = 0;
|
|
|
|
|
if(stringIsWhitespace(c) || c == ';') {
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Found a matching variable, we basically change this entire command
|
|
|
|
|
// to a get or a set. Arguments are reversed so they aren't stepped over
|
|
|
|
|
if(exec->argc == 1) {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_PARSE_CMD;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_PARSE_CMD:
|
|
|
|
|
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_CMD_PARSED;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(c == '"') {
|
|
|
|
|
// Can't handle quotes within the command.
|
|
|
|
|
consolePrint("Error: Invalid command.");
|
|
|
|
|
while(c != '\0' && c != ';') c = line[++i];
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assertTrue(j < CONSOLE_LINE_MAX, "Command is too long.");
|
|
|
|
|
buffer[j++] = c;
|
|
|
|
|
i++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_CMD_PARSED:
|
|
|
|
|
if(j == 0) {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create exec
|
|
|
|
|
assertNull(exec, "Existing command parsing?");
|
|
|
|
|
|
|
|
|
|
exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount];
|
|
|
|
|
memoryZero(exec, sizeof(consolecmdexec_t));
|
|
|
|
|
|
|
|
|
|
buffer[j] = '\0';
|
|
|
|
|
stringCopy(exec->command, buffer, CONSOLE_LINE_MAX);
|
|
|
|
|
state = CONSOLE_EXEC_STATE_FIND_ARG;
|
|
|
|
|
|
|
|
|
|
j = 0;// Free up buffer
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_FIND_ARG:
|
|
|
|
|
|
|
|
|
|
if(c == '\0' || c == ';') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_CMD_FINISHED;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(stringIsWhitespace(c)) {
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(c == '"') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED;
|
|
|
|
|
i++;
|
|
|
|
|
} else {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_PARSE_ARG;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_PARSE_ARG:
|
|
|
|
|
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_ARG_PARSED;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer[j++] = c;
|
|
|
|
|
i++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED:
|
|
|
|
|
if(c == '"') {
|
|
|
|
|
state = CONSOLE_EXEC_STATE_ARG_PARSED;
|
|
|
|
|
i++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(c == '\0' || c == ';') {
|
|
|
|
|
consolePrint("Error: Unterminated quoted argument.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(c == '\\') {
|
|
|
|
|
c = line[++i];
|
|
|
|
|
|
|
|
|
|
if(c == '\0' || c == ';') {
|
|
|
|
|
consolePrint("Error: Unterminated quoted argument.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer[j++] = c;
|
|
|
|
|
i++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_ARG_PARSED:
|
|
|
|
|
assertTrue(j != 0, "Empty argument?");
|
|
|
|
|
buffer[j] = '\0';
|
|
|
|
|
stringCopy(exec->argv[exec->argc++], buffer, CONSOLE_LINE_MAX);
|
|
|
|
|
state = CONSOLE_EXEC_STATE_FIND_ARG;
|
|
|
|
|
j = 0;// Free up buffer
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case CONSOLE_EXEC_STATE_CMD_FINISHED:
|
|
|
|
|
assertNotNull(exec, "No command found?");
|
|
|
|
|
|
|
|
|
|
// Now, is there a command that matches?
|
|
|
|
|
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
|
|
|
|
|
consolecmd_t *cmd = &CONSOLE.commands[k];
|
|
|
|
|
if(stringCompare(cmd->name, exec->command) != 0) continue;
|
|
|
|
|
exec->cmd = cmd;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(exec->cmd == NULL) {
|
|
|
|
|
// Command wasn't found, is there a variable that matches?
|
|
|
|
|
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
|
|
|
|
|
consolevar_t *var = &CONSOLE.variables[k];
|
|
|
|
|
if(stringCompare(var->name, exec->command) != 0) continue;
|
|
|
|
|
|
|
|
|
|
// Matching variable found, is this a GET or a SET?
|
|
|
|
|
if(exec->argc == 0) {
|
|
|
|
|
exec->cmd = CONSOLE.cmdGet;
|
|
|
|
|
exec->argc = 2;
|
|
|
|
|
stringCopy(exec->argv[1], var->name, CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[0], CONSOLE.cmdGet->name, CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
|
|
|
|
|
exec->argc = 1;
|
|
|
|
|
} else {
|
|
|
|
|
exec->cmd = CONSOLE.cmdSet;
|
|
|
|
|
exec->argc = 3;
|
|
|
|
|
stringCopy(exec->argv[2], exec->argv[1], CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[1], var->name, CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[0], CONSOLE.cmdSet->name, CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[1], exec->argv[0], CONSOLE_LINE_MAX);
|
|
|
|
|
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
|
|
|
|
|
exec->argc = 2;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
|
continue;
|
|
|
|
|
if(exec->cmd == NULL) {
|
|
|
|
|
consolePrint("Error: Command '%s' not found.", exec->command);
|
|
|
|
|
exec = NULL;
|
|
|
|
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(j >= CONSOLE_LINE_MAX) {
|
|
|
|
|
assertTrue(false, "Command exceeds maximum length.");
|
|
|
|
|
// Prep for next command.
|
|
|
|
|
exec = NULL;
|
|
|
|
|
state = CONSOLE_EXEC_STATE_INITIAL;
|
|
|
|
|
CONSOLE.execBufferCount++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
assertUnreachable("Invalid state.");
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buffer[j++] = c; // Accumulate characters into the buffer
|
|
|
|
|
} while(c != '\0');
|
|
|
|
|
pthread_mutex_unlock(&CONSOLE.lock); // Unlock
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void consoleProcess() {
|
|
|
|
|
pthread_mutex_lock(&CONSOLE.lock); // Lock
|
|
|
|
|
for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) {
|
|
|
|
|
consolecmdexec_t *exec = &CONSOLE.execBuffer[i];
|
|
|
|
|
|
|
|
|
|
assertTrue(
|
|
|
|
|
exec->argc > 0,
|
|
|
|
|
"Command execution has no arguments."
|
|
|
|
|
);
|
|
|
|
|
assertNotNull(
|
|
|
|
|
exec->cmd,
|
|
|
|
|
"Command execution has no command."
|
|
|
|
|
);
|
|
|
|
|
assertNotNull(exec->cmd, "Command execution has no command.");
|
|
|
|
|
|
|
|
|
|
exec->cmd->function(exec);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the exec buffer
|
|
|
|
|
CONSOLE.execBufferCount = 0;
|
|
|
|
|
pthread_mutex_unlock(&CONSOLE.lock); // Unlock
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void consoleCmdGet(const consolecmdexec_t *exec) {
|
|
|
|
|
void cmdGet(const consolecmdexec_t *exec) {
|
|
|
|
|
assertTrue(
|
|
|
|
|
exec->argc >= 2,
|
|
|
|
|
"get command requires 1 argument."
|
|
|
|
|
exec->argc >= 1,
|
|
|
|
|
"Get command requires 1 argument."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
|
|
|
|
consolevar_t *var = &CONSOLE.variables[i];
|
|
|
|
|
if(stringCompare(var->name, exec->argv[1]) != 0) continue;
|
|
|
|
|
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
|
|
|
|
|
consolePrint("%s", var->value);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consolePrint("Error: Variable '%s' not found.", exec->argv[1]);
|
|
|
|
|
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void consoleCmdSet(const consolecmdexec_t *exec) {
|
|
|
|
|
void cmdSet(const consolecmdexec_t *exec) {
|
|
|
|
|
assertTrue(
|
|
|
|
|
exec->argc >= 3,
|
|
|
|
|
exec->argc >= 2,
|
|
|
|
|
"set command requires 2 arguments."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
|
|
|
|
|
consolevar_t *var = &CONSOLE.variables[i];
|
|
|
|
|
if(stringCompare(var->name, exec->argv[1]) != 0) continue;
|
|
|
|
|
consoleVarSetValue(var, exec->argv[2]);
|
|
|
|
|
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
|
|
|
|
|
consoleVarSetValue(var, exec->argv[1]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
consolePrint("Error: Variable '%s' not found.", exec->argv[1]);
|
|
|
|
|
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cmdEcho(const consolecmdexec_t *exec) {
|
|
|
|
|
assertTrue(
|
|
|
|
|
exec->argc >= 1,
|
|
|
|
|
"echo command requires 1 argument."
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
consolePrint("%s", exec->argv[0]);
|
|
|
|
|
}
|