168 lines
4.6 KiB
C++
168 lines
4.6 KiB
C++
/*
|
|
* 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")) {
|
|
sync_cwd();
|
|
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;
|
|
}
|
|
}
|
|
}
|