]> git.armaanb.net Git - opendoas.git/blob - parse.y
wrap some exceedingly long lines
[opendoas.git] / parse.y
1 /* $OpenBSD: parse.y,v 1.10 2015/07/24 06:36:42 zhuk Exp $ */
2 /*
3  * Copyright (c) 2015 Ted Unangst <tedu@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 %{
19 #include <sys/types.h>
20 #include <ctype.h>
21 #include <unistd.h>
22 #include <stdint.h>
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <err.h>
27
28 #include "doas.h"
29
30 typedef struct {
31         union {
32                 struct {
33                         int action;
34                         int options;
35                         const char *cmd;
36                         const char **cmdargs;
37                         const char **envlist;
38                 };
39                 const char *str;
40         };
41         int lineno;
42         int colno;
43 } yystype;
44 #define YYSTYPE yystype
45
46 FILE *yyfp;
47
48 struct rule **rules;
49 int nrules, maxrules;
50 int parse_errors = 0;
51
52 void yyerror(const char *, ...);
53 int yylex(void);
54 int yyparse(void);
55
56 %}
57
58 %token TPERMIT TDENY TAS TCMD TARGS
59 %token TNOPASS TKEEPENV
60 %token TSTRING
61
62 %%
63
64 grammar:        /* empty */
65                 | grammar '\n'
66                 | grammar rule '\n'
67                 | error '\n'
68                 ;
69
70 rule:           action ident target cmd {
71                         struct rule *r;
72                         r = calloc(1, sizeof(*r));
73                         if (!r)
74                                 errx(1, "can't allocate rule");
75                         r->action = $1.action;
76                         r->options = $1.options;
77                         r->envlist = $1.envlist;
78                         r->ident = $2.str;
79                         r->target = $3.str;
80                         r->cmd = $4.cmd;
81                         r->cmdargs = $4.cmdargs;
82                         if (nrules == maxrules) {
83                                 if (maxrules == 0)
84                                         maxrules = 63;
85                                 else
86                                         maxrules *= 2;
87                                 if (!(rules = reallocarray(rules, maxrules,
88                                     sizeof(*rules))))
89                                         errx(1, "can't allocate rules");
90                         }
91                         rules[nrules++] = r;
92                 } ;
93
94 action:         TPERMIT options {
95                         $$.action = PERMIT;
96                         $$.options = $2.options;
97                         $$.envlist = $2.envlist;
98                 } | TDENY {
99                         $$.action = DENY;
100                 } ;
101
102 options:        /* none */
103                 | options option {
104                         $$.options = $1.options | $2.options;
105                         $$.envlist = $1.envlist;
106                         if ($2.envlist) {
107                                 if ($$.envlist) {
108                                         yyerror("can't have two keepenv sections");
109                                         YYERROR;
110                                 } else
111                                         $$.envlist = $2.envlist;
112                         }
113                 } ;
114 option:         TNOPASS {
115                         $$.options = NOPASS;
116                 } | TKEEPENV {
117                         $$.options = KEEPENV;
118                 } | TKEEPENV '{' envlist '}' {
119                         $$.options = KEEPENV;
120                         $$.envlist = $3.envlist;
121                 } ;
122
123 envlist:        /* empty */ {
124                         if (!($$.envlist = calloc(1, sizeof(char *))))
125                                 errx(1, "can't allocate envlist");
126                 } | envlist TSTRING {
127                         int nenv = arraylen($1.envlist);
128                         if (!($$.envlist = reallocarray($1.envlist, nenv + 2,
129                             sizeof(char *))))
130                                 errx(1, "can't allocate envlist");
131                         $$.envlist[nenv] = $2.str;
132                         $$.envlist[nenv + 1] = NULL;
133                 }
134
135
136 ident:          TSTRING {
137                         $$.str = $1.str;
138                 } ;
139
140 target:         /* optional */ {
141                         $$.str = NULL;
142                 } | TAS TSTRING {
143                         $$.str = $2.str;
144                 } ;
145
146 cmd:            /* optional */ {
147                         $$.cmd = NULL;
148                         $$.cmdargs = NULL;
149                 } | TCMD TSTRING args {
150                         $$.cmd = $2.str;
151                         $$.cmdargs = $3.cmdargs;
152                 } ;
153
154 args:           /* empty */ {
155                         $$.cmdargs = NULL;
156                 } | TARGS argslist {
157                         $$.cmdargs = $2.cmdargs;
158                 } ;
159
160 argslist:       /* empty */ {
161                         if (!($$.cmdargs = calloc(1, sizeof(char *))))
162                                 errx(1, "can't allocate args");
163                 } | argslist TSTRING {
164                         int nargs = arraylen($1.cmdargs);
165                         if (!($$.cmdargs = reallocarray($1.cmdargs, nargs + 2,
166                             sizeof(char *))))
167                                 errx(1, "can't allocate args");
168                         $$.cmdargs[nargs] = $2.str;
169                         $$.cmdargs[nargs + 1] = NULL;
170                 } ;
171
172 %%
173
174 void
175 yyerror(const char *fmt, ...)
176 {
177         va_list va;
178
179         va_start(va, fmt);
180         vfprintf(stderr, fmt, va);
181         va_end(va);
182         fprintf(stderr, " at line %d\n", yylval.lineno + 1);
183         parse_errors++;
184 }
185
186 struct keyword {
187         const char *word;
188         int token;
189 } keywords[] = {
190         { "deny", TDENY },
191         { "permit", TPERMIT },
192         { "as", TAS },
193         { "cmd", TCMD },
194         { "args", TARGS },
195         { "nopass", TNOPASS },
196         { "keepenv", TKEEPENV },
197 };
198
199 int
200 yylex(void)
201 {
202         char buf[1024], *ebuf, *p, *str;
203         int i, c, quotes = 0, escape = 0, qpos = -1, nonkw = 0;
204
205         p = buf;
206         ebuf = buf + sizeof(buf);
207
208 repeat:
209         /* skip whitespace first */
210         for (c = getc(yyfp); c == ' ' || c == '\t'; c = getc(yyfp))
211                 yylval.colno++;
212
213         /* check for special one-character constructions */
214         switch (c) {
215                 case '\n':
216                         yylval.colno = 0;
217                         yylval.lineno++;
218                         /* FALLTHROUGH */
219                 case '{':
220                 case '}':
221                         return c;
222                 case '#':
223                         /* skip comments; NUL is allowed; no continuation */
224                         while ((c = getc(yyfp)) != '\n')
225                                 if (c == EOF)
226                                         return 0;
227                         yylval.colno = 0;
228                         yylval.lineno++;
229                         return c;
230                 case EOF:
231                         return 0;
232         }
233
234         /* parsing next word */
235         for (;; c = getc(yyfp), yylval.colno++) {
236                 switch (c) {
237                 case '\0':
238                         yyerror("unallowed character NUL in column %d",
239                             yylval.colno + 1);
240                         escape = 0;
241                         continue;
242                 case '\\':
243                         escape = !escape;
244                         if (escape)
245                                 continue;
246                         break;
247                 case '\n':
248                         if (quotes)
249                                 yyerror("unterminated quotes in column %d",
250                                     qpos + 1);
251                         if (escape) {
252                                 nonkw = 1;
253                                 escape = 0;
254                                 continue;
255                         }
256                         goto eow;
257                 case EOF:
258                         if (escape)
259                                 yyerror("unterminated escape in column %d",
260                                     yylval.colno);
261                         if (quotes)
262                                 yyerror("unterminated quotes in column %d",
263                                     qpos + 1);
264                         goto eow;
265                         /* FALLTHROUGH */
266                 case '{':
267                 case '}':
268                 case '#':
269                 case ' ':
270                 case '\t':
271                         if (!escape && !quotes)
272                                 goto eow;
273                         break;
274                 case '"':
275                         if (!escape) {
276                                 quotes = !quotes;
277                                 if (quotes) {
278                                         nonkw = 1;
279                                         qpos = yylval.colno;
280                                 }
281                                 continue;
282                         }
283                 }
284                 *p++ = c;
285                 if (p == ebuf)
286                         yyerror("too long line");
287                 escape = 0;
288         }
289
290 eow:
291         *p = 0;
292         if (c != EOF)
293                 ungetc(c, yyfp);
294         if (p == buf) {
295                 /*
296                  * There could be a number of reasons for empty buffer,
297                  * and we handle all of them here, to avoid cluttering
298                  * the main loop.
299                  */
300                 if (c == EOF)
301                         return 0;
302                 else if (qpos == -1)    /* accept, e.g., empty args: cmd foo args "" */
303                         goto repeat;
304         }
305         if (!nonkw) {
306                 for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
307                         if (strcmp(buf, keywords[i].word) == 0)
308                                 return keywords[i].token;
309                 }
310         }
311         if ((str = strdup(buf)) == NULL)
312                 err(1, "strdup");
313         yylval.str = str;
314         return TSTRING;
315 }