opendoas

A portable version of the OpenBSD `doas` command
git clone https://pi.duncano.de/git/opendoas.git
Log | Files | Refs | README | LICENSE

commit edf4632491d75d7e56c5293de3c33dc2de74231d
parent f45bae9626d4c8eac50ee347a28fec29bffd92ff
Author: Vadim Zhukov <zhuk@openbsd.org>
Date:   Fri, 24 Jul 2015 06:36:42 +0000

Further improve syntax error reporting in doas:

  - teach parser to recover after error, allowing to report many errors
    instead of the first one only;
  - fix remaining error printouts without exact position.

Some ideas were taken from diff sent by dlg@ earlier, thanks!

okay tedu@, dlg@

Diffstat:
doas.c | 4+++-
doas.h | 3++-
parse.y | 56+++++++++++++++++++++++++++++++-------------------------
3 files changed, 36 insertions(+), 27 deletions(-)

diff --git a/doas.c b/doas.c @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.c,v 1.19 2015/07/22 05:37:23 deraadt Exp $ */ +/* $OpenBSD: doas.c,v 1.20 2015/07/22 16:35:03 zhuk Exp $ */ /* * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> * @@ -173,6 +173,8 @@ parseconfig(const char *filename) yyparse(); fclose(yyfp); + if (parse_errors) + exit(1); } static int diff --git a/doas.h b/doas.h @@ -1,4 +1,4 @@ -/* $OpenBSD: doas.h,v 1.2 2015/07/18 07:49:16 bcallah Exp $ */ +/* $OpenBSD: doas.h,v 1.3 2015/07/21 11:04:06 zhuk Exp $ */ struct rule { int action; @@ -12,6 +12,7 @@ struct rule { extern struct rule **rules; extern int nrules, maxrules; +extern int parse_errors; size_t arraylen(const char **); diff --git a/parse.y b/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.8 2015/07/21 16:12:04 tedu Exp $ */ +/* $OpenBSD: parse.y,v 1.9 2015/07/22 20:15:24 zhuk Exp $ */ /* * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org> * @@ -38,6 +38,8 @@ typedef struct { }; const char *str; }; + int lineno; + int colno; } yystype; #define YYSTYPE yystype @@ -45,6 +47,7 @@ FILE *yyfp; struct rule **rules; int nrules, maxrules; +int parse_errors = 0; void yyerror(const char *, ...); int yylex(void); @@ -61,6 +64,7 @@ int yyparse(void); grammar: /* empty */ | grammar '\n' | grammar rule '\n' + | error '\n' ; rule: action ident target cmd { @@ -100,9 +104,10 @@ options: /* none */ $$.options = $1.options | $2.options; $$.envlist = $1.envlist; if ($2.envlist) { - if ($$.envlist) - errx(1, "can't have two keepenv sections"); - else + if ($$.envlist) { + yyerror("can't have two keepenv sections"); + YYERROR; + } else $$.envlist = $2.envlist; } } ; @@ -171,7 +176,10 @@ yyerror(const char *fmt, ...) va_list va; va_start(va, fmt); - verrx(1, fmt, va); + vfprintf(stderr, fmt, va); + va_end(va); + fprintf(stderr, " at line %d\n", yylval.lineno + 1); + parse_errors++; } struct keyword { @@ -190,10 +198,8 @@ struct keyword { int yylex(void) { - static int colno = 1, lineno = 1; - char buf[1024], *ebuf, *p, *str; - int i, c, quotes = 0, escape = 0, qpos = 0, nonkw = 0; + int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0; p = buf; ebuf = buf + sizeof(buf); @@ -201,13 +207,13 @@ yylex(void) repeat: /* skip whitespace first */ for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp)) - colno++; + yylval.colno++; /* check for special one-character constructions */ switch (c) { case '\n': - colno = 1; - lineno++; + yylval.colno = 0; + yylval.lineno++; /* FALLTHROUGH */ case '{': case '}': @@ -217,19 +223,18 @@ repeat: while ((c = getc(yyfp)) != '\n') if (c == EOF) return 0; - colno = 1; - lineno++; + yylval.colno = 0; + yylval.lineno++; return c; case EOF: return 0; } /* parsing next word */ - for (;; c = getc(yyfp), colno++) { + for (;; c = getc(yyfp), yylval.colno++) { switch (c) { case '\0': - yyerror("unallowed character NUL at " - "line %d, column %d", lineno, colno); + yyerror("unallowed character NUL in column %d", yylval.colno + 1); escape = 0; continue; case '\\': @@ -239,8 +244,8 @@ repeat: break; case '\n': if (quotes) - yyerror("unterminated quotes at line %d, column %d", - lineno, qpos); + yyerror("unterminated quotes in column %d", + qpos + 1); if (escape) { nonkw = 1; escape = 0; @@ -249,11 +254,12 @@ repeat: goto eow; case EOF: if (escape) - yyerror("unterminated escape at line %d, column %d", - lineno, colno - 1); + yyerror("unterminated escape in column %d", + yylval.colno); if (quotes) - yyerror("unterminated quotes at line %d, column %d", - lineno, qpos); + yyerror("unterminated quotes in column %d", + qpos + 1); + goto eow; /* FALLTHROUGH */ case '{': case '}': @@ -268,14 +274,14 @@ repeat: quotes = !quotes; if (quotes) { nonkw = 1; - qpos = colno; + qpos = yylval.colno; } continue; } } *p++ = c; if (p == ebuf) - yyerror("too long line %d", lineno); + yyerror("too long line"); escape = 0; } @@ -290,7 +296,7 @@ eow: */ if (c == EOF) return 0; - else if (!qpos) /* accept, e.g., empty args: cmd foo args "" */ + else if (qpos == -1) /* accept, e.g., empty args: cmd foo args "" */ goto repeat; } if (!nonkw) {