Commit Diff
Diff:
fafc6849577c9374ee6acb8ae7f30104464bb08e
3b21cca385c403247960cfe9385dda1d56f28670
Commit:
3b21cca385c403247960cfe9385dda1d56f28670
Tree:
341ea2e3cad32b3853c9fa182d0cdfd68404d583
Author:
Omar Polo <op@omarpolo.com>
Date:
Tue Jun 29 12:17:40 2021 UTC
Message:
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 <op@omarpolo.com>
+
+ * 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 parse_conf(const char*);
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 @@ struct vhost *host;
* 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 add_param(char *, char *, int);
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 @@ conf : options vhosts ;
%%
-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 @@ yylex(void)
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_conf(const char *path)
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;
+}
Omar Polo