commit 3b21cca385c403247960cfe9385dda1d56f28670 from: Omar Polo date: Tue Jun 29 12:17:40 2021 UTC allow to define macros in the config file Macros can be defined at the top of the configuration file: dir = "/var/gemini" cert = "/etc/keys" and re-used later, for example server "foo" { root "$dir/foo" # -> /var/gemini/foo cert "$cert/foo.pem" # -> /etc/keys/foo.pem } commit - fafc6849577c9374ee6acb8ae7f30104464bb08e commit + 3b21cca385c403247960cfe9385dda1d56f28670 blob - cc360c4901c08c5bd5da70304cab9f3e9ba19a71 blob + 3801306c4c6e1ee019b5b3cb1e5e57d3b9f55b78 --- ChangeLog +++ ChangeLog @@ -1,4 +1,6 @@ 2021-06-29 Omar Polo + + * parse.y (yylex): allow to define macros in the config file * gmid.c (main): use getopt_long, add --help as synonym of -h and -V/--version blob - 34b210d3cff25aecc2c30aa64de95cb054f1143c blob + 26ad06c3df63f7fd26eb40b970f28d4785fee7cf --- gmid.h +++ gmid.h @@ -314,6 +314,7 @@ void drop_priv(void); void yyerror(const char*, ...); int parse_portno(const char*); void parse_conf(const char*); +int cmdline_symset(char *); /* log.c */ void fatal(const char*, ...) blob - 4e796ccaa5a903cbb4c8109b3925a8e9a20bb10e blob + e4e1b3074f0559f5b6c1f6d4e8d8f8a330346967 --- parse.y +++ parse.y @@ -42,6 +42,18 @@ typedef struct { * int yydebug = 1; */ +/* + * The idea behind this implementation of macros is from rad/parse.y + */ +TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); +struct sym { + TAILQ_ENTRY(sym) entry; + int used; + int persist; + char *name; + char *val; +}; + struct vhost *host; struct location *loc; @@ -64,6 +76,8 @@ void only_once(const void*, const char*); void only_oncei(int, const char*); int fastcgi_conf(char *, char *, char *); void add_param(char *, char *, int); +int symset(const char *, const char *, int); +char *symget(const char *); %} @@ -83,8 +97,29 @@ void add_param(char *, char *, int); %% -conf : options vhosts ; +conf : vars options vhosts ; +vars : /* empty */ + | vars var + ; + +var : TSTRING '=' TSTRING { + char *s = $1; + while (*s++) { + if (isspace(*s)) { + yyerror("macro name cannot contain " + "whitespaces"); + free($1); + free($3); + YYERROR; + } + } + symset($1, $3, 0); + free($1); + free($3); + } + ; + options : /* empty */ | options option ; @@ -338,9 +373,9 @@ static struct keyword { static int yylex(void) { - char buf[1024], *ebuf, *p, *str; + char buf[8096], *ebuf, *p, *str, *v, *val; int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; - size_t i; + size_t i, len; p = buf; ebuf = buf + sizeof(buf); @@ -368,10 +403,13 @@ repeat: yylval.colno = 0; yylval.lineno++; goto repeat; + case '=': + return c; case EOF: goto eof; } +top: /* parsing next word */ for (;; c = getc(yyfp), yylval.colno++) { switch (c) { @@ -385,6 +423,45 @@ repeat: if (escape) continue; break; + + /* expand macros in-place */ + case '$': + if (!escape) { + v = p; + while (1) { + if ((c = getc(yyfp)) == EOF) { + yyerror("EOF during macro expansion"); + return 0; + } + if (p + 1 >= ebuf - 1) { + yyerror("string too long"); + return 0; + } + if (isalnum(c) || c == '_') { + *p++ = c; + continue; + } + *p = 0; + ungetc(c, yyfp); + break; + } + p = v; + if ((val = symget(p)) == NULL) { + yyerror("macro '%s' not defined", v); + goto top; + } + len = strlen(val); + if (p + len >= ebuf - 1) { + yyerror("after macro-expansion, " + "string too long"); + goto top; + } + *p = '\0'; + strlcat(p, val, ebuf - p); + p += len; + goto top; + } + break; case '\n': if (quotes) yyerror("unterminated quotes in column %d", @@ -490,6 +567,8 @@ parse_portno(const char *p) void parse_conf(const char *path) { + struct sym *sym, *next; + config_path = path; if ((yyfp = fopen(path, "r")) == NULL) err(1, "cannot open config: %s", path); @@ -501,6 +580,17 @@ parse_conf(const char *path) if (TAILQ_FIRST(&hosts)->domain == NULL) errx(1, "no vhost defined in %s", path); + + /* free unused macros */ + TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) { + /* TODO: warn if !sym->used */ + if (!sym->persist) { + free(sym->name); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } } char * @@ -628,4 +718,65 @@ add_param(char *name, char *val, int env) TAILQ_INSERT_HEAD(h, e, envs); else TAILQ_INSERT_TAIL(h, e, envs); +} + +int +symset(const char *name, const char *val, int persist) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (!strcmp(name, sym->name)) + break; + } + + if (sym != NULL) { + if (sym->persist) + return 0; + else { + free(sym->name); + free(sym->val); + TAILQ_REMOVE(&symhead, sym, entry); + free(sym); + } + } + + sym = xcalloc(1, sizeof(*sym)); + sym->name = xstrdup(name); + sym->val = xstrdup(val); + sym->used = 0; + sym->persist = persist; + + TAILQ_INSERT_TAIL(&symhead, sym, entry); + return 0; } + +int +cmdline_symset(char *s) +{ + char *sym, *val; + int ret; + + if ((val = strrchr(s, '=')) == NULL) + return -1; + sym = xcalloc(1, val - s + 1); + memcpy(sym, s, val - s); + ret = symset(sym, val + 1, 1); + free(sym); + return ret; +} + +char * +symget(const char *name) +{ + struct sym *sym; + + TAILQ_FOREACH(sym, &symhead, entry) { + if (!strcmp(name, sym->name)) { + sym->used = 1; + return sym->val; + } + } + + return NULL; +}