commit f1e6447562f8521696af876a0f7c7fe3c0dcd51b
parent f19990ed156eba0d37b9ba155c0d430e99cddf5c
Author: Louis Burda <quent.burda@gmail.com>
Date: Wed, 7 Jun 2023 17:50:57 +0200
Rename to main.c and fix Makefile
Diffstat:
M | Makefile | | | 5 | ++++- |
A | main.c | | | 241 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | tmpl.c | | | 240 | ------------------------------------------------------------------------------- |
3 files changed, 245 insertions(+), 241 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,12 +1,15 @@
PREFIX ?= /usr/local
BINDIR ?= /bin
+CFLAGS = -std=c99 -Wunused-variable -Wunused-function -Wconversion
+
all: tmpl
clean:
rm -f tmpl
-tmp: tmpl.c
+tmpl: main.c
+ $(CC) -o $@ $< $(CFLAGS)
install:
install -m755 tmpl -t "$(DESTDIR)$(PREFIX)$(BINDIR)"
diff --git a/main.c b/main.c
@@ -0,0 +1,241 @@
+#define _XOPEN_SOURCE 500
+
+#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;
+
+const char *
+getvar(const char *name)
+{
+ struct var *var;
+
+ for (var = vars; var; var = var->next) {
+ if (!strcmp(var->name, name))
+ return var->value;
+ }
+
+ return getenv(name);
+}
+
+void
+assign(char *line)
+{
+ char *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;
+ const char *value;
+ 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) {
+ value = getvar(line + 6);
+ sep = strchr(line + 6, ' ');
+ if (!sep) err(1, "invalid #ifeq: %lu:%s", lineno, line);
+ if (!strcmp(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';
+ value = getvar(line + 9);
+ if (!value) {
+ *sep = '=';
+ assign(line + 9);
+ }
+ } else if (!strncmp(line, "#-- ", 4)) {
+ return; /* comment */
+ } 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';
+ value = getvar(open + 2);
+ if (value) fputs(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 + (size_t) ret >= cap) {
+ cap *= 2;
+ contents = realloc(contents, cap);
+ if (!contents) err(1, "realloc");
+ }
+ memcpy(contents + len, buf, (size_t) ret);
+ len += (size_t) ret;
+ }
+ contents[len] = '\0';
+
+ return contents;
+}
+
+int
+main(int argc, const char **argv)
+{
+ const char **arg;
+ const char *path;
+ char *str, *contents;
+
+ 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);
+ }
+}
diff --git a/tmpl.c b/tmpl.c
@@ -1,240 +0,0 @@
-#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;
-
-const char *
-getvar(const char *name)
-{
- struct var *var;
-
- for (var = vars; var; var = var->next) {
- if (!strcmp(var->name, name))
- return var->value;
- }
-
- return getenv(name);
-}
-
-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;
- const char *value;
- 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) {
- value = getvar(line + 6);
- sep = strchr(line + 6, ' ');
- if (!sep) err(1, "invalid #ifeq: %lu:%s", lineno, line);
- if (!strcmp(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';
- value = getvar(line + 9);
- if (!value) {
- *sep = '=';
- assign(line + 9);
- }
- } else if (!strncmp(line, "#-- ", 4)) {
- return; /* comment */
- } 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';
- value = getvar(open + 2);
- if (value) fputs(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);
- }
-}