commit - a5d822e542a927e88f655842273356225370c11f
commit + c39be742cf8348232f6a527b19c42f764e80aae0
blob - d60c900a549371abb7163f884116e2e8af7303ed
blob + bc44e2438232742dab541f12aadbc2b1760a7726
--- ChangeLog
+++ ChangeLog
+2021-07-09 Omar Polo <op@omarpolo.com>
+
+ * parse.y (STRING): add `include' directive
+
2021-07-08 Omar Polo <op@omarpolo.com>
* parse.y (option): rename `mime MIME EXT' to `map MIME to-ext EXT', but retain the old `mime' for compatibility.
blob - 457ea98b2f921158136c1d15640307c71480ab92
blob + 8f450de756554371b618add63490d625cf99dc3a
--- parse.y
+++ parse.y
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2018 Florian Obser <florian@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.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
#include "gmid.h"
-FILE *yyfp;
+TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
+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;
+} *file, *topfile;
-typedef struct {
- union {
- char *str;
- int num;
- } v;
- int lineno;
- int colno;
-} yystype;
-#define YYSTYPE yystype
+struct file *pushfile(const char *, int);
+int popfile(void);
+int yyparse(void);
+int yylex(void);
+void yyerror(const char *, ...)
+ __attribute__((__format__ (printf, 1, 2)))
+ __attribute__((__nonnull__ (1)));
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int igetc(void);
+int lgetc(int);
+void lungetc(int);
+int findeol(void);
/*
* #define YYDEBUG 1
* 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;
char *val;
};
-struct vhost *host;
-struct location *loc;
+int symset(const char *, const char *, int);
+char *symget(const char *);
-static int goterror;
-
-static struct vhost *new_vhost(void);
-static struct location *new_location(void);
-
-void yyerror(const char*, ...);
-int kw_cmp(const void *, const void *);
-static int yylex(void);
+struct vhost *new_vhost(void);
+struct location *new_location(void);
int parse_portno(const char*);
-void parse_conf(const char*);
char *ensure_absolute_path(char*);
int check_block_code(int);
char *check_block_fmt(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 *);
+static struct vhost *host;
+static struct location *loc;
+static int errors;
+
+typedef struct {
+ union {
+ char *string;
+ int number;
+ } v;
+ int lineno;
+} YYSTYPE;
+
%}
/* for bison: */
%token TIPV6 TPORT TPROTOCOLS TMIME TDEFAULT TTYPE TCHROOT TUSER TSERVER
%token TPREFORK TLOCATION TCERT TKEY TROOT TCGI TENV TLANG TLOG TINDEX TAUTO
%token TSTRIP TBLOCK TRETURN TENTRYPOINT TREQUIRE TCLIENT TCA TALIAS TTCP
-%token TFASTCGI TSPAWN TPARAM TMAP TTOEXT
+%token TFASTCGI TSPAWN TPARAM TMAP TTOEXT INCLUDE TON TOFF
-%token TERR
+%token ERROR
-%token <v.str> TSTRING
-%token <v.num> TNUM
-%token <v.num> TBOOL
+%token <v.string> STRING
+%token <v.number> NUM
-%type <v.str> string
+%type <v.number> bool
+%type <v.string> string
%%
conf : /* empty */
- | conf var
- | conf option
- | conf vhost
+ | conf include '\n'
+ | conf '\n'
+ | conf varset '\n'
+ | conf option '\n'
+ | conf vhost '\n'
+ | conf error '\n' { file->errors++; }
;
-string : string TSTRING {
+include : INCLUDE STRING {
+ struct file *nfile;
+
+ if ((nfile = pushfile($2, 0)) == NULL) {
+ yyerror("failed to include file %s", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+
+ file = nfile;
+ lungetc('\n');
+ }
+ ;
+
+bool : TON { $$ = 1; }
+ | TOFF { $$ = 0; }
+ ;
+
+string : string STRING {
if (asprintf(&$$, "%s%s", $1, $2) == -1) {
free($1);
free($2);
free($1);
free($2);
}
- | TSTRING
+ | STRING
;
-var : TSTRING '=' string {
+varset : STRING '=' string {
char *s = $1;
while (*s++) {
- if (isspace(*s)) {
+ if (isspace((unsigned char)*s)) {
yyerror("macro name cannot contain "
"whitespaces");
free($1);
;
option : TCHROOT string { conf.chroot = $2; }
- | TIPV6 TBOOL { conf.ipv6 = $2; }
- | TMIME TSTRING string {
+ | TIPV6 bool { conf.ipv6 = $2; }
+ | TMIME STRING string {
fprintf(stderr, "%s:%d: `mime MIME EXT' is deprecated and "
"will be removed in a future version, "
"please use the new syntax: `map MIME to-ext EXT'",
add_mime(&conf.mime, $2, $3);
}
| TMAP string TTOEXT string { add_mime(&conf.mime, $2, $4); }
- | TPORT TNUM { conf.port = $2; }
- | TPREFORK TNUM { conf.prefork = check_prefork_num($2); }
+ | TPORT NUM { conf.port = $2; }
+ | TPREFORK NUM { conf.prefork = check_prefork_num($2); }
| TPROTOCOLS string {
if (tls_config_parse_protocols(&conf.protos, $2) == -1)
yyerror("invalid protocols string \"%s\"", $2);
host->domain = $2;
if (strstr($2, "xn--") != NULL) {
- warnx("%s:%d:%d \"%s\" looks like punycode: "
+ warnx("%s:%d \"%s\" looks like punycode: "
"you should use the decoded hostname.",
- config_path, yylval.lineno+1, yylval.colno,
- $2);
+ config_path, yylval.lineno+1, $2);
}
- } '{' servopts locations '}' {
-
+ } '{' optnl servopts locations '}' {
if (host->cert == NULL || host->key == NULL)
yyerror("invalid vhost definition: %s", $2);
}
;
servopts : /* empty */
- | servopts servopt
+ | servopts servopt optnl
;
servopt : TALIAS string {
;
locations : /* empty */
- | locations location
+ | locations location optnl
;
-location : TLOCATION { advance_loc(); } string '{' locopts '}' {
+location : TLOCATION { advance_loc(); } string '{' optnl locopts '}' {
/* drop the starting '/' if any */
if (*$3 == '/')
memmove($3, $3+1, strlen($3));
;
locopts : /* empty */
- | locopts locopt
+ | locopts locopt optnl
;
-locopt : TAUTO TINDEX TBOOL { loc->auto_index = $3 ? 1 : -1; }
- | TBLOCK TRETURN TNUM string {
+locopt : TAUTO TINDEX bool { loc->auto_index = $3 ? 1 : -1; }
+ | TBLOCK TRETURN NUM string {
only_once(loc->block_fmt, "block");
loc->block_fmt = check_block_fmt($4);
loc->block_code = check_block_code($3);
}
- | TBLOCK TRETURN TNUM {
+ | TBLOCK TRETURN NUM {
only_once(loc->block_fmt, "block");
loc->block_fmt = xstrdup("temporary failure");
loc->block_code = check_block_code($3);
only_once(loc->lang, "lang");
loc->lang = $2;
}
- | TLOG TBOOL { loc->disable_log = !$2; }
+ | TLOG bool { loc->disable_log = !$2; }
| TREQUIRE TCLIENT TCA string {
only_once(loc->reqca, "require client ca");
ensure_absolute_path($4);
only_once(loc->dir, "root");
loc->dir = ensure_absolute_path($2);
}
- | TSTRIP TNUM { loc->strip = check_strip_no($2); }
+ | TSTRIP NUM { loc->strip = check_strip_no($2); }
;
fastcgi : TSPAWN string {
only_oncei(loc->fcgi, "fastcgi");
loc->fcgi = fastcgi_conf($1, NULL, NULL);
}
- | TTCP string TPORT TNUM {
+ | TTCP string TPORT NUM {
char *c;
if (asprintf(&c, "%d", $4) == -1)
err(1, "asprintf");
}
;
-%%
-
-static struct vhost *
-new_vhost(void)
-{
- return xcalloc(1, sizeof(struct vhost));
-}
-
-static struct location *
-new_location(void)
-{
- struct location *l;
+optnl : '\n' optnl /* zero or more newlines */
+ | /*empty*/
+ ;
- l = xcalloc(1, sizeof(*l));
- l->dirfd = -1;
- l->fcgi = -1;
- return l;
-}
+%%
-void
-yyerror(const char *msg, ...)
-{
- va_list ap;
-
- goterror = 1;
-
- va_start(ap, msg);
- fprintf(stderr, "%s:%d: ", config_path, yylval.lineno);
- vfprintf(stderr, msg, ap);
- fprintf(stderr, "\n");
- va_end(ap);
-}
-
static struct keyword {
const char *word;
int token;
{"log", TLOG},
{"map", TMAP},
{"mime", TMIME},
+ {"off", TOFF},
+ {"on", TON},
{"param", TPARAM},
{"port", TPORT},
{"prefork", TPREFORK},
{"user", TUSER},
};
+void
+yyerror(const char *msg, ...)
+{
+ va_list ap;
+
+ file->errors++;
+
+ va_start(ap, msg);
+ fprintf(stderr, "%s:%d: ", config_path, yylval.lineno);
+ vfprintf(stderr, msg, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+}
+
int
kw_cmp(const void *k, const void *e)
{
return strcmp(k, ((struct keyword *)e)->word);
}
-/*
- * Taken an adapted from doas' parse.y
- */
-static int
-yylex(void)
+int
+lookup(char *s)
{
- struct keyword *kw;
- char buf[8096], *ebuf, *p, *str, *v, *val;
- int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
- size_t len;
+ const struct keyword *p;
- p = buf;
- ebuf = buf + sizeof(buf);
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
-repeat:
- /* skip whitespace first */
- for (c = getc(yyfp); isspace(c); c = getc(yyfp)) {
- yylval.colno++;
- if (c == '\n') {
- yylval.lineno++;
- yylval.colno = 0;
- }
+ if (p)
+ return p->token;
+ else
+ return STRING;
+}
+
+#define START_EXPAND 1
+#define DONE_EXPAND 2
+
+static int expanding;
+
+int
+igetc(void)
+{
+ int c;
+
+ 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;
+}
- /* check for special one-character constructions */
- switch (c) {
- case '{':
- case '}':
+int
+lgetc(int quotec)
+{
+ int c, next;
+
+ if (quotec) {
+ if ((c = igetc()) == EOF) {
+ yyerror("reached end of file while parsing "
+ "quoted string");
+ if (file == topfile || popfile() == EOF)
+ return EOF;
+ return quotec;
+ }
return c;
- case '#':
- /* skip comments; NUL is allowed; no continuation */
- while ((c = getc(yyfp)) != '\n')
- if (c == EOF)
- goto eof;
- yylval.colno = 0;
- yylval.lineno++;
- goto repeat;
- case '=':
- return c;
- case EOF:
- goto eof;
}
- /* parsing next word */
- for (;; c = getc(yyfp), yylval.colno++) {
- switch (c) {
- case '\0':
- yyerror("unallowed character NULL in column %d",
- yylval.colno+1);
- escape = 0;
- continue;
- case '\\':
- escape = !escape;
- if (escape)
- continue;
+ while ((c = igetc()) == '\\') {
+ next = igetc();
+ if (next != '\n') {
+ c = next;
break;
+ }
+ yylval.lineno = file->lineno;
+ file->lineno++;
+ }
- /* expand macros in-place */
- case '$':
- if (!escape && !quotes) {
- 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;
- break;
- }
- p = v;
- if ((val = symget(p)) == NULL) {
- yyerror("macro '%s' not defined", v);
- return TERR;
- }
- len = strlen(val);
- if (p + len >= ebuf - 1) {
- yyerror("after macro-expansion, "
- "string too long");
- return TERR;
- }
- *p = '\0';
- strlcat(p, val, ebuf - p);
- p += len;
- nonkw = 1;
- goto eow;
- }
+ 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;
+}
+
+void
+lungetc(int c)
+{
+ if (c == EOF)
+ return;
+
+ if (file->ungetpos >= file->ungetsize) {
+ void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
+ if (p == NULL)
+ err(1, "lungetc");
+ file->ungetbuf = p;
+ file->ungetsize *= 2;
+ }
+ file->ungetbuf[file->ungetpos++] = c;
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ /* Skip to either EOF or the first real EOL. */
+ while (1) {
+ c = lgetc(0);
+ if (c == '\n') {
+ file->lineno++;
break;
- case '\n':
- if (quotes)
- yyerror("unterminated quotes in column %d",
- yylval.colno+1);
- if (escape) {
- nonkw = 1;
- escape = 0;
- yylval.colno = 0;
- yylval.lineno++;
- }
- goto eow;
- case EOF:
- if (escape)
- yyerror("unterminated escape in column %d",
- yylval.colno);
- if (quotes)
- yyerror("unterminated quotes in column %d",
- qpos+1);
- goto eow;
- case '{':
- case '}':
- case '#':
- case ' ':
- case '\t':
- if (!escape && !quotes)
- goto eow;
+ }
+ if (c == EOF)
break;
- case '"':
- if (!escape) {
- quotes = !quotes;
- if (quotes) {
- nonkw = 1;
- qpos = yylval.colno;
- }
+ }
+ return ERROR;
+}
+
+int
+yylex(void)
+{
+ unsigned char buf[8096];
+ unsigned char *p, *val;
+ int quotec, next, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(0)) == ' ' || c == '\t')
+ ; /* nothing */
+
+ yylval.lineno = file->lineno;
+ 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;
}
- *p++ = c;
- if (p == ebuf) {
- yyerror("line too long");
- p = buf;
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return findeol();
}
- escape = 0;
+ p = val + strlen(val) - 1;
+ lungetc(DONE_EXPAND);
+ while (p >= val) {
+ lungetc(*p);
+ p--;
+ }
+ lungetc(START_EXPAND);
+ goto top;
}
-eow:
- *p = 0;
- if (c != EOF)
- ungetc(c, yyfp);
- if (p == buf) {
- /*
- * There could be a number of reason for empty buffer,
- * and we handle all of them here, to avoid cluttering
- * the main loop.
- */
- if (c == EOF)
- goto eof;
- else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */
- goto repeat;
+ switch (c) {
+ case '\'':
+ case '"':
+ quotec = c;
+ while (1) {
+ if ((c = lgetc(quotec)) == EOF)
+ return 0;
+ if (c == '\n') {
+ file->lineno++;
+ continue;
+ } else if (c == '\\') {
+ if ((next = lgetc(quotec)) == EOF)
+ return (0);
+ if (next == quotec || next == ' ' ||
+ next == '\t')
+ c = next;
+ else if (next == '\n') {
+ file->lineno++;
+ continue;
+ } else
+ lungetc(next);
+ } else if (c == quotec) {
+ *p = '\0';
+ break;
+ } else if (c == '\0') {
+ yyerror("syntax error");
+ return findeol();
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return findeol();
+ }
+ *p++ = c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ err(1, "yylex: strdup");
+ return STRING;
}
- if (!nonkw) {
- kw = bsearch(buf, keywords, sizeof(keywords)/sizeof(keywords[0]),
- sizeof(keywords[0]), kw_cmp);
- if (kw != NULL)
- return kw->token;
+
+#define allowed_to_end_number(x) \
+ (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
+
+ if (c == '-' || isdigit(c)) {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && isdigit(c));
+ lungetc(c);
+ if (p == buf + 1 && buf[0] == '-')
+ goto nodigits;
+ if (c == EOF || allowed_to_end_number(c)) {
+ const char *errstr = NULL;
+
+ *p = '\0';
+ yylval.v.number = strtonum(buf, LLONG_MIN,
+ LLONG_MAX, &errstr);
+ if (errstr) {
+ yyerror("\"%s\" invalid number: %s",
+ buf, errstr);
+ return findeol();
+ }
+ return NUM;
+ } else {
+nodigits:
+ while (p > buf + 1)
+ lungetc(*--p);
+ c = *--p;
+ if (c == '-')
+ return c;
+ }
}
- c = *buf;
- if (!nonkw && (c == '-' || isdigit(c))) {
- yylval.v.num = parse_portno(buf);
- return TNUM;
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_' || c == '/' || c == '.') {
+ do {
+ *p++ = c;
+ if ((size_t)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return findeol();
+ }
+ } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ yylval.v.string = xstrdup(buf);
+ return token;
}
- if (!nonkw && !strcmp(buf, "on")) {
- yylval.v.num = 1;
- return TBOOL;
+ if (c == '\n') {
+ yylval.lineno = file->lineno;
+ file->lineno++;
}
- if (!nonkw && !strcmp(buf, "off")) {
- yylval.v.num = 0;
- return TBOOL;
+ if (c == EOF)
+ return 0;
+ return c;
+}
+
+struct file *
+pushfile(const char *name, int secret)
+{
+ struct file *nfile;
+
+ nfile = xcalloc(1, sizeof(*nfile));
+ nfile->name = xstrdup(name);
+ if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
+ yyerror("can't open %s: %s", nfile->name,
+ strerror(errno));
+ free(nfile->name);
+ free(nfile);
+ return NULL;
}
- if ((str = strdup(buf)) == NULL)
- err(1, "%s", __func__);
- yylval.v.str = str;
- return TSTRING;
+ nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
+ nfile->ungetsize = 16;
+ nfile->ungetbuf = xcalloc(1, nfile->ungetsize);
+ TAILQ_INSERT_TAIL(&files, nfile, entry);
+ return nfile;
+}
-eof:
- if (ferror(yyfp))
- yyerror("input error reading config");
- return 0;
-}
+int
+popfile(void)
+{
+ struct file *prev;
-int
-parse_portno(const char *p)
-{
- const char *errstr;
- int n;
+ if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
+ prev->errors += file->errors;
- n = strtonum(p, 0, UINT16_MAX, &errstr);
- if (errstr != NULL)
- yyerror("port number is %s: %s", errstr, p);
- return n;
+ TAILQ_REMOVE(&files, file, entry);
+ fclose(file->stream);
+ free(file->name);
+ free(file->ungetbuf);
+ free(file);
+ file = prev;
+ return file ? 0 : EOF;
}
void
-parse_conf(const char *path)
+parse_conf(const char *filename)
{
- struct sym *sym, *next;
+ struct sym *sym, *next;
- config_path = path;
- if ((yyfp = fopen(path, "r")) == NULL)
- err(1, "cannot open config: %s", path);
+ file = pushfile(filename, 0);
+ if (file == NULL)
+ return;
+ topfile = file;
+
yyparse();
- fclose(yyfp);
+ errors = file->errors;
+ popfile();
- if (goterror)
- exit(1);
-
- if (TAILQ_FIRST(&hosts)->domain == NULL)
- errx(1, "no vhost defined in %s", path);
-
- /* free unused macros */
+ /* Free macros and check which have not been used. */
TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
/* TODO: warn if !sym->used */
if (!sym->persist) {
free(sym);
}
}
+
+ if (errors)
+ exit(1);
}
+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 *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entry) {
+ if (strcmp(nam, sym->name) == 0) {
+ sym->used = 1;
+ return sym->val;
+ }
+ }
+ return NULL;
+}
+
+struct vhost *
+new_vhost(void)
+{
+ return xcalloc(1, sizeof(struct vhost));
+}
+
+struct location *
+new_location(void)
+{
+ struct location *l;
+
+ l = xcalloc(1, sizeof(*l));
+ l->dirfd = -1;
+ l->fcgi = -1;
+ return l;
+}
+
+int
+parse_portno(const char *p)
+{
+ const char *errstr;
+ int n;
+
+ n = strtonum(p, 0, UINT16_MAX, &errstr);
+ if (errstr != NULL)
+ yyerror("port number is %s: %s", errstr, p);
+ return n;
+}
+
+char *
ensure_absolute_path(char *path)
{
if (path == NULL || *path != '/')
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;
-}