commit - fb863fa46f4ac9493cff1e6bb9aa13d753bd5fb8
commit + c34ec41754978e41240e8aedd4e5d3a9e6362635
blob - 35d7892394e81db18aca90b995cfedb1ef98f9f0
blob + 836222204f43d83aad1ade8a0feac1ddaf124f5f
--- got/parse.y
+++ got/parse.y
/*
- * 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
#include <string.h>
#include <unistd.h>
+#include <got_error.h>
#include <got_object.h>
#include <got_reference.h>
#include <got_repository.h>
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;
}
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;
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;
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
#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"
#define BUFFER_SIZE 2048
-struct gotweb_conf {
+struct gotweb_config {
char *got_repos_path;
char *got_site_name;
char *got_site_owner;
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
/*
- * 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.
%{
#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);
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);
__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;
%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;
}
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 {
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
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 },
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)
return (c);
}
- while ((c = getc(file->stream)) == '\\') {
- next = getc(file->stream);
+ while ((c = igetc()) == '\\') {
+ next = igetc();
if (next != '\n') {
c = next;
break;
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
{
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;
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 */
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 '"':
} 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++;
}
yylval.v.string = strdup(buf);
if (yylval.v.string == NULL)
- errx(1, "yylex: strdup");
+ err(1, "%s", __func__);
return (STRING);
}
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());
}
#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());
}
*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') {
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);
+}