xref: /openbsd/usr.bin/rdist/gram.y (revision e06ba677)
1 %{
2 /*	$OpenBSD: gram.y,v 1.12 2015/01/20 09:00:16 guenther Exp $	*/
3 
4 /*
5  * Copyright (c) 1993 Michael A. Cooper
6  * Copyright (c) 1993 Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include "client.h"
35 
36 static struct namelist *addnl(struct namelist *, struct namelist *);
37 static struct namelist *subnl(struct namelist *, struct namelist *);
38 static struct namelist *andnl(struct namelist *, struct namelist *);
39 static int innl(struct namelist *nl, char *p);
40 
41 struct	cmd *cmds = NULL;
42 struct	cmd *last_cmd;
43 struct	namelist *last_n;
44 struct	subcmd *last_sc;
45 int	parendepth = 0;
46 
47 %}
48 
49 %term ARROW		1
50 %term COLON		2
51 %term DCOLON		3
52 %term NAME		4
53 %term STRING		5
54 %term INSTALL		6
55 %term NOTIFY		7
56 %term EXCEPT		8
57 %term PATTERN		9
58 %term SPECIAL		10
59 %term CMDSPECIAL	11
60 %term OPTION		12
61 
62 %union {
63 	opt_t 			optval;
64 	char 		       *string;
65 	struct subcmd 	       *subcmd;
66 	struct namelist        *namel;
67 }
68 
69 %type <optval> OPTION, options
70 %type <string> NAME, STRING
71 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, CMDSPECIAL, cmdlist, cmd
72 %type <namel> namelist, names, opt_namelist nlist
73 
74 %%
75 
76 file:		  /* VOID */
77 		| file command
78 		;
79 
80 command:	  NAME '=' namelist = {
81 			(void) lookup($1, INSERT, $3);
82 		}
83 		| namelist ARROW namelist cmdlist = {
84 			insert((char *)NULL, $1, $3, $4);
85 		}
86 		| NAME COLON namelist ARROW namelist cmdlist = {
87 			insert($1, $3, $5, $6);
88 		}
89 		| namelist DCOLON NAME cmdlist = {
90 			append((char *)NULL, $1, $3, $4);
91 		}
92 		| NAME COLON namelist DCOLON NAME cmdlist = {
93 			append($1, $3, $5, $6);
94 		}
95 		| error
96 		;
97 
98 namelist: 	nlist {
99 			$$ = $1;
100 		}
101 		| nlist '-' nlist {
102 			$$ = subnl($1, $3);
103 		}
104 		| nlist '+' nlist {
105 			$$ = addnl($1, $3);
106 		}
107 		| nlist '&' nlist {
108 			$$ = andnl($1, $3);
109 		}
110 		;
111 
112 nlist:	  NAME = {
113 			$$ = makenl($1);
114 		}
115 		| '(' names ')' = {
116 			$$ = $2;
117 		}
118 		;
119 
120 names:		  /* VOID */ {
121 			$$ = last_n = NULL;
122 		}
123 		| names NAME = {
124 			if (last_n == NULL)
125 				$$ = last_n = makenl($2);
126 			else {
127 				last_n->n_next = makenl($2);
128 				last_n = last_n->n_next;
129 				$$ = $1;
130 			}
131 		}
132 		;
133 
134 cmdlist:	  /* VOID */ {
135 			$$ = last_sc = NULL;
136 		}
137 		| cmdlist cmd = {
138 			if (last_sc == NULL)
139 				$$ = last_sc = $2;
140 			else {
141 				last_sc->sc_next = $2;
142 				last_sc = $2;
143 				$$ = $1;
144 			}
145 		}
146 		;
147 
148 cmd:		  INSTALL options opt_namelist ';' = {
149 			struct namelist *nl;
150 
151 			$1->sc_options = $2 | options;
152 			if ($3 != NULL) {
153 				nl = expand($3, E_VARS);
154 				if (nl) {
155 					if (nl->n_next != NULL)
156 					    yyerror("only one name allowed\n");
157 					$1->sc_name = nl->n_name;
158 					free(nl);
159 				} else
160 					$1->sc_name = NULL;
161 			}
162 			$$ = $1;
163 		}
164 		| NOTIFY namelist ';' = {
165 			if ($2 != NULL)
166 				$1->sc_args = expand($2, E_VARS);
167 			$$ = $1;
168 		}
169 		| EXCEPT namelist ';' = {
170 			if ($2 != NULL)
171 				$1->sc_args = expand($2, E_ALL);
172 			$$ = $1;
173 		}
174 		| PATTERN namelist ';' = {
175 			struct namelist *nl;
176 			char ebuf[BUFSIZ];
177 			regex_t reg;
178 			int ecode;
179 
180 			for (nl = $2; nl != NULL; nl = nl->n_next) {
181 				/* check for a valid regex */
182 				ecode = regcomp(&reg, nl->n_name, REG_NOSUB);
183 				if (ecode) {
184 					regerror(ecode, &reg, ebuf,
185 					    sizeof(ebuf));
186 					yyerror(ebuf);
187 				}
188 				regfree(&reg);
189 			}
190 			$1->sc_args = expand($2, E_VARS);
191 			$$ = $1;
192 		}
193 		| SPECIAL opt_namelist STRING ';' = {
194 			if ($2 != NULL)
195 				$1->sc_args = expand($2, E_ALL);
196 			$1->sc_name = $3;
197 			$$ = $1;
198 		}
199 		| CMDSPECIAL opt_namelist STRING ';' = {
200 			if ($2 != NULL)
201 				$1->sc_args = expand($2, E_ALL);
202 			$1->sc_name = $3;
203 			$$ = $1;
204 		}
205 		;
206 
207 options:	  /* VOID */ = {
208 			$$ = 0;
209 		}
210 		| options OPTION = {
211 			$$ |= $2;
212 		}
213 		;
214 
215 opt_namelist:	  /* VOID */ = {
216 			$$ = NULL;
217 		}
218 		| namelist = {
219 			$$ = $1;
220 		}
221 		;
222 
223 %%
224 
225 int	yylineno = 1;
226 extern	FILE *fin;
227 
228 int
yylex(void)229 yylex(void)
230 {
231 	static char yytext[INMAX];
232 	int c;
233 	char *cp1, *cp2;
234 	static char quotechars[] = "[]{}*?$";
235 
236 again:
237 	switch (c = getc(fin)) {
238 	case EOF:  /* end of file */
239 		return(0);
240 
241 	case '#':  /* start of comment */
242 		while ((c = getc(fin)) != EOF && c != '\n')
243 			;
244 		if (c == EOF)
245 			return(0);
246 	case '\n':
247 		yylineno++;
248 	case ' ':
249 	case '\t':  /* skip blanks */
250 		goto again;
251 
252 	case '=':  /* EQUAL */
253 	case ';':  /* SM */
254 	case '+':
255 	case '&':
256 		return(c);
257 
258 	case '(':  /* LP */
259 		++parendepth;
260 		return(c);
261 
262 	case ')':  /* RP */
263 		--parendepth;
264 		return(c);
265 
266 	case '-':  /* -> */
267 		if ((c = getc(fin)) == '>')
268 			return(ARROW);
269 		(void) ungetc(c, fin);
270 		c = '-';
271 		break;
272 
273 	case '"':  /* STRING */
274 		cp1 = yytext;
275 		cp2 = &yytext[INMAX - 1];
276 		for (;;) {
277 			if (cp1 >= cp2) {
278 				yyerror("command string too long\n");
279 				break;
280 			}
281 			c = getc(fin);
282 			if (c == EOF || c == '"')
283 				break;
284 			if (c == '\\') {
285 				if ((c = getc(fin)) == EOF) {
286 					*cp1++ = '\\';
287 					break;
288 				}
289 			}
290 			if (c == '\n') {
291 				yylineno++;
292 				c = ' '; /* can't send '\n' */
293 			}
294 			*cp1++ = c;
295 		}
296 		if (c != '"')
297 			yyerror("missing closing '\"'\n");
298 		*cp1 = '\0';
299 		yylval.string = xstrdup(yytext);
300 		return(STRING);
301 
302 	case ':':  /* : or :: */
303 		if ((c = getc(fin)) == ':')
304 			return(DCOLON);
305 		(void) ungetc(c, fin);
306 		return(COLON);
307 	}
308 	cp1 = yytext;
309 	cp2 = &yytext[INMAX - 1];
310 	for (;;) {
311 		if (cp1 >= cp2) {
312 			yyerror("input line too long\n");
313 			break;
314 		}
315 		if (c == '\\') {
316 			if ((c = getc(fin)) != EOF) {
317 				if (any(c, quotechars))
318 					*cp1++ = QUOTECHAR;
319 			} else {
320 				*cp1++ = '\\';
321 				break;
322 			}
323 		}
324 		*cp1++ = c;
325 		c = getc(fin);
326 		if (c == EOF || any(c, " \"'\t()=;:\n")) {
327 			(void) ungetc(c, fin);
328 			break;
329 		}
330 	}
331 	*cp1 = '\0';
332 	if (yytext[0] == '-' && yytext[1] == CNULL)
333 		return '-';
334 	if (yytext[0] == '-' && parendepth <= 0) {
335 		opt_t opt = 0;
336 		static char ebuf[BUFSIZ];
337 
338 		switch (yytext[1]) {
339 		case 'o':
340 			if (parsedistopts(&yytext[2], &opt, TRUE)) {
341 				(void) snprintf(ebuf, sizeof(ebuf),
342 					        "Bad distfile options \"%s\".",
343 					        &yytext[2]);
344 				yyerror(ebuf);
345 			}
346 			break;
347 
348 			/*
349 			 * These options are obsoleted by -o.
350 			 */
351 		case 'b':	opt = DO_COMPARE;		break;
352 		case 'R':	opt = DO_REMOVE;		break;
353 		case 'v':	opt = DO_VERIFY;		break;
354 		case 'w':	opt = DO_WHOLE;			break;
355 		case 'y':	opt = DO_YOUNGER;		break;
356 		case 'h':	opt = DO_FOLLOW;		break;
357 		case 'i':	opt = DO_IGNLNKS;		break;
358 		case 'q':	opt = DO_QUIET;			break;
359 		case 'x':	opt = DO_NOEXEC;		break;
360 		case 'N':	opt = DO_CHKNFS;		break;
361 		case 'O':	opt = DO_CHKREADONLY;		break;
362 		case 's':	opt = DO_SAVETARGETS;		break;
363 		case 'r':	opt = DO_NODESCEND;		break;
364 
365 		default:
366 			(void) snprintf(ebuf, sizeof(ebuf),
367 					"Unknown option \"%s\".", yytext);
368 			yyerror(ebuf);
369 		}
370 
371 		yylval.optval = opt;
372 		return(OPTION);
373 	}
374 	if (!strcmp(yytext, "install"))
375 		c = INSTALL;
376 	else if (!strcmp(yytext, "notify"))
377 		c = NOTIFY;
378 	else if (!strcmp(yytext, "except"))
379 		c = EXCEPT;
380 	else if (!strcmp(yytext, "except_pat"))
381 		c = PATTERN;
382 	else if (!strcmp(yytext, "special"))
383 		c = SPECIAL;
384 	else if (!strcmp(yytext, "cmdspecial"))
385 		c = CMDSPECIAL;
386 	else {
387 		yylval.string = xstrdup(yytext);
388 		return(NAME);
389 	}
390 	yylval.subcmd = makesubcmd(c);
391 	return(c);
392 }
393 
394 /*
395  * XXX We should use strchr(), but most versions can't handle
396  * some of the characters we use.
397  */
any(int c,char * str)398 int any(int c, char *str)
399 {
400 	while (*str)
401 		if (c == *str++)
402 			return(1);
403 	return(0);
404 }
405 
406 /*
407  * Insert or append ARROW command to list of hosts to be updated.
408  */
409 void
insert(char * label,struct namelist * files,struct namelist * hosts,struct subcmd * scmds)410 insert(char *label, struct namelist *files, struct namelist *hosts,
411     struct subcmd *scmds)
412 {
413 	struct cmd *c, *prev, *nc;
414 	struct namelist *h, *lasth;
415 
416 	debugmsg(DM_CALL, "insert(%s, %p, %p, %p) start, files = %s",
417 		 label == NULL ? "(null)" : label,
418 		 files, hosts, scmds, getnlstr(files));
419 
420 	files = expand(files, E_VARS|E_SHELL);
421 	hosts = expand(hosts, E_ALL);
422 	for (h = hosts; h != NULL; lasth = h, h = h->n_next,
423 	     free((char *)lasth)) {
424 		/*
425 		 * Search command list for an update to the same host.
426 		 */
427 		for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
428 			if (strcmp(c->c_name, h->n_name) == 0) {
429 				do {
430 					prev = c;
431 					c = c->c_next;
432 				} while (c != NULL &&
433 					strcmp(c->c_name, h->n_name) == 0);
434 				break;
435 			}
436 		}
437 		/*
438 		 * Insert new command to update host.
439 		 */
440 		nc = ALLOC(cmd);
441 		nc->c_type = ARROW;
442 		nc->c_name = h->n_name;
443 		nc->c_label = label;
444 		nc->c_files = files;
445 		nc->c_cmds = scmds;
446 		nc->c_flags = 0;
447 		nc->c_next = c;
448 		if (prev == NULL)
449 			cmds = nc;
450 		else
451 			prev->c_next = nc;
452 		/* update last_cmd if appending nc to cmds */
453 		if (c == NULL)
454 			last_cmd = nc;
455 	}
456 }
457 
458 /*
459  * Append DCOLON command to the end of the command list since these are always
460  * executed in the order they appear in the distfile.
461  */
462 void
append(char * label,struct namelist * files,char * stamp,struct subcmd * scmds)463 append(char *label, struct namelist *files, char *stamp, struct subcmd *scmds)
464 {
465 	struct cmd *c;
466 
467 	c = ALLOC(cmd);
468 	c->c_type = DCOLON;
469 	c->c_name = stamp;
470 	c->c_label = label;
471 	c->c_files = expand(files, E_ALL);
472 	c->c_cmds = scmds;
473 	c->c_next = NULL;
474 	if (cmds == NULL)
475 		cmds = last_cmd = c;
476 	else {
477 		last_cmd->c_next = c;
478 		last_cmd = c;
479 	}
480 }
481 
482 /*
483  * Error printing routine in parser.
484  */
485 void
yyerror(char * s)486 yyerror(char *s)
487 {
488 	error("Error in distfile: line %d: %s", yylineno, s);
489 }
490 
491 /*
492  * Allocate a namelist structure.
493  */
494 struct namelist *
makenl(char * name)495 makenl(char *name)
496 {
497 	struct namelist *nl;
498 
499 	debugmsg(DM_CALL, "makenl(%s)", name == NULL ? "null" : name);
500 
501 	nl = ALLOC(namelist);
502 	nl->n_name = name;
503 	nl->n_regex = NULL;
504 	nl->n_next = NULL;
505 
506 	return(nl);
507 }
508 
509 
510 /*
511  * Is the name p in the namelist nl?
512  */
513 static int
innl(struct namelist * nl,char * p)514 innl(struct namelist *nl, char *p)
515 {
516 	for ( ; nl; nl = nl->n_next)
517 		if (!strcmp(p, nl->n_name))
518 			return(1);
519 	return(0);
520 }
521 
522 /*
523  * Join two namelists.
524  */
525 static struct namelist *
addnl(struct namelist * n1,struct namelist * n2)526 addnl(struct namelist *n1, struct namelist *n2)
527 {
528 	struct namelist *nl, *prev;
529 
530 	n1 = expand(n1, E_VARS);
531 	n2 = expand(n2, E_VARS);
532 	for (prev = NULL, nl = NULL; n1; n1 = n1->n_next, prev = nl) {
533 		nl = makenl(n1->n_name);
534 		nl->n_next = prev;
535 	}
536 	for (; n2; n2 = n2->n_next)
537 		if (!innl(nl, n2->n_name)) {
538 			nl = makenl(n2->n_name);
539 			nl->n_next = prev;
540 			prev = nl;
541 		}
542 	return(prev);
543 }
544 
545 /*
546  * Copy n1 except for elements that are in n2.
547  */
548 static struct namelist *
subnl(struct namelist * n1,struct namelist * n2)549 subnl(struct namelist *n1, struct namelist *n2)
550 {
551 	struct namelist *nl, *prev;
552 
553 	n1 = expand(n1, E_VARS);
554 	n2 = expand(n2, E_VARS);
555 	for (prev = NULL; n1; n1 = n1->n_next)
556 		if (!innl(n2, n1->n_name)) {
557 			nl = makenl(n1->n_name);
558 			nl->n_next = prev;
559 			prev = nl;
560 		}
561 	return(prev);
562 }
563 
564 /*
565  * Copy all items of n1 that are also in n2.
566  */
567 static struct namelist *
andnl(struct namelist * n1,struct namelist * n2)568 andnl(struct namelist *n1, struct namelist *n2)
569 {
570 	struct namelist *nl, *prev;
571 
572 	n1 = expand(n1, E_VARS);
573 	n2 = expand(n2, E_VARS);
574 	for (prev = NULL; n1; n1 = n1->n_next)
575 		if (innl(n2, n1->n_name)) {
576 			nl = makenl(n1->n_name);
577 			nl->n_next = prev;
578 			prev = nl;
579 		}
580 	return(prev);
581 }
582 
583 /*
584  * Make a sub command for lists of variables, commands, etc.
585  */
586 struct subcmd *
makesubcmd(int type)587 makesubcmd(int type)
588 {
589 	struct subcmd *sc;
590 
591 	sc = ALLOC(subcmd);
592 	sc->sc_type = type;
593 	sc->sc_args = NULL;
594 	sc->sc_next = NULL;
595 	sc->sc_name = NULL;
596 
597 	return(sc);
598 }
599