archivemuh

This commit is contained in:
2025-08-20 19:18:38 -05:00
parent 1411c2e96b
commit fbfcbe9578
173 changed files with 2802 additions and 13 deletions

344
src/console/console.c Normal file
View File

@@ -0,0 +1,344 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "console/cmd/cmdquit.h"
#include "console/cmd/cmdecho.h"
#include "console/cmd/cmdset.h"
#include "console/cmd/cmdget.h"
console_t CONSOLE;
void consoleInit() {
memoryZero(&CONSOLE, sizeof(console_t));
// Register the get and set command.
CONSOLE.cmdGet = consoleRegCmd("get", cmdGet);
CONSOLE.cmdSet = consoleRegCmd("set", cmdSet);
consoleRegCmd("quit", cmdQuit);
consoleRegCmd("echo", cmdEcho);
consolePrint(" = Dawn Console = ");
#if DUSK_CONSOLE_TERMIOS
// Create termios session.
struct termios newTermios;
tcgetattr(STDIN_FILENO, &CONSOLE.originalTermios);
// Disable canonical mode & echo
newTermios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newTermios);
fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK);// Set stdin to non-blocking
#endif
}
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
consoleCmdInit(cmd, name, function);
return cmd;
}
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consolevar_t *var = &CONSOLE.variables[CONSOLE.variableCount++];
consoleVarInitListener(var, name, value, event);
return var;
}
void consolePrint(const char_t *message, ...) {
char_t buffer[CONSOLE_LINE_MAX];
va_list args;
va_start(args, message);
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
// Move all lines back
memoryMove(
CONSOLE.line[0],
CONSOLE.line[1],
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
);
// Copy the new line
memoryCopy(
CONSOLE.line[CONSOLE_HISTORY_MAX - 1],
buffer,
len + 1
);
printf("%s\n", buffer);
}
void consoleExec(const char_t *line) {
assertNotNull(line, "line must not be NULL");
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Too many commands in the buffer."
);
char_t buffer[CONSOLE_LINE_MAX];
size_t i = 0, j = 0;
char_t c;
consoleexecstate_t state = CONSOLE_EXEC_STATE_INITIAL;
consolecmdexec_t *exec = NULL;
while(state != CONSOLE_EXEC_STATE_FULLY_PARSED) {
c = line[i];
switch(state) {
case CONSOLE_EXEC_STATE_INITIAL:
assertTrue(j == 0, "Buffer not empty?");
if(c == '\0') {
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
break;
}
if(stringIsWhitespace(c) || c == ';') {
i++;
continue;
}
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("Invalid command");
while(c != '\0' && c != ';') c = line[++i];
continue;
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Command too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
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++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED:
if(c == '"') {
state = CONSOLE_EXEC_STATE_ARG_PARSED;
i++;
continue;
}
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
if(c == '\\') {
c = line[++i];
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_ARG_PARSED:
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;
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 1;
} else {
exec->cmd = CONSOLE.cmdSet;
stringCopy(exec->argv[1], exec->argv[0], CONSOLE_LINE_MAX);
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 2;
}
break;
}
if(exec->cmd == NULL) {
consolePrint("Command not found", exec->command);
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
break;
}
}
// Prep for next command.
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
CONSOLE.execBufferCount++;
break;
default:
assertUnreachable("Invalid state.");
break;
}
}
}
// May move these later
void consoleUpdate() {
#if DUSK_CONSOLE_TERMIOS
char_t c;
for(;;) {
if(!read(STDIN_FILENO, &c, 1)) break;
putchar(c);
}
#endif
for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) {
consolecmdexec_t *exec = &CONSOLE.execBuffer[i];
assertNotNull(exec->cmd, "Command execution has no command.");
exec->cmd->function(exec);
}
// #if DUSK_KEYBOARD_SUPPORT == 1
// uint8_t key;
// while((key = inputKeyboardPop()) != 0) {
// printf("Key pressed: %c\n", key);
// switch(key) {
// case 0:
// break;
// case INPUT_KEY_ENTER:
// consoleExec(CONSOLE.inputBuffer);
// CONSOLE.inputIndex = 0;
// CONSOLE.inputBuffer[0] = '\0';
// break;
// case INPUT_KEY_BACKSPACE:
// if(CONSOLE.inputIndex > 0) {
// CONSOLE.inputIndex--;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// default:
// if(
// key >= INPUT_KEY_ASCII_START && key <= INPUT_KEY_ASCII_END &&
// CONSOLE.inputIndex < CONSOLE_LINE_MAX - 1
// ) {
// CONSOLE.inputBuffer[CONSOLE.inputIndex++] = key;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// }
// }
// #endif
// Clear the exec buffer
CONSOLE.execBufferCount = 0;
}
void consoleDispose(void) {
// Reset termios
#if DUSK_CONSOLE_TERMIOS
tcsetattr(STDIN_FILENO, TCSANOW, &CONSOLE.originalTermios);
#endif
}