tmpl

Simple key-value templator
git clone https://git.sinitax.com/sinitax/tmpl
Log | Files | Refs | README | LICENSE | sfeed.txt

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:
MMakefile | 5++++-
Amain.c | 241+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtmpl.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); - } -}