aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--Makefile7
-rw-r--r--README.md4
-rwxr-xr-xconfigure14
-rw-r--r--gmid.h6
-rw-r--r--parse.y198
6 files changed, 203 insertions, 33 deletions
diff --git a/ChangeLog b/ChangeLog
index d4b2817..76e321f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2021-06-16 Omar Polo <op@omarpolo.com>
+
+ * parse.y (yylex): drop the dependency on lex by implementing
+ yylex by ourselves (the actual implementation is based off doas'
+ parse.y). This gave us various benefits, like cleaner code, \ to
+ break long lines, better handling of quotes etc...
+
2021-06-11 Omar Polo <op@omarpolo.com>
* parse.y (servopt): add `param' keyword
diff --git a/Makefile b/Makefile
index 5f91bd7..be01f67 100644
--- a/Makefile
+++ b/Makefile
@@ -7,15 +7,12 @@ Makefile.local: configure
include Makefile.local
-lex.yy.c: lex.l y.tab.c
- ${LEX} lex.l
-
y.tab.c: parse.y
- ${YACC} -b y -d parse.y
+ ${YACC} -b y parse.y
SRCS = gmid.c iri.c utf8.c ex.c server.c sandbox.c mime.c puny.c \
utils.c log.c dirs.c fcgi.c
-OBJS = ${SRCS:.c=.o} lex.yy.o y.tab.o ${COMPAT}
+OBJS = ${SRCS:.c=.o} y.tab.o ${COMPAT}
gmid: ${OBJS}
${CC} ${OBJS} -o gmid ${LDFLAGS}
diff --git a/README.md b/README.md
index 8b2ebeb..67cd275 100644
--- a/README.md
+++ b/README.md
@@ -92,8 +92,8 @@ server "example.com" {
## Building
gmid depends on a POSIX libc, libevent2, OpenSSL/LibreSSL and libtls
-(provided either by LibreSSL or libretls). At build time, flex and
-yacc (or GNU bison) are also needed.
+(provided either by LibreSSL or libretls). At build time, yacc (or
+GNU bison) is also needed.
The build is as simple as
diff --git a/configure b/configure
index 84ed361..d5f4035 100755
--- a/configure
+++ b/configure
@@ -40,7 +40,6 @@ CFLAGS="${CFLAGS} -g -W -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="${CFLAGS} -Wwrite-strings -Wno-unused-parameter"
LDFLAGS="-ltls -levent"
LD_IMSG=
-LEX=lex
STATIC=
YACC=yacc
@@ -74,17 +73,6 @@ if which pkg-config 2>/dev/null 1>&2; then
esac
fi
-# auto detect lex/flex
-which ${LEX} 2>/dev/null 1>&2 || {
- echo "${LEX} not found: trying flex" 1>&2
- echo "${LEX} not found: trying flex" 1>&3
- LEX=flex
- which ${LEX} 2>/dev/null 1>&2 || {
- echo "${LEX} not found: giving up" 1>&2
- echo "${LEX} not found: giving up" 1>&3
- }
-}
-
# auto detect yacc/bison
which ${YACC} 2>/dev/null 1>&2 || {
echo "${YACC} not found: trying bison" 1>&2
@@ -112,7 +100,6 @@ for keyvals in "$@"; do
CFLAGS) CFLAGS="$val" ;;
DESTDIR) DESTDIR="$val" ;;
LDFLAGS) LDFLAGS="$val" ;;
- LEX) LEX="$lex" ;;
PREFIX) PREFIX="$val" ;;
YACC) YACC="$val" ;;
*)
@@ -398,7 +385,6 @@ CC = ${CC}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS} ${LD_IMSG}
YACC = ${YACC}
-LEX = ${LEX}
STATIC = ${STATIC}
PREFIX = ${PREFIX}
BINDIR = ${BINDIR}
diff --git a/gmid.h b/gmid.h
index a013af5..731a4fd 100644
--- a/gmid.h
+++ b/gmid.h
@@ -310,12 +310,6 @@ void init_config(void);
void free_config(void);
void drop_priv(void);
-/* provided by lex/yacc */
-extern FILE *yyin;
-extern int yylineno;
-extern int yyparse(void);
-extern int yylex(void);
-
void yyerror(const char*, ...);
int parse_portno(const char*);
void parse_conf(const char*);
diff --git a/parse.y b/parse.y
index 661e8e8..9182fd4 100644
--- a/parse.y
+++ b/parse.y
@@ -1,4 +1,3 @@
-/* -*- mode: fundamental; indent-tabs-mode: t; -*- */
%{
/*
@@ -17,13 +16,17 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "gmid.h"
+FILE *yyfp;
+
/*
* #define YYDEBUG 1
* int yydebug = 1;
@@ -32,12 +35,14 @@
struct vhost *host;
struct location *loc;
-int goterror = 0;
+static int goterror;
+static int lineno, colno;
static struct vhost *new_vhost(void);
static struct location *new_location(void);
void yyerror(const char*, ...);
+static int yylex(void);
int parse_portno(const char*);
void parse_conf(const char*);
char *ensure_absolute_path(char*);
@@ -109,7 +114,7 @@ vhost : TSERVER TSTRING {
if (strstr($2, "xn--") != NULL) {
warnx("%s:%d \"%s\" looks like punycode: "
"you should use the decoded hostname.",
- config_path, yylineno, $2);
+ config_path, lineno, $2);
}
} '{' servopts locations '}' {
@@ -278,12 +283,193 @@ yyerror(const char *msg, ...)
goterror = 1;
va_start(ap, msg);
- fprintf(stderr, "%s:%d: ", config_path, yylineno);
+ fprintf(stderr, "%s:%d: ", config_path, lineno);
vfprintf(stderr, msg, ap);
fprintf(stderr, "\n");
va_end(ap);
}
+static struct keyword {
+ const char *word;
+ int token;
+} keywords[] = {
+ {"alias", TALIAS},
+ {"auto", TAUTO},
+ {"block", TBLOCK},
+ {"ca", TCA},
+ {"cert", TCERT},
+ {"cgi", TCGI},
+ {"chroot", TCHROOT},
+ {"client", TCLIENT},
+ {"default", TDEFAULT},
+ {"entrypoint", TENTRYPOINT},
+ {"env", TENV},
+ {"fastcgi", TFASTCGI},
+ {"index", TINDEX},
+ {"ipv6", TIPV6},
+ {"key", TKEY},
+ {"lang", TLANG},
+ {"location", TLOCATION},
+ {"log", TLOG},
+ {"mime", TMIME},
+ {"param", TPARAM},
+ {"port", TPORT},
+ {"prefork", TPREFORK},
+ {"protocols", TPROTOCOLS},
+ {"require", TREQUIRE},
+ {"return", TRETURN},
+ {"root", TROOT},
+ {"server", TSERVER},
+ {"spawn", TSPAWN},
+ {"strip", TSTRIP},
+ {"tcp", TTCP},
+ {"type", TTYPE},
+ {"user", TUSER},
+};
+
+/*
+ * Taken an adapted from doas' parse.y
+ */
+static int
+yylex(void)
+{
+ char buf[1024], *ebuf, *p, *str;
+ int c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
+ size_t i;
+
+ p = buf;
+ ebuf = buf + sizeof(buf);
+
+repeat:
+ /* skip whitespace first */
+ for (c = getc(yyfp); isspace(c); c = getc(yyfp)) {
+ colno++;
+ if (c == '\n') {
+ lineno++;
+ colno = 0;
+ }
+ }
+
+ /* check for special one-character constructions */
+ switch (c) {
+ case '{':
+ case '}':
+ return c;
+ case '#':
+ /* skip comments; NUL is allowed; no continuation */
+ while ((c = getc(yyfp)) != '\n')
+ if (c == EOF)
+ goto eof;
+ colno = 0;
+ lineno++;
+ goto repeat;
+ case EOF:
+ goto eof;
+ }
+
+ /* parsing next word */
+ for (;; c = getc(yyfp), colno++) {
+ switch (c) {
+ case '\0':
+ yyerror("unallowed character NULL in column %d",
+ colno+1);
+ escape = 0;
+ continue;
+ case '\\':
+ escape = !escape;
+ if (escape)
+ continue;
+ break;
+ case '\n':
+ if (quotes)
+ yyerror("unterminated quotes in column %d",
+ colno+1);
+ if (escape) {
+ nonkw = 1;
+ escape = 0;
+ colno = 0;
+ lineno++;
+ }
+ goto eow;
+ case EOF:
+ if (escape)
+ yyerror("unterminated escape in column %d",
+ colno);
+ if (quotes)
+ yyerror("unterminated quotes in column %d",
+ qpos+1);
+ goto eow;
+ case '{':
+ case '}':
+ case '#':
+ case ' ':
+ case '\t':
+ if (!escape && !quotes)
+ goto eow;
+ break;
+ case '"':
+ if (!escape) {
+ quotes = !quotes;
+ if (quotes) {
+ nonkw = 1;
+ qpos = colno;
+ }
+ continue;
+ }
+ }
+ *p++ = c;
+ if (p == ebuf) {
+ yyerror("line too long");
+ p = buf;
+ }
+ escape = 0;
+ }
+
+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;
+ }
+ if (!nonkw) {
+ for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); ++i) {
+ if (!strcmp(buf, keywords[i].word))
+ return keywords[i].token;
+ }
+ }
+ c = *buf;
+ if (!nonkw && (c == '-' || isdigit(c))) {
+ yylval.num = parse_portno(buf);
+ return TNUM;
+ }
+ if (!nonkw && !strcmp(buf, "on")) {
+ yylval.num = 1;
+ return TBOOL;
+ }
+ if (!nonkw && !strcmp(buf, "off")) {
+ yylval.num = 0;
+ return TBOOL;
+ }
+ if ((str = strdup(buf)) == NULL)
+ err(1, "%s", __func__);
+ yylval.str = str;
+ return TSTRING;
+
+eof:
+ if (ferror(yyfp))
+ yyerror("input error reading config");
+ return 0;
+}
+
int
parse_portno(const char *p)
{
@@ -300,10 +486,10 @@ void
parse_conf(const char *path)
{
config_path = path;
- if ((yyin = fopen(path, "r")) == NULL)
+ if ((yyfp = fopen(path, "r")) == NULL)
err(1, "cannot open config: %s", path);
yyparse();
- fclose(yyin);
+ fclose(yyfp);
if (goterror)
exit(1);