/* * 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; } } }