aboutsummaryrefslogtreecommitdiffstats
path: root/src/subst.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/subst.c')
-rw-r--r--src/subst.c148
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;
+}