Commit Diff


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		 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;
+}