#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "../utils/ast/ast.h" #include "../utils/hash_map/hash_map.h" #include "../utils/string_utils/string_utils.h" #include "../utils/vars/vars.h" static bool is_var_start_char(char c) { return isalpha(c) || c == '_'; } static bool is_var_char(char c) { return isalnum(c) || c == '_'; } static bool is_special_var_char(char c) { return c == '@' || c == '*' || c == '?' || c == '$' || isdigit(c) || c == '#'; } size_t parse_var_name(char *str, char **res) { char *brace = NULL; size_t i = 1; // skip the '$' if (str[i] == '{' && str[i + 1] != 0 && str[i + 1] != '}') { if (is_special_var_char(str[i + 1]) && str[i + 2] == '}') { // Special variable like ${1}, ${?} *res = strndup(str + i + 1, 1); return 4; // length of ${X} } brace = str + i; i++; // skip the '{' } else if (is_special_var_char(str[i])) { *res = strndup(str + i, 1); return 2; // length of $X } if (!is_var_start_char(str[i])) { // Not a valid variable start *res = NULL; return 0; } while (1) { if (str[i] == '}' && *brace == '{') { *res = strndup(str + 2, i - 2); return i + 1; } else if (!is_var_char(str[i])) { if (brace != NULL) { // Missing closing '}' *res = NULL; return 0; } break; } i++; } *res = strndup(str + 1, i - 1); return i; } static bool expand_var(char **str, size_t pos, const struct hash_map *vars) { char *var_name = NULL; size_t r = parse_var_name(*str + pos, &var_name); if (r > 0 && var_name != NULL) { char *value; char rnd_str[10]; // max 5 digits + null terminator if (strcmp(var_name, "RANDOM") == 0) { short rnd = short_random(); snprintf(rnd_str, 10, "%d", rnd); value = rnd_str; } else { value = get_var_or_env(vars, var_name); if (value == NULL) // Undefined variable: expand to empty string value = ""; } char *p = insert_into(*str, value, pos, r); free(var_name); if (p == NULL) { // error: insertion failed return false; } *str = p; return true; } return false; } bool expand(struct ast_command *command, const struct hash_map *vars) { if (command == NULL) return false; char *str; size_t len; bool in_quotes; struct list *l = command->command; while (l != NULL) { in_quotes = false; str = (char *)l->data; len = strlen(str); for (size_t i = 0; str[i] != 0; i++) { if (str[i] == '\'') { // remove single quote in_quotes = !in_quotes; memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); i--; } else if (in_quotes) { continue; // do nothing } else if (str[i] == '\"') { // remove double quote memmove(str + i, str + i + 1, strlen(str + i + 1) + 1); i--; } else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1])) { // variable expansion bool r = expand_var(&str, i, vars); if (r == false || str == NULL) return false; i--; // -1 because loop will increment i } } if (in_quotes) { // error: quote not closed fprintf(stderr, "Error: quote not closed in string: %s\n", str); return false; } if (len != strlen(str)) { char *new_str = realloc(str, strlen(str) + 1); if (new_str == NULL) { // error: realloc fail return false; } l->data = new_str; } l = l->next; } return true; }