-.\" $OpenBSD: doas.conf.5,v 1.8 2015/07/21 11:04:06 zhuk Exp $
+.\" $OpenBSD: doas.conf.5,v 1.9 2015/07/22 06:30:12 jmc Exp $
.\"
.\"Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
.\"
.\"WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\"ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\"OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-.Dd $Mdocdate: July 21 2015 $
+.Dd $Mdocdate: July 22 2015 $
.Dt DOAS.CONF 5
.Os
.Sh NAME
.Pp
The last matching rule determines the action taken.
.Pp
-The current line can be extended over multiple lines using a backslash
-.Pq Sq \e .
Comments can be put anywhere in the file using a hash mark
.Pq Sq # ,
and extend to the end of the current line.
+.Pp
+The following quoting rules apply:
+.Bl -dash
+.It
+The text between a pair of double quotes
+.Pq Sq \&"
+is taken as is.
+.It
+The backslash
+.Pq Sq \e
+escapes next character, including new line character, outside comment;
+as a result, comments may not be extended over multiple lines.
+.It
+If quotes or backslash are used in the word, this word won't be
+considered a keyword.
+.El
.Sh EXAMPLES
The following example permits users in group wsrc to build ports,
wheel to execute commands as root while keeping the environment
-/* $OpenBSD: parse.y,v 1.7 2015/07/21 11:04:06 zhuk Exp $ */
+/* $OpenBSD: parse.y,v 1.8 2015/07/21 16:12:04 tedu Exp $ */
/*
* Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
*
int
yylex(void)
{
+ static int colno = 1, lineno = 1;
+
char buf[1024], *ebuf, *p, *str;
- int i, c, next;
+ int i, c, quotes = 0, escape = 0, qpos = 0, nonkw = 0;
p = buf;
ebuf = buf + sizeof(buf);
+
repeat:
- c = getc(yyfp);
+ /* skip whitespace first */
+ for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
+ colno++;
+
+ /* check for special one-character constructions */
switch (c) {
- case ' ':
- case '\t':
- goto repeat; /* skip spaces */
- case '\\':
- next = getc(yyfp);
- if (next == '\n')
- goto repeat;
- else
- c = next;
- case '\n':
- case '{':
- case '}':
- return c;
- case '#':
- while ((c = getc(yyfp)) != '\n' && c != EOF)
- ; /* skip comments */
- if (c == EOF)
+ case '\n':
+ colno = 1;
+ lineno++;
+ /* FALLTHROUGH */
+ case '{':
+ case '}':
+ return c;
+ case '#':
+ /* skip comments; NUL is allowed; no continuation */
+ while ((c = getc(yyfp)) != '\n')
+ if (c == EOF)
+ return 0;
+ colno = 1;
+ lineno++;
+ return c;
+ case EOF:
return 0;
- return c;
- case EOF:
- return 0;
}
- while (1) {
+
+ /* parsing next word */
+ for (;; c = getc(yyfp), colno++) {
switch (c) {
+ case '\0':
+ yyerror("unallowed character NUL at "
+ "line %d, column %d", lineno, colno);
+ escape = 0;
+ continue;
+ case '\\':
+ escape = !escape;
+ if (escape)
+ continue;
+ break;
case '\n':
+ if (quotes)
+ yyerror("unterminated quotes at line %d, column %d",
+ lineno, qpos);
+ if (escape) {
+ nonkw = 1;
+ escape = 0;
+ continue;
+ }
+ goto eow;
+ case EOF:
+ if (escape)
+ yyerror("unterminated escape at line %d, column %d",
+ lineno, colno - 1);
+ if (quotes)
+ yyerror("unterminated quotes at line %d, column %d",
+ lineno, qpos);
+ /* FALLTHROUGH */
case '{':
case '}':
case '#':
case ' ':
case '\t':
- case EOF:
- goto eow;
+ if (!escape && !quotes)
+ goto eow;
+ break;
+ case '"':
+ if (!escape) {
+ quotes = !quotes;
+ if (quotes) {
+ nonkw = 1;
+ qpos = colno;
+ }
+ continue;
+ }
}
*p++ = c;
if (p == ebuf)
- yyerror("too much stuff");
- c = getc(yyfp);
+ yyerror("too long line %d", lineno);
+ escape = 0;
}
+
eow:
*p = 0;
if (c != EOF)
ungetc(c, yyfp);
- for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
- if (strcmp(buf, keywords[i].word) == 0)
- return keywords[i].token;
+ if (p == buf) {
+ /*
+ * There could be a number of reasons for empty buffer, and we handle
+ * all of them here, to avoid cluttering the main loop.
+ */
+ if (c == EOF)
+ return 0;
+ else if (!qpos) /* 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) == 0)
+ return keywords[i].token;
+ }
}
if ((str = strdup(buf)) == NULL)
err(1, "strdup");