feat: multi-user system, bug fixes, security & performance fixes, and more

This commit is contained in:
2026-03-14 13:28:46 +01:00
parent 9f54e4957d
commit 39b0424085
389 changed files with 231853 additions and 591 deletions
+166
View File
@@ -0,0 +1,166 @@
/*
* vars.cpp
* Shell variable storage, expansion, and tilde/comment processing
* Copyright (c) 2025-2026 Daniel Hammer
*/
#include "shell.h"
// ---- Storage ----
static constexpr int MAX_VARS = 32;
struct ShellVar {
char name[32];
char value[128];
};
static ShellVar vars[MAX_VARS];
static int var_count = 0;
// ---- Core operations ----
void var_set(const char* name, const char* value) {
for (int i = 0; i < var_count; i++) {
if (streq(vars[i].name, name)) {
scopy(vars[i].value, value, sizeof(vars[i].value));
return;
}
}
if (var_count < MAX_VARS) {
scopy(vars[var_count].name, name, sizeof(vars[var_count].name));
scopy(vars[var_count].value, value, sizeof(vars[var_count].value));
var_count++;
}
}
void var_unset(const char* name) {
for (int i = 0; i < var_count; i++) {
if (streq(vars[i].name, name)) {
for (int j = i; j < var_count - 1; j++) vars[j] = vars[j + 1];
var_count--;
return;
}
}
}
// Get variable value. Built-in dynamic vars ($?, $PWD) are synthesized on
// demand. Returns nullptr if not found. The returned pointer for dynamic
// vars points to a static buffer overwritten on the next call.
const char* var_get(const char* name) {
// User-defined vars take priority
for (int i = 0; i < var_count; i++) {
if (streq(vars[i].name, name)) return vars[i].value;
}
// Synthesize built-in vars
static char synth[128];
if (streq(name, "?")) {
int_to_str(last_exit, synth, sizeof(synth));
return synth;
}
if (streq(name, "USER")) return session_user[0] ? session_user : nullptr;
if (streq(name, "HOME")) return session_home[0] ? session_home : nullptr;
if (streq(name, "PWD")) {
build_dir_path(cwd, synth, sizeof(synth));
return synth;
}
return nullptr;
}
// ---- Helpers used by the set builtin (main.cpp) ----
// The set builtin needs to iterate user-defined vars and show built-in
// vars. We expose a small iteration API rather than the raw array.
int var_user_count() { return var_count; }
const char* var_user_name(int idx) {
return (idx >= 0 && idx < var_count) ? vars[idx].name : nullptr;
}
const char* var_user_value(int idx) {
return (idx >= 0 && idx < var_count) ? vars[idx].value : nullptr;
}
// ---- Character classification ----
bool is_var_char(char c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') || c == '_';
}
// ---- Variable expansion ----
void expand_vars(const char* in, char* out, int outMax) {
int o = 0;
while (*in && o < outMax - 1) {
if (*in == '\\' && in[1] == '$') {
if (o < outMax - 1) out[o++] = '$';
in += 2;
continue;
}
if (*in == '$') {
in++;
char name[32];
int n = 0;
if (*in == '{') {
in++;
while (*in && *in != '}' && n < 31) name[n++] = *in++;
if (*in == '}') in++;
} else if (*in == '?') {
name[n++] = '?';
in++;
} else {
while (is_var_char(*in) && n < 31) name[n++] = *in++;
}
name[n] = '\0';
if (n > 0) {
const char* val = var_get(name);
if (val) {
while (*val && o < outMax - 1) out[o++] = *val++;
}
} else {
if (o < outMax - 1) out[o++] = '$';
}
} else {
out[o++] = *in++;
}
}
out[o] = '\0';
}
// ---- Tilde expansion ----
void expand_tilde(const char* in, char* out, int outMax) {
int o = 0;
bool at_start = true;
while (*in && o < outMax - 1) {
if (at_start && *in == '~' && session_home[0]) {
if (in[1] == '\0' || in[1] == '/' || in[1] == ' ') {
const char* h = session_home;
while (*h && o < outMax - 1) out[o++] = *h++;
in++;
} else {
out[o++] = *in++;
}
} else {
at_start = (*in == ' ');
out[o++] = *in++;
}
}
out[o] = '\0';
}
// ---- Comment stripping ----
void strip_comment(char* line) {
bool in_sq = false, in_dq = false;
for (int i = 0; line[i]; i++) {
if (line[i] == '\'' && !in_dq) in_sq = !in_sq;
else if (line[i] == '"' && !in_sq) in_dq = !in_dq;
else if (line[i] == '#' && !in_sq && !in_dq) {
line[i] = '\0';
return;
}
}
}