aboutsummaryrefslogtreecommitdiff
path: root/parse.y
diff options
context:
space:
mode:
authorOmar Polo <op@omarpolo.com>2021-06-29 12:17:40 +0000
committerOmar Polo <op@omarpolo.com>2021-06-29 12:17:40 +0000
commit3b21cca385c403247960cfe9385dda1d56f28670 (patch)
tree341ea2e3cad32b3853c9fa182d0cdfd68404d583 /parse.y
parentfafc6849577c9374ee6acb8ae7f30104464bb08e (diff)
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 }
Diffstat (limited to 'parse.y')
-rw-r--r--parse.y157
1 files changed, 154 insertions, 3 deletions
diff --git a/parse.y b/parse.y
index 4e796cc..e4e1b30 100644
--- a/parse.y
+++ b/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,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;
+}