feat: history

- fix: move many things to heap
This commit is contained in:
2026-10-10 00:49:26 +02:00
parent 28d6123c4c
commit a7cbe7b32c
2 changed files with 140 additions and 17 deletions

View File

@@ -7,9 +7,11 @@ CFLAGS ?= -Wall
ifeq ($(RELEASE),true) ifeq ($(RELEASE),true)
CFLAGS += -O3 CFLAGS += -O3
else
CFLAGS += --debug
endif endif
.PHONY: clean release-$(RELEASE) .PHONY: clean grind release-$(RELEASE)
all: dist/msh all: dist/msh
@@ -21,3 +23,6 @@ dist:
clean: clean:
rm -rf dist rm -rf dist
grind:
valgrind --leak-check=full --show-leak-kinds=all ./dist/msh

148
src/msh.c
View File

@@ -17,6 +17,8 @@
#define HISTORY_FILE_NAME "/history.log" #define HISTORY_FILE_NAME "/history.log"
#define LOCAL_SHARE_DIRNAME "/.local/share/msh" #define LOCAL_SHARE_DIRNAME "/.local/share/msh"
#define DEFAULTLINECAPACITY 128
#define DEFAULTTOKENSCAPACITY 32
const int LINESIZE = 512; const int LINESIZE = 512;
@@ -138,16 +140,19 @@ char* status_string(int status) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
int retval = 0; int retval = 0;
char* tokens[LINESIZE] = {}; char* line = NULL;
char** args; size_t linecapacity = 0;
char line[LINESIZE] = {};
char prompt[LINESIZE] = {}; char prompt[LINESIZE] = {};
int linelen = 0; size_t linelen = 0;
char* tokenwork; char* tokenwork = NULL;
char* token; size_t tokenworkcapacity = 0;
int ntokens = 0; char** tokens = NULL;
size_t tokenscapacity = 0;
size_t ntokens = 0;
char** history = NULL;
size_t historycapacity = 0;
size_t historyi = 0;
int i;
int pid; int pid;
int laststatus = 0; 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 // Main loop
while(1) { while(1) {
char* result = getcwd(pwd, LINESIZE - 1); char* result = getcwd(pwd, LINESIZE - 1);
@@ -289,7 +313,10 @@ int main(int argc, char** argv) {
tcsetattr(fileno(stdin), 0, &term); tcsetattr(fileno(stdin), 0, &term);
memset(line, 0, LINESIZE); line = malloc(DEFAULTLINECAPACITY);
linecapacity = DEFAULTLINECAPACITY;
memset(line, 0, linecapacity);
while ((readc = getchar()) != '\n') { while ((readc = getchar()) != '\n') {
if (readc == EOF) { if (readc == EOF) {
@@ -351,6 +378,19 @@ int main(int argc, char** argv) {
cursor++; cursor++;
linelen++; 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: print_prompt:
// Manual echo // Manual echo
printf(ANSI_ESC "[2K" ANSI_ESC "[0G" "%s%s", prompt, line); printf(ANSI_ESC "[2K" ANSI_ESC "[0G" "%s%s", prompt, line);
@@ -369,19 +409,58 @@ print_prompt:
fprintf(historyfile, "%s\n", line); fprintf(historyfile, "%s\n", line);
fflush(historyfile); 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 // Split into tokens
for (tokenwork = line, i = 0; ; tokenwork = NULL, i++) { for (;; ntokens++) {
token = strtok(tokenwork, " \t"); char* token = strtok(tokensarg, " \t");
if (token == NULL) { if (token == NULL) {
break; 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; tokens[ntokens] = NULL;
args = &tokens[1]; char** args = &tokens[1];
if (ntokens == 0) { if (ntokens == 0) {
continue; continue;
@@ -399,17 +478,32 @@ print_prompt:
continue; 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 // Execute command
pid = fork(); pid = fork();
if(pid == 0) { if(pid == 0) {
return execvpe(tokens[0], tokens, environ); retval = execvpe(tokens[0], tokens, environ);
goto cleanup;
} else { } else {
waitpid(pid, &wstatus, 0); waitpid(pid, &wstatus, 0);
laststatus = WEXITSTATUS(wstatus); laststatus = WEXITSTATUS(wstatus);
} }
} }
@@ -425,6 +519,30 @@ cleanup:
fflush(historyfile); fflush(historyfile);
fclose(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); tcsetattr(fileno(stdin), 0, &orig_term);
return retval; return retval;
} }