From a7cbe7b32c1e3f147865ae8b29700a56853e45dd Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 10 Oct 2026 00:49:26 +0200 Subject: [PATCH] feat: history - fix: move many things to heap --- Makefile | 7 ++- src/msh.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 140 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 263464f..7a5fc7b 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,11 @@ CFLAGS ?= -Wall ifeq ($(RELEASE),true) CFLAGS += -O3 +else + CFLAGS += --debug endif -.PHONY: clean release-$(RELEASE) +.PHONY: clean grind release-$(RELEASE) all: dist/msh @@ -21,3 +23,6 @@ dist: clean: rm -rf dist + +grind: + valgrind --leak-check=full --show-leak-kinds=all ./dist/msh diff --git a/src/msh.c b/src/msh.c index 468a1ae..3a080e9 100644 --- a/src/msh.c +++ b/src/msh.c @@ -17,6 +17,8 @@ #define HISTORY_FILE_NAME "/history.log" #define LOCAL_SHARE_DIRNAME "/.local/share/msh" +#define DEFAULTLINECAPACITY 128 +#define DEFAULTTOKENSCAPACITY 32 const int LINESIZE = 512; @@ -138,16 +140,19 @@ char* status_string(int status) { int main(int argc, char** argv) { int retval = 0; - char* tokens[LINESIZE] = {}; - char** args; - char line[LINESIZE] = {}; + char* line = NULL; + size_t linecapacity = 0; char prompt[LINESIZE] = {}; - int linelen = 0; - char* tokenwork; - char* token; - int ntokens = 0; - - int i; + size_t linelen = 0; + char* tokenwork = NULL; + size_t tokenworkcapacity = 0; + char** tokens = NULL; + size_t tokenscapacity = 0; + size_t ntokens = 0; + char** history = NULL; + size_t historycapacity = 0; + size_t historyi = 0; + int pid; int laststatus = 0; @@ -246,6 +251,25 @@ int main(int argc, char** argv) { } + // Initialize memory + tokens = calloc(DEFAULTTOKENSCAPACITY, sizeof(char*)); + if (tokens == NULL) { + retval = 1; + goto cleanup; + } + memset(tokens, 0, DEFAULTTOKENSCAPACITY * sizeof(char*)); + tokenscapacity = DEFAULTTOKENSCAPACITY; + + tokenwork = malloc(DEFAULTLINECAPACITY); + if (tokenwork == NULL) { + retval = 1; + goto cleanup; + } + tokenworkcapacity = DEFAULTLINECAPACITY; + + historycapacity = 16; + history = calloc(historycapacity, sizeof(char*)); + // Main loop while(1) { char* result = getcwd(pwd, LINESIZE - 1); @@ -289,7 +313,10 @@ int main(int argc, char** argv) { tcsetattr(fileno(stdin), 0, &term); - memset(line, 0, LINESIZE); + line = malloc(DEFAULTLINECAPACITY); + linecapacity = DEFAULTLINECAPACITY; + + memset(line, 0, linecapacity); while ((readc = getchar()) != '\n') { if (readc == EOF) { @@ -351,6 +378,19 @@ int main(int argc, char** argv) { cursor++; linelen++; + // Are we out of capacity? Double line length + if (linelen >= linecapacity - 1) { + linecapacity *= 2; + line = realloc(line, linecapacity); + if (line == NULL) { + fprintf(stderr, "Error: Failed to re-allocated line buffer"); + retval = 1; + goto cleanup; + } + + memset(line + linecapacity / 2, 0, linecapacity / 2); + } + print_prompt: // Manual echo printf(ANSI_ESC "[2K" ANSI_ESC "[0G" "%s%s", prompt, line); @@ -369,19 +409,58 @@ print_prompt: fprintf(historyfile, "%s\n", line); fflush(historyfile); + if (tokenworkcapacity < linelen - 1) { + tokenworkcapacity *= 2; + tokenwork = realloc(tokenwork, tokenworkcapacity); + } + + memset(tokenwork, 0, tokenworkcapacity); + tokenwork = strncpy(tokenwork, line, linelen); + if (tokenwork == NULL) { + retval = 1; + goto cleanup; + } + + + // TEMPORARY + if (historyi >= historycapacity) { + historycapacity *= 2; + history = reallocarray(history, historycapacity, sizeof(char*)); + + if (history == NULL) { + retval = 1; + goto cleanup; + } + } + // change ownership of line to history + history[historyi++] = line; + line = NULL; + + char* tokensarg = tokenwork; + ntokens = 0; + // Split into tokens - for (tokenwork = line, i = 0; ; tokenwork = NULL, i++) { - token = strtok(tokenwork, " \t"); + for (;; ntokens++) { + char* token = strtok(tokensarg, " \t"); if (token == NULL) { break; } - tokens[i] = token; + tokens[ntokens] = token; + + if (ntokens >= tokenscapacity - 1) { + tokenscapacity *= 2; + tokens = reallocarray(tokens, tokenscapacity, sizeof(char*)); + } + + // required to be NULL for second strtok invocation + if (tokensarg != NULL) { + tokensarg = NULL; + } } - ntokens = i; tokens[ntokens] = NULL; - args = &tokens[1]; + char** args = &tokens[1]; if (ntokens == 0) { continue; @@ -399,17 +478,32 @@ print_prompt: continue; } + if (strcmp("history", tokens[0]) == 0) { + for (int i = 0; i < historycapacity; i++) { + if (history[i] == NULL) { + break; + } + + printf("%.3d: %s\n", i, history[i]); + } + + laststatus = 0; + continue; + } + // Execute command pid = fork(); if(pid == 0) { - return execvpe(tokens[0], tokens, environ); + retval = execvpe(tokens[0], tokens, environ); + goto cleanup; } else { waitpid(pid, &wstatus, 0); laststatus = WEXITSTATUS(wstatus); } + } @@ -425,6 +519,30 @@ cleanup: fflush(historyfile); fclose(historyfile); } + + if (line != NULL) { + free(line); + } + + if (tokenwork != NULL) { + free(tokenwork); + } + + if (tokens != NULL) { + free(tokens); + } + + if (history != NULL) { + for (int i = 0; i < historycapacity; i++) { + if (history[i] != NULL) { + free(history[i]); + } + } + + free(history); + } + + tcsetattr(fileno(stdin), 0, &orig_term); return retval; }