Commit Diff


commit - fb863fa46f4ac9493cff1e6bb9aa13d753bd5fb8
commit + c34ec41754978e41240e8aedd4e5d3a9e6362635
blob - 35d7892394e81db18aca90b995cfedb1ef98f9f0
blob + 836222204f43d83aad1ade8a0feac1ddaf124f5f
--- got/parse.y
+++ got/parse.y
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Tracey Emery <tracey@openbsd.org>
+ * Copyright (c) 2020 Tracey Emery <tracey@openbsd.org>
  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
blob - 1bfc514197abcd84859f193604c899656ca61ca6
blob + 0af46f6d0f3ce6f15863ad64dec29f5f54ca2757
--- gotweb/gotweb.c
+++ gotweb/gotweb.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <got_error.h>
 #include <got_object.h>
 #include <got_reference.h>
 #include <got_repository.h>
@@ -57,7 +58,7 @@ struct gw_trans {
 	TAILQ_HEAD(dirs, gw_dir)	 gw_dirs;
 	struct got_repository	*repo;
 	struct gw_dir		*gw_dir;
-	struct gotweb_conf	*gw_conf;
+	struct gotweb_config	*gw_conf;
 	struct ktemplate	*gw_tmpl;
 	struct khtmlreq		*gw_html_req;
 	struct kreq		*gw_req;
@@ -4656,7 +4657,7 @@ main(int argc, char *argv[])
 	}
 
 	if ((gw_trans->gw_conf =
-	    malloc(sizeof(struct gotweb_conf))) == NULL) {
+	    malloc(sizeof(struct gotweb_config))) == NULL) {
 		gw_malloc = 0;
 		error = got_error_from_errno("malloc");
 		goto done;
@@ -4680,7 +4681,8 @@ main(int argc, char *argv[])
 	gw_trans->gw_tmpl->keysz = TEMPL__MAX;
 	gw_trans->gw_tmpl->arg = gw_trans;
 	gw_trans->gw_tmpl->cb = gw_template;
-	error = parse_conf(GOTWEB_CONF, gw_trans->gw_conf);
+
+	error = parse_gotweb_config(&gw_trans->gw_conf, GOTWEB_CONF);
 	if (error)
 		goto done;
 
@@ -4700,7 +4702,6 @@ done:
 		free(gw_trans->gw_conf->got_site_link);
 		free(gw_trans->gw_conf->got_logo);
 		free(gw_trans->gw_conf->got_logo_url);
-		free(gw_trans->gw_conf);
 		free(gw_trans->commit_id);
 		free(gw_trans->next_id);
 		free(gw_trans->next_prev_id);
blob - a2b02715b9a59a20e78eac7a01d868854f4fd3ac
blob + c2c8f7043eff68f8b9dfc3de1baa9d8d8fef8de5
--- gotweb/gotweb.h
+++ gotweb/gotweb.h
@@ -20,8 +20,6 @@
 
 #include <stdbool.h>
 
-#include <got_error.h>
-
 #define	GOTWEB_CONF	 "/etc/gotweb.conf"
 #define GOTWEB_TMPL_DIR	 "/cgi-bin/gw_tmpl"
 #define GOTWEB		 "/cgi-bin/gotweb/gotweb"
@@ -48,7 +46,7 @@
 
 #define BUFFER_SIZE	 2048
 
-struct gotweb_conf {
+struct gotweb_config {
 	char		*got_repos_path;
 	char		*got_site_name;
 	char		*got_site_owner;
@@ -67,6 +65,11 @@ struct gotweb_conf {
 	bool		 got_show_repo_cloneurl;
 };
 
-const struct got_error*	 parse_conf(const char *, struct gotweb_conf *);
+/*
+ * Parse gotweb config file, if it exists
+ * Load gotweb_config struct
+ */
+const struct got_error*	 parse_gotweb_config(struct gotweb_config **,
+    const char *);
 
 #endif /* GOTWEB_H */
blob - 2271bbde9b190920695f4451cfe046353cbe8ec7
blob + f9275d17b97b19973d9956571f8dc6a2a2a2afe3
--- gotweb/parse.y
+++ gotweb/parse.y
@@ -1,5 +1,7 @@
 /*
- * Copyright (c) 2019 Tracey Emery <tracey@traceyemery.net>
+ * Copyright (c) 2019, 2020 Tracey Emery <tracey@openbsd.org>
+ * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
@@ -21,16 +23,27 @@
 %{
 #include <sys/types.h>
 #include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
 
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
 #include <ctype.h>
 #include <err.h>
+#include <errno.h>
+#include <event.h>
+#include <ifaddrs.h>
+#include <imsg.h>
 #include <limits.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
+#include <syslog.h>
+#include <unistd.h>
 
+#include "got_error.h"
 #include "gotweb.h"
 
 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
@@ -38,11 +51,13 @@ static struct file {
 	TAILQ_ENTRY(file)	 entry;
 	FILE			*stream;
 	char			*name;
+	size_t	 		 ungetpos;
+	size_t			 ungetsize;
+	u_char			*ungetbuf;
+	int			 eof_reached;
 	int			 lineno;
-	int			 errors;
-	const struct got_error*	 error;
 } *file, *topfile;
-struct file	*pushfile(const char *);
+static const struct got_error*	pushfile(struct file**, const char *);
 int		 popfile(void);
 int		 yyparse(void);
 int		 yylex(void);
@@ -51,19 +66,30 @@ int		 yyerror(const char *, ...)
     __attribute__((__nonnull__ (1)));
 int		 kw_cmp(const void *, const void *);
 int		 lookup(char *);
+int		 igetc(void);
 int		 lgetc(int);
-int		 lungetc(int);
+void		 lungetc(int);
 int		 findeol(void);
 
-static const struct got_error*	 gerror = NULL;
-char				*syn_err;
+TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+	TAILQ_ENTRY(sym)	 entry;
+	int			 used;
+	int			 persist;
+	char			*nam;
+	char			*val;
+};
 
-struct gotweb_conf		*gw_conf;
+int	 symset(const char *, const char *, int);
+char	*symget(const char *);
 
+const struct got_error* gerror = NULL;
+struct gotweb_config		 gw_conf;
+
 typedef struct {
 	union {
-		int64_t			 number;
-		char			*string;
+		int64_t		 number;
+		char		*string;
 	} v;
 	int lineno;
 } YYSTYPE;
@@ -75,18 +101,17 @@ typedef struct {
 %token	GOT_SHOW_REPO_DESCRIPTION GOT_MAX_REPOS_DISPLAY GOT_REPOS_PATH
 %token	GOT_MAX_COMMITS_DISPLAY ON ERROR GOT_SHOW_SITE_OWNER
 %token	GOT_SHOW_REPO_CLONEURL
-%token	<v.string>		STRING
-%token	<v.number>		NUMBER
-%type	<v.number>		boolean
+%token	<v.string>	STRING
+%token	<v.number>	NUMBER
+%type	<v.number>	boolean
 %%
 
 grammar		: /* empty */
 		| grammar '\n'
 		| grammar main '\n'
-		| grammar error '\n'		{ file->errors++; }
 		;
 
-boolean		: STRING			{
+boolean		: STRING {
 			if (strcasecmp($1, "true") == 0 ||
 			    strcasecmp($1, "yes") == 0)
 				$$ = 1;
@@ -101,60 +126,84 @@ boolean		: STRING			{
 			}
 			free($1);
 		}
-		| ON				{ $$ = 1; }
+		| ON { $$ = 1; }
 		;
-
 main		: GOT_REPOS_PATH STRING {
-			if ((gw_conf->got_repos_path = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_repos_path = strdup($2);
+			if (gw_conf.got_repos_path== NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_MAX_REPOS NUMBER {
 			if ($2 > 0)
-				gw_conf->got_max_repos = $2;
+				gw_conf.got_max_repos = $2;
 		}
 		| GOT_SITE_NAME STRING {
-				if ((gw_conf->got_site_name = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_site_name = strdup($2);
+			if (gw_conf.got_site_name == NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_SITE_OWNER STRING {
-				if ((gw_conf->got_site_owner = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_site_owner = strdup($2);
+			if (gw_conf.got_site_owner == NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_SITE_LINK STRING {
-				if ((gw_conf->got_site_link = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_site_link = strdup($2);
+			if (gw_conf.got_site_link == NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_LOGO STRING {
-				if ((gw_conf->got_logo = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_logo = strdup($2);
+			if (gw_conf.got_logo== NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_LOGO_URL STRING {
-				if ((gw_conf->got_logo_url = strdup($2)) == NULL)
-				errx(1, "out of memory");
+			gw_conf.got_logo_url = strdup($2);
+			if (gw_conf.got_logo_url== NULL) {
+				free($2);
+				yyerror("strdup");
+				YYERROR;
+			}
 		}
 		| GOT_SHOW_SITE_OWNER boolean {
-			gw_conf->got_show_site_owner = $2;
+			gw_conf.got_show_site_owner = $2;
 		}
 		| GOT_SHOW_REPO_OWNER boolean {
-			gw_conf->got_show_repo_owner = $2;
+			gw_conf.got_show_repo_owner = $2;
 		}
-		| GOT_SHOW_REPO_AGE boolean { gw_conf->got_show_repo_age = $2; }
+		| GOT_SHOW_REPO_AGE boolean {
+			gw_conf.got_show_repo_age = $2;
+		}
 		| GOT_SHOW_REPO_DESCRIPTION boolean {
-			gw_conf->got_show_repo_description =	$2;
+			gw_conf.got_show_repo_description = $2;
 		}
 		| GOT_SHOW_REPO_CLONEURL boolean {
-			gw_conf->got_show_repo_cloneurl =	$2;
+			gw_conf.got_show_repo_cloneurl = $2;
 		}
 		| GOT_MAX_REPOS_DISPLAY NUMBER {
 			if ($2 > 0)
-				gw_conf->got_max_repos_display = $2;
+				gw_conf.got_max_repos_display = $2;
 		}
 		| GOT_MAX_COMMITS_DISPLAY NUMBER {
 			if ($2 > 0)
-				gw_conf->got_max_commits_display = $2;
+				gw_conf.got_max_commits_display = $2;
 		}
 		;
-
 %%
 
 struct keywords {
@@ -166,20 +215,23 @@ int
 yyerror(const char *fmt, ...)
 {
 	va_list		 ap;
-	char		*msg = NULL;
-	static char	 err_msg[512];
+	char		*msg;
+	char		*err = NULL;
 
-	file->errors++;
 	va_start(ap, fmt);
-	if (vasprintf(&msg, fmt, ap) == -1)
-		errx(1, "yyerror vasprintf");
+	if (vasprintf(&msg, fmt, ap) == -1) {
+		gerror =  got_error_from_errno("vasprintf");
+		return 0;
+	}
 	va_end(ap);
-	snprintf(err_msg, sizeof(err_msg), "%s:%d: %s", file->name,
-	    yylval.lineno, msg);
-	gerror = got_error_from_errno2("parse_error", err_msg);
-
+	if (asprintf(&err, "%s:%d: %s", file->name, yylval.lineno, msg) == -1) {
+		gerror = got_error_from_errno("asprintf");
+		return(0);
+	}
+	gerror = got_error_msg(GOT_ERR_PARSE_Y_YY, strdup(err));
 	free(msg);
-	return (0);
+	free(err);
+	return(0);
 }
 
 int
@@ -191,7 +243,7 @@ kw_cmp(const void *k, const void *e)
 int
 lookup(char *s)
 {
-	/* this has to be sorted always */
+	/* This has to be sorted always. */
 	static const struct keywords keywords[] = {
 		{ "got_logo",			GOT_LOGO },
 		{ "got_logo_url",		GOT_LOGO_URL },
@@ -219,34 +271,39 @@ lookup(char *s)
 		return (STRING);
 }
 
-#define MAXPUSHBACK	128
+#define START_EXPAND	1
+#define DONE_EXPAND	2
 
-u_char	*parsebuf;
-int	 parseindex;
-u_char	 pushback_buffer[MAXPUSHBACK];
-int	 pushback_index = 0;
+static int	expanding;
 
 int
-lgetc(int quotec)
+igetc(void)
 {
-	int		c, next;
+	int	c;
 
-	if (parsebuf) {
-		/* Read character from the parsebuffer instead of input. */
-		if (parseindex >= 0) {
-			c = parsebuf[parseindex++];
-			if (c != '\0')
-				return (c);
-			parsebuf = NULL;
-		} else
-			parseindex++;
+	while (1) {
+		if (file->ungetpos > 0)
+			c = file->ungetbuf[--file->ungetpos];
+		else
+			c = getc(file->stream);
+
+		if (c == START_EXPAND)
+			expanding = 1;
+		else if (c == DONE_EXPAND)
+			expanding = 0;
+		else
+			break;
 	}
+	return (c);
+}
 
-	if (pushback_index)
-		return (pushback_buffer[--pushback_index]);
+int
+lgetc(int quotec)
+{
+	int		c, next;
 
 	if (quotec) {
-		if ((c = getc(file->stream)) == EOF) {
+		if ((c = igetc()) == EOF) {
 			yyerror("reached end of file while parsing "
 			    "quoted string");
 			if (file == topfile || popfile() == EOF)
@@ -256,8 +313,8 @@ lgetc(int quotec)
 		return (c);
 	}
 
-	while ((c = getc(file->stream)) == '\\') {
-		next = getc(file->stream);
+	while ((c = igetc()) == '\\') {
+		next = igetc();
 		if (next != '\n') {
 			c = next;
 			break;
@@ -266,28 +323,39 @@ lgetc(int quotec)
 		file->lineno++;
 	}
 
-	while (c == EOF) {
-		if (file == topfile || popfile() == EOF)
-			return (EOF);
-		c = getc(file->stream);
+	if (c == EOF) {
+		/*
+		 * Fake EOL when hit EOF for the first time. This gets line
+		 * count right if last line in included file is syntactically
+		 * invalid and has no newline.
+		 */
+		if (file->eof_reached == 0) {
+			file->eof_reached = 1;
+			return ('\n');
+		}
+		while (c == EOF) {
+			if (file == topfile || popfile() == EOF)
+				return (EOF);
+			c = igetc();
+		}
 	}
 	return (c);
 }
 
-int
+void
 lungetc(int c)
 {
 	if (c == EOF)
-		return (EOF);
-	if (parsebuf) {
-		parseindex--;
-		if (parseindex >= 0)
-			return (c);
+		return;
+
+	if (file->ungetpos >= file->ungetsize) {
+		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+		if (p == NULL)
+			err(1, "%s", __func__);
+		file->ungetbuf = p;
+		file->ungetsize *= 2;
 	}
-	if (pushback_index < MAXPUSHBACK-1)
-		return (pushback_buffer[pushback_index++] = c);
-	else
-		return (EOF);
+	file->ungetbuf[file->ungetpos++] = c;
 }
 
 int
@@ -295,14 +363,9 @@ findeol(void)
 {
 	int	c;
 
-	parsebuf = NULL;
-
-	/* skip to either EOF or the first real EOL */
+	/* Skip to either EOF or the first real EOL. */
 	while (1) {
-		if (pushback_index)
-			c = pushback_buffer[--pushback_index];
-		else
-			c = lgetc(0);
+		c = lgetc(0);
 		if (c == '\n') {
 			file->lineno++;
 			break;
@@ -316,11 +379,12 @@ findeol(void)
 int
 yylex(void)
 {
-	u_char	 buf[8096];
-	u_char	*p;
-	int	 quotec, next, c;
-	int	 token;
+	unsigned char	 buf[8096];
+	unsigned char	*p, *val;
+	int		 quotec, next, c;
+	int		 token;
 
+top:
 	p = buf;
 	while ((c = lgetc(0)) == ' ' || c == '\t')
 		; /* nothing */
@@ -329,7 +393,38 @@ yylex(void)
 	if (c == '#')
 		while ((c = lgetc(0)) != '\n' && c != EOF)
 			; /* nothing */
+	if (c == '$' && !expanding) {
+		while (1) {
+			if ((c = lgetc(0)) == EOF)
+				return (0);
 
+			if (p + 1 >= buf + sizeof(buf) - 1) {
+				yyerror("string too long");
+				return (findeol());
+			}
+			if (isalnum(c) || c == '_') {
+				*p++ = c;
+				continue;
+			}
+			*p = '\0';
+			lungetc(c);
+			break;
+		}
+		val = symget(buf);
+		if (val == NULL) {
+			yyerror("macro '%s' not defined", buf);
+			return (findeol());
+		}
+		p = val + strlen(val) - 1;
+		lungetc(DONE_EXPAND);
+		while (p >= val) {
+			lungetc(*p);
+			p--;
+		}
+		lungetc(START_EXPAND);
+		goto top;
+	}
+
 	switch (c) {
 	case '\'':
 	case '"':
@@ -343,8 +438,7 @@ yylex(void)
 			} else if (c == '\\') {
 				if ((next = lgetc(quotec)) == EOF)
 					return (0);
-				if (next == quotec || next == ' ' ||
-				    next == '\t')
+				if (next == quotec || c == ' ' || c == '\t')
 					c = next;
 				else if (next == '\n') {
 					file->lineno++;
@@ -366,7 +460,7 @@ yylex(void)
 		}
 		yylval.v.string = strdup(buf);
 		if (yylval.v.string == NULL)
-			errx(1, "yylex: strdup");
+			err(1, "%s", __func__);
 		return (STRING);
 	}
 
@@ -376,7 +470,7 @@ yylex(void)
 	if (c == '-' || isdigit(c)) {
 		do {
 			*p++ = c;
-			if ((size_t)(p-buf) >= sizeof(buf)) {
+			if ((unsigned)(p-buf) >= sizeof(buf)) {
 				yyerror("string too long");
 				return (findeol());
 			}
@@ -408,14 +502,14 @@ nodigits:
 
 #define allowed_in_string(x) \
 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
-	x != '{' && x != '}' && x != '<' && x != '>' && \
-	x != '!' && x != '=' && x != '/' && x != '#' && \
+	x != '{' && x != '}' && \
+	x != '!' && x != '=' && x != '#' && \
 	x != ','))
 
-	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
+	if (isalnum(c) || c == ':' || c == '_') {
 		do {
 			*p++ = c;
-			if ((size_t)(p-buf) >= sizeof(buf)) {
+			if ((unsigned)(p-buf) >= sizeof(buf)) {
 				yyerror("string too long");
 				return (findeol());
 			}
@@ -424,7 +518,7 @@ nodigits:
 		*p = '\0';
 		if ((token = lookup(buf)) == STRING)
 			if ((yylval.v.string = strdup(buf)) == NULL)
-				errx(1, "yylex: strdup");
+				err(1, "%s", __func__);
 		return (token);
 	}
 	if (c == '\n') {
@@ -436,82 +530,187 @@ nodigits:
 	return (c);
 }
 
-struct file *
-pushfile(const char *name)
+static const struct got_error*
+pushfile(struct file **nfile, const char *name)
 {
-	struct file	*nfile;
+	const struct got_error* error = NULL;
 
-	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
-		gerror = got_error(GOT_ERR_NO_SPACE);
-		return (NULL);
-	}
-	if ((nfile->name = strdup(name)) == NULL) {
-		gerror = got_error(GOT_ERR_NO_SPACE);
+	if (((*nfile) = calloc(1, sizeof(struct file))) == NULL)
+		return got_error_from_errno2(__func__, "calloc");
+	if (((*nfile)->name = strdup(name)) == NULL) {
 		free(nfile);
-		return (NULL);
+		return got_error_from_errno2(__func__, "strdup");
 	}
-	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
-		gerror = got_error_from_errno2("parse_conf", nfile->name);
-		free(nfile->name);
-		free(nfile);
-		return (NULL);
+	if (((*nfile)->stream = fopen((*nfile)->name, "r")) == NULL) {
+		char *msg = NULL;
+		if (asprintf(&msg, "%s", (*nfile)->name) == -1)
+			return got_error_from_errno("asprintf");
+		error = got_error_msg(GOT_ERR_NO_CONFIG_FILE, msg);
+		free((*nfile)->name);
+		free((*nfile));
+		free(msg);
+		return error;
 	}
-	nfile->lineno = 1;
-	TAILQ_INSERT_TAIL(&files, nfile, entry);
-	return (nfile);
+	(*nfile)->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+	(*nfile)->ungetsize = 16;
+	(*nfile)->ungetbuf = malloc((*nfile)->ungetsize);
+	if ((*nfile)->ungetbuf == NULL) {
+		fclose((*nfile)->stream);
+		free((*nfile)->name);
+		free((*nfile));
+		return got_error_from_errno2(__func__, "malloc");
+	}
+	TAILQ_INSERT_TAIL(&files, (*nfile), entry);
+	return error;
 }
 
 int
 popfile(void)
 {
-	struct file	*prev;
+	struct file	*prev = NULL;
 
-	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
-		prev->errors += file->errors;
-
 	TAILQ_REMOVE(&files, file, entry);
 	fclose(file->stream);
 	free(file->name);
+	free(file->ungetbuf);
 	free(file);
 	file = prev;
 	return (file ? 0 : EOF);
 }
 
 const struct got_error*
-parse_conf(const char *filename, struct gotweb_conf *gconf)
+parse_gotweb_config(struct gotweb_config **gconf, const char *filename)
 {
-	static const struct got_error*	 error = NULL;
-
-	gw_conf = gconf;
-	if ((gw_conf->got_repos_path = strdup(D_GOTPATH)) == NULL)
-	err(1, "strdup");
-	if ((gw_conf->got_site_name = strdup(D_SITENAME)) == NULL)
-	err(1, "strdup");
-	if ((gw_conf->got_site_owner = strdup(D_SITEOWNER)) == NULL)
-	err(1, "strdup");
-	if ((gw_conf->got_site_link = strdup(D_SITELINK)) == NULL)
-	err(1, "strdup");
-	if ((gw_conf->got_logo = strdup(D_GOTLOGO)) == NULL)
-	err(1, "strdup");
-	if ((gw_conf->got_logo_url = strdup(D_GOTURL)) == NULL)
-	err(1, "strdup");
-	gw_conf->got_show_site_owner = D_SHOWSOWNER;
-	gw_conf->got_show_repo_owner = D_SHOWROWNER;
-	gw_conf->got_show_repo_age = D_SHOWAGE;
-	gw_conf->got_show_repo_description = D_SHOWDESC;
-	gw_conf->got_show_repo_cloneurl = D_SHOWURL;
-	gw_conf->got_max_repos = D_MAXREPO;
-	gw_conf->got_max_repos_display = D_MAXREPODISP;
-	gw_conf->got_max_commits_display = D_MAXCOMMITDISP;
-	if ((file = pushfile(filename)) == NULL) {
+	gw_conf.got_repos_path = strdup(D_GOTPATH);
+	if (gw_conf.got_repos_path == NULL) {
+		gerror = got_error_from_errno("strdup");
 		goto done;
 	}
+	gw_conf.got_site_name = strdup(D_SITENAME);
+	if (gw_conf.got_site_name == NULL) {
+		gerror = got_error_from_errno("strdup");
+		goto done;
+	}
+	gw_conf.got_site_owner = strdup(D_SITEOWNER);
+	if (gw_conf.got_site_owner == NULL) {
+		gerror = got_error_from_errno("strdup");
+		goto done;
+	}
+	gw_conf.got_site_link = strdup(D_SITELINK);
+	if (gw_conf.got_site_link == NULL) {
+		gerror = got_error_from_errno("strdup");
+		goto done;
+	}
+	gw_conf.got_logo = strdup(D_GOTLOGO);
+	if (gw_conf.got_logo == NULL) {
+		gerror = got_error_from_errno("strdup");
+		goto done;
+	}
+	gw_conf.got_logo_url = strdup(D_GOTURL);
+	if (gw_conf.got_logo_url == NULL) {
+		gerror = got_error_from_errno("strdup");
+		goto done;
+	}
+	gw_conf.got_show_site_owner = D_SHOWSOWNER;
+	gw_conf.got_show_repo_owner = D_SHOWROWNER;
+	gw_conf.got_show_repo_age = D_SHOWAGE;
+	gw_conf.got_show_repo_description = D_SHOWDESC;
+	gw_conf.got_show_repo_cloneurl = D_SHOWURL;
+	gw_conf.got_max_repos = D_MAXREPO;
+	gw_conf.got_max_repos_display = D_MAXREPODISP;
+	gw_conf.got_max_commits_display = D_MAXCOMMITDISP;
+
+	/*
+	 * We don't require that the gotweb config file exists
+	 * So reset gerror if it doesn't exist and goto done.
+	 */
+	gerror = pushfile(&file, filename);
+	if (gerror && gerror->code == GOT_ERR_NO_CONFIG_FILE) {
+		gerror = NULL;
+		goto done;
+	} else if (gerror)
+		return gerror;
 	topfile = file;
 
 	yyparse();
 	popfile();
-	if (gerror)
-		error = gerror;
 done:
-	return error;
+	*gconf = &gw_conf;
+	return gerror;
 }
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+	struct sym	*sym;
+
+	TAILQ_FOREACH(sym, &symhead, entry) {
+		if (strcmp(nam, sym->nam) == 0)
+			break;
+	}
+
+	if (sym != NULL) {
+		if (sym->persist == 1)
+			return (0);
+		else {
+			free(sym->nam);
+			free(sym->val);
+			TAILQ_REMOVE(&symhead, sym, entry);
+			free(sym);
+		}
+	}
+	if ((sym = calloc(1, sizeof(*sym))) == NULL)
+		return (-1);
+
+	sym->nam = strdup(nam);
+	if (sym->nam == NULL) {
+		free(sym);
+		return (-1);
+	}
+	sym->val = strdup(val);
+	if (sym->val == NULL) {
+		free(sym->nam);
+		free(sym);
+		return (-1);
+	}
+	sym->used = 0;
+	sym->persist = persist;
+	TAILQ_INSERT_TAIL(&symhead, sym, entry);
+	return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+	char	*sym, *val;
+	int	ret;
+	size_t	len;
+
+	if ((val = strrchr(s, '=')) == NULL)
+		return (-1);
+
+	len = strlen(s) - strlen(val) + 1;
+	if ((sym = malloc(len)) == NULL)
+		errx(1, "cmdline_symset: malloc");
+
+	strlcpy(sym, s, len);
+
+	ret = symset(sym, val + 1, 1);
+	free(sym);
+
+	return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+	struct sym	*sym;
+
+	TAILQ_FOREACH(sym, &symhead, entry) {
+		if (strcmp(nam, sym->nam) == 0) {
+			sym->used = 1;
+			return (sym->val);
+		}
+	}
+	return (NULL);
+}