merge(expansion)
This commit is contained in:
commit
953b05fba0
10 changed files with 650 additions and 40 deletions
|
|
@ -1,3 +1,4 @@
|
|||
#define _POSIX_C_SOURCE 200809L
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
|
@ -5,23 +6,113 @@
|
|||
#include <string.h>
|
||||
|
||||
#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 size_t var_len(char *start)
|
||||
// {
|
||||
// char *iter = start;
|
||||
// while (*iter != ' ' && *iter != 0)
|
||||
// *iter++;
|
||||
// return iter - start;
|
||||
// }
|
||||
static bool is_var_start_char(char c)
|
||||
{
|
||||
return isalpha(c) || c == '_';
|
||||
}
|
||||
|
||||
struct ast_command *expand(struct ast_command *command)
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
struct ast_command *expand(struct ast_command *command,
|
||||
const struct hash_map *vars)
|
||||
{
|
||||
if (command == NULL)
|
||||
return NULL;
|
||||
|
||||
bool in_quotes = false;
|
||||
char *str;
|
||||
size_t len;
|
||||
bool in_quotes;
|
||||
struct list *l = command->command;
|
||||
|
||||
while (l != NULL)
|
||||
|
|
@ -30,31 +121,35 @@ struct ast_command *expand(struct ast_command *command)
|
|||
str = (char *)l->data;
|
||||
len = strlen(str);
|
||||
|
||||
for (size_t i = 0; str[i] != '\0'; i++)
|
||||
for (size_t i = 0; str[i] != 0; i++)
|
||||
{
|
||||
if (in_quotes)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
else if (str[i] == '\'')
|
||||
if (str[i] == '\'')
|
||||
{
|
||||
// remove quote
|
||||
in_quotes = !in_quotes;
|
||||
memmove(&str[i], &str[i + 1], strlen(&str[i + 1]) + 1);
|
||||
memmove(str + i, str + i + 1, strlen(str + i + 1) + 1);
|
||||
i--;
|
||||
}
|
||||
else if (in_quotes)
|
||||
{
|
||||
continue; // do nothing
|
||||
}
|
||||
else if (str[i] == '$' && str[i + 1] != 0 && !isspace(str[i + 1]))
|
||||
{
|
||||
// size_t len = var_len(str + i + 1);
|
||||
// char *end = str + i + len + 1;
|
||||
// char c = *end;
|
||||
// *end = 0;
|
||||
// printf("var: %s\n", str + i + 1);
|
||||
// *end = c;
|
||||
// variable expansion
|
||||
bool r = expand_var(&str, i, vars);
|
||||
if (r == false || str == NULL)
|
||||
return NULL;
|
||||
|
||||
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 NULL;
|
||||
}
|
||||
|
||||
if (len != strlen(str))
|
||||
|
|
@ -63,6 +158,7 @@ struct ast_command *expand(struct ast_command *command)
|
|||
if (new_str == NULL)
|
||||
{
|
||||
// error: realloc fail
|
||||
return NULL;
|
||||
}
|
||||
l->data = new_str;
|
||||
}
|
||||
|
|
@ -71,17 +167,3 @@ struct ast_command *expand(struct ast_command *command)
|
|||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
// int main()
|
||||
// {
|
||||
// printf("Expansion module test\n");
|
||||
// struct ast_command ast_command;
|
||||
// // char str[] = "echo Hello $?";
|
||||
// char str[] = "echo Hello $AE86";
|
||||
// ast_command.command = list_append(NULL, str);
|
||||
|
||||
// struct ast_command *command2 = expand(&ast_command);
|
||||
// printf("command2: %s\n", (char *)command2->command->data);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
|
|
|||
|
|
@ -1,4 +1,26 @@
|
|||
#ifndef EXPANSION_H
|
||||
#define EXPANSION_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "../utils/hash_map/hash_map.h"
|
||||
|
||||
/**
|
||||
* Parse a variable from a string starting with '$'.
|
||||
* @param str The input string starting with '$'. It must start with '$'.
|
||||
* @param res Pointer to a char pointer that will be set to the extracted
|
||||
* variable name.
|
||||
* @return The number of characters processed in the input string.
|
||||
*/
|
||||
size_t parse_var_name(char *str, char **res);
|
||||
|
||||
/**
|
||||
* Expand variables in an AST command using the provided variable map.
|
||||
* @param command The AST command to expand.
|
||||
* @param vars The hash map containing variables.
|
||||
* @return A new AST command with variables expanded, or NULL on error.
|
||||
*/
|
||||
struct ast_command *expand(struct ast_command *command,
|
||||
const struct hash_map *vars);
|
||||
|
||||
#endif /* ! EXPANSION_H */
|
||||
|
|
|
|||
|
|
@ -28,6 +28,14 @@ static size_t hash(const char *key)
|
|||
return hash;
|
||||
}
|
||||
|
||||
static void destroy_pair_list(struct pair_list **p)
|
||||
{
|
||||
free((char *)(*p)->key);
|
||||
free((*p)->value);
|
||||
free((*p));
|
||||
*p = NULL;
|
||||
}
|
||||
|
||||
struct hash_map *hash_map_init(size_t size)
|
||||
{
|
||||
struct hash_map *p = malloc(sizeof(struct hash_map));
|
||||
|
|
@ -102,7 +110,7 @@ void hash_map_free(struct hash_map *hash_map)
|
|||
{
|
||||
prev = l;
|
||||
l = l->next;
|
||||
free(prev);
|
||||
destroy_pair_list(&prev);
|
||||
}
|
||||
}
|
||||
free(hash_map->data);
|
||||
|
|
@ -163,7 +171,7 @@ bool hash_map_remove(struct hash_map *hash_map, const char *key)
|
|||
p->next = l->next;
|
||||
else
|
||||
hash_map->data[i] = l->next;
|
||||
free(l);
|
||||
destroy_pair_list(&l);
|
||||
return true;
|
||||
}
|
||||
p = l;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#include "string_utils.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
char *trim_blank_left(char *str)
|
||||
{
|
||||
|
|
@ -13,3 +14,31 @@ char *trim_blank_left(char *str)
|
|||
|
||||
return str;
|
||||
}
|
||||
|
||||
char *insert_into(char *dest, const char *src, size_t pos, size_t len)
|
||||
{
|
||||
size_t res_len = strlen(dest);
|
||||
size_t prefix_len = pos;
|
||||
size_t suffix_len = res_len - (pos + len);
|
||||
size_t src_len = strlen(src);
|
||||
size_t new_len = prefix_len + src_len + suffix_len;
|
||||
|
||||
if (dest == NULL || src == NULL || pos + len > res_len)
|
||||
return NULL;
|
||||
|
||||
if (res_len < new_len)
|
||||
{
|
||||
char *p = realloc(dest, new_len + 1);
|
||||
if (p == NULL)
|
||||
return NULL; // allocation failure
|
||||
dest = p;
|
||||
}
|
||||
|
||||
memmove(dest + pos + src_len, dest + pos + len, suffix_len);
|
||||
memcpy(dest + pos, src, src_len);
|
||||
dest[new_len] = 0;
|
||||
|
||||
if (res_len > new_len)
|
||||
return realloc(dest, new_len + 1);
|
||||
return dest;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,4 +12,10 @@
|
|||
*/
|
||||
char *trim_blank_left(char *str);
|
||||
|
||||
/**
|
||||
* Inserts a substring into a destination string at a specified position,
|
||||
* replacing a specified length of characters.
|
||||
*/
|
||||
char *insert_into(char *dest, const char *src, size_t pos, size_t len);
|
||||
|
||||
#endif /* STRING_UTILS_H */
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
#include "vars.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../hash_map/hash_map.h"
|
||||
|
|
@ -19,6 +19,14 @@ char *get_var(const struct hash_map *vars, const char *key)
|
|||
return (char *)hash_map_get(vars, key);
|
||||
}
|
||||
|
||||
char *get_var_or_env(const struct hash_map *vars, const char *key)
|
||||
{
|
||||
char *value = (char *)hash_map_get(vars, key);
|
||||
if (value == NULL)
|
||||
value = getenv(key);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool set_var(struct hash_map *vars, const char *key, const char *value)
|
||||
{
|
||||
if (key == NULL || value == NULL)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ struct hash_map *vars_init(void);
|
|||
*/
|
||||
char *get_var(const struct hash_map *vars, const char *key);
|
||||
|
||||
/**
|
||||
* Get the value of a variable, from the environment if not found in vars,
|
||||
* NULL if not found in either.
|
||||
*/
|
||||
char *get_var_or_env(const struct hash_map *vars, const char *key);
|
||||
|
||||
/**
|
||||
* Set the value of a variable. Key and value ownership are transferred to
|
||||
* the hash_map and need to be on the heap. Returns true on success, false on
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue