commit 110975105e860a4530f9d5b1595d063b8356ccd6
Author: Louis Burda <quent.burda@gmail.com>
Date: Fri, 26 May 2023 20:44:50 +0200
Add initial version
Diffstat:
A | .gitignore | | | 2 | ++ |
A | Makefile | | | 17 | +++++++++++++++++ |
A | test | | | 10 | ++++++++++ |
A | tmpl.c | | | 238 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 267 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,2 @@
+tmpl
+.gdb_history
diff --git a/Makefile b/Makefile
@@ -0,0 +1,17 @@
+PREFIX ?= /usr/local
+BINDIR ?= /bin
+
+all: tmpl
+
+clean:
+ rm -f tmpl
+
+tmp: tmpl.c
+
+install:
+ install -m755 tmpl -t "$(DESTDIR)$(PREFIX)$(BINDIR)"
+
+uninstall:
+ rm -f "$(DESTDIR)$(PREFIX)$(BINDIR)/tmpl"
+
+.PHONY: all clean install uninstall
diff --git a/test b/test
@@ -0,0 +1,10 @@
+#default PREFIX /usr/local
+#default BINDIR /bin
+
+#ifdef DEBUG
+yes debug
+#else
+no debug
+#endif
+
+install -m755 "#{TARGET}" -t "#{DESTDIR}#{PREFIX}#{BINDIR}"
diff --git a/tmpl.c b/tmpl.c
@@ -0,0 +1,238 @@
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum {
+ IFEQ,
+ IFDEF,
+ ELSE
+};
+
+struct block {
+ int active;
+ int type;
+};
+
+struct var {
+ const char *name;
+ const char *value;
+ struct var *next;
+};
+
+struct var *vars, **vars_end;
+
+struct var *
+getvar(const char *name)
+{
+ struct var *var;
+
+ for (var = vars; var; var = var->next) {
+ if (!strcmp(var->name, name))
+ return var;
+ }
+
+ return NULL;
+}
+
+void
+assign(char *line)
+{
+ char *name, *value, *sep;
+ struct var *var;
+
+ sep = strchr(line, '=');
+ if (!sep) err(1, "invalid assign: '%s'", line);
+ *sep = '\0';
+
+ var = malloc(sizeof(struct var));
+ if (!var) err(1, "malloc");
+ var->name = line;
+ var->value = sep + 1;
+ var->next = NULL;
+
+ *vars_end = var;
+ vars_end = &var->next;
+}
+
+void
+template(char *line)
+{
+ static struct block stack[32] = { 0 };
+ static int stack_top = 0;
+ static size_t lineno = 0;
+ struct var *var;
+ char *open, *close;
+ char *sep;
+
+ lineno++;
+
+ if (!strncmp(line, "#ifdef ", 7)) {
+ if (stack_top == 32) errx(1, "too much nesting");
+ stack[stack_top].active = 0;
+ stack[stack_top].type = IFDEF;
+ if (!stack_top || stack[stack_top-1].active) {
+ if (getvar(line + 7))
+ stack[stack_top].active = 1;
+ }
+ stack_top++;
+ } else if (!strncmp(line, "#ifeq ", 6)) {
+ if (stack_top == 32) errx(1, "too much nesting");
+ stack[stack_top].active = 0;
+ stack[stack_top].type = IFEQ;
+ if (!stack_top || stack[stack_top-1].active) {
+ var = getvar(line + 6);
+ sep = strchr(line + 6, ' ');
+ if (!sep) err(1, "invalid #ifeq: %lu:%s", lineno, line);
+ if (!strcmp(var->value, sep + 1)) {
+ stack[stack_top].active = 1;
+ }
+ }
+ stack_top++;
+ } else if (!strcmp(line, "#else")) {
+ if (!stack_top || stack[stack_top-1].type != IFDEF
+ && stack[stack_top-1].type != IFEQ)
+ err(1, "invalid #else: %lu:%s", lineno, line);
+ stack[stack_top-1].active ^= 1;
+ stack[stack_top-1].type = ELSE;
+ } else if (!strcmp(line, "#endif")) {
+ if (!stack_top) err(1, "invalid #endif: %lu:%s", lineno, line);
+ stack_top--;
+ } else if (!strncmp(line, "#define ", 8)) {
+ if (stack_top && !stack[stack_top-1].active)
+ return;
+
+ sep = strchr(line + 8, ' ');
+ if (!sep) err(1, "invalid #default: %lu:%s", lineno, line);
+ *sep = '=';
+ assign(line + 8);
+ } else if (!strncmp(line, "#default ", 9)) {
+ if (stack_top && !stack[stack_top-1].active)
+ return;
+
+ sep = strchr(line + 9, ' ');
+ if (!sep) err(1, "invalid #default: %lu:%s", lineno, line);
+ *sep = '\0';
+ var = getvar(line + 9);
+ if (!var) {
+ *sep = '=';
+ assign(line + 9);
+ }
+ } else {
+ if (stack_top && !stack[stack_top-1].active)
+ return;
+
+ while (1) {
+ open = strstr(line, "#{");
+ if (!open) break;
+
+ if (open != line && open[-1] == '\\') {
+ open[-1] = '\0';
+ fputs(line, stdout);
+ fputs("#{", stdout);
+ line = open + 2;
+ continue;
+ }
+
+ close = strchr(open, '}');
+ if (!close) break;
+
+ *open = '\0';
+ fputs(line, stdout);
+
+ *close = '\0';
+ var = getvar(open + 2);
+ if (var) fputs(var->value, stdout);
+
+ line = close + 1;
+ }
+
+ puts(line);
+ }
+}
+
+void
+perline(char *contents, int nonempty, void (*process)(char *))
+{
+ char *line, *sep, *end;
+
+ line = contents;
+ do {
+ sep = strchr(line, '\n');
+ end = sep ? sep : line + strlen(line);
+ *end = '\0';
+ if (!nonempty || end > line + 1)
+ process(line);
+ line = end + 1;
+ } while (sep);
+}
+
+char *
+readall(const char *path)
+{
+ char buf[BUFSIZ];
+ char *contents;
+ ssize_t ret;
+ size_t len, cap;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) err(1, "open '%s'", path);
+
+ cap = BUFSIZ + 1;
+ contents = malloc(cap);
+ if (!contents) err(1, "malloc");
+
+ len = 0;
+ while ((ret = read(fd, buf, BUFSIZ)) > 0) {
+ if (len + ret >= cap) {
+ cap *= 2;
+ contents = realloc(contents, cap);
+ if (!contents) err(1, "realloc");
+ }
+ memcpy(contents + len, buf, ret);
+ len += ret;
+ }
+ contents[len] = '\0';
+
+ return contents;
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char **arg;
+ const char *path;
+ char *str, *contents;
+ int fd;
+
+ vars = NULL;
+ vars_end = &vars;
+
+ if (!argc) return 1;
+ for (arg = argv + 1; *arg; arg++) {
+ if (!strcmp(*arg, "-h") || !strcmp(*arg, "--help")) {
+ fprintf(stderr, "Usage: tmpl [-C CONFIG].. [-D NAME=VALUE].. FILE..\n");
+ return 1;
+ } else if (!strcmp(*arg, "-D") || !strcmp(*arg, "--define")) {
+ str = strdup(*++arg);
+ if (!str) err(1, "strdup");
+ assign(str);
+ } else if (!strcmp(*arg, "-C") || !strcmp(*arg, "--config")) {
+ contents = readall(*++arg);
+ perline(contents, 1, assign);
+ free(contents);
+ } else {
+ break;
+ }
+ }
+
+ for (; *arg; arg++) {
+ path = !strcmp(*arg, "-") ? "/dev/stdin" : *arg;
+ contents = readall(path);
+ perline(contents, 0, template);
+ free(contents);
+ }
+}