diff options
Diffstat (limited to 'src/subst.c')
| -rw-r--r-- | src/subst.c | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/src/subst.c b/src/subst.c new file mode 100644 index 0000000..6c8bec6 --- /dev/null +++ b/src/subst.c @@ -0,0 +1,148 @@ +#include "l2su.h" + +#include <stdlib.h> +#include <string.h> + +struct l2_subst__item { + char *name; + char *value; + + struct l2_subst__item *next; +}; + +struct l2_subst { + struct l2_subst__item *items; + int status; +}; + +int l2_subst_init(l2_subst_t **sp) +{ + l2_subst_t *ret = calloc(1, sizeof(struct l2_subst)); + if (!ret) return -1; + + *sp = ret; + + ret->items = NULL; + ret->status = 0; + + return 0; +} + +int l2_subst_add(l2_subst_t *sp, const char *name, const char *value) +{ + if (sp->status < 0) return sp->status; + + char *namedup = strdup(name); + char *valdup = strdup(value); + struct l2_subst__item *item = calloc(1, sizeof(struct l2_subst__item)); + + if (!namedup || !valdup || !item) { + free(namedup); + free(valdup); + free(item); + sp->status = -1; + return -1; + } + + item->name = namedup; + item->value = valdup; + item->next = sp->items; + sp->items = item; + + return 0; +} + +const char *l2_subst__find(l2_subst_t *sp, const char *name, size_t len) +{ + for (struct l2_subst__item *item = sp->items; item; item = item->next) { + if (!strncmp(item->name, name, len)) return item->value; + } + return NULL; +} + +int l2_subst__append(char **retbuf, size_t *sz, size_t *cap, const char *start, const char *end) +{ + ptrdiff_t len = end - start; + if (len <= 0) return 0; + + if (*sz + len > *cap) { + size_t newcap = *cap ? *cap : 16; + while (newcap && *sz + len > newcap) newcap <<= 1; + + if (!newcap) { + goto cleanup; + } + + char *newbuf = realloc(*retbuf, newcap); + if (!newbuf) { + goto cleanup; + } + + *retbuf = newbuf; + *cap = newcap; + } + + memcpy(*retbuf + *sz, start, len); + *sz += len; + return 0; + +cleanup: + free(*retbuf); + return -1; +} + +int l2_subst_apply(l2_subst_t *sp, const char *in, char **out) +{ + if (sp->status < 0) return sp->status; + + char *retbuf = NULL, *temp; + size_t size = 0; + size_t cap = 0; + + const char *lastcpy = in; + const char *open = NULL; + const char *cur; + + for (cur = in; *cur; ++cur) { + if (*cur == '$' && *(cur + 1) == '{') { + if (l2_subst__append(&retbuf, &size, &cap, lastcpy, cur) < 0) + return -1; + + lastcpy = cur; + open = cur + 2; + continue; + } else if (*cur == '}' && open) { + const char *rep = l2_subst__find(sp, open, cur - open); + if (rep) { + if (l2_subst__append(&retbuf, &size, &cap, rep, rep + strlen(rep)) < 0) + return -1; + lastcpy = cur + 1; + } + + open = NULL; + } + } + + if (l2_subst__append(&retbuf, &size, &cap, lastcpy, cur + 1) < 0) return -1; + + temp = realloc(retbuf, size); + if (temp) retbuf = temp; + *out = retbuf; + + return 0; +} + +void l2_subst_free(l2_subst_t *sp) +{ + if (!sp) return; + + for (struct l2_subst__item *cur = sp->items, *temp; cur; cur = temp) { + temp = cur->next; + free(cur->name); + free(cur->value); + free(cur); + } + + free(sp); + return; +} |
