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
	}
This commit is contained in:
Omar Polo 2021-06-29 12:17:40 +00:00
parent fafc684957
commit 3b21cca385
3 changed files with 157 additions and 3 deletions

View File

@ -1,5 +1,7 @@
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
2021-06-17 Omar Polo <op@omarpolo.com>

1
gmid.h
View File

@ -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*, ...)

157
parse.y
View File

@ -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,7 +97,28 @@ 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 *
@ -629,3 +719,64 @@ add_param(char *name, char *val, int env)
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;
}