xref: /netbsd-src/usr.bin/rdist/gram.y (revision fbffadb9f864c0324fb295860ab0faeb187269cc)
1 %{
2 /*	$NetBSD: gram.y,v 1.14 2019/02/03 03:19:29 mrg Exp $	*/
3 
4 /*
5  * Copyright (c) 1983, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/cdefs.h>
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)gram.y	8.1 (Berkeley) 6/9/93";
37 #else
38 __RCSID("$NetBSD: gram.y,v 1.14 2019/02/03 03:19:29 mrg Exp $");
39 #endif
40 #endif /* not lint */
41 
42 #include "defs.h"
43 
44 struct	cmd *cmds = NULL;
45 struct	cmd *last_cmd;
46 struct	namelist *last_n;
47 struct	subcmd *last_sc;
48 
49 static char   *makestr(char *);
50 void	append(char *, struct namelist *, char *, struct subcmd *);
51 
52 %}
53 
54 %term EQUAL	1
55 %term LP	2
56 %term RP	3
57 %term SM	4
58 %term ARROW	5
59 %term COLON	6
60 %term DCOLON	7
61 %term NAME	8
62 %term STRING	9
63 %term INSTALL	10
64 %term NOTIFY	11
65 %term EXCEPT	12
66 %term PATTERN	13
67 %term SPECIAL	14
68 %term OPTION	15
69 
70 %union {
71 	int intval;
72 	char *string;
73 	struct subcmd *subcmd;
74 	struct namelist *namel;
75 }
76 
77 %type <intval> OPTION, options
78 %type <string> NAME, STRING
79 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
80 %type <namel> namelist, names, opt_namelist
81 
82 %%
83 
84 file:		  /* VOID */
85 		| file command
86 		;
87 
88 command:	  NAME EQUAL namelist = {
89 			(void) lookup($1, INSERT, $3);
90 		}
91 		| namelist ARROW namelist cmdlist = {
92 			insert(NULL, $1, $3, $4);
93 		}
94 		| NAME COLON namelist ARROW namelist cmdlist = {
95 			insert($1, $3, $5, $6);
96 		}
97 		| namelist DCOLON NAME cmdlist = {
98 			append(NULL, $1, $3, $4);
99 		}
100 		| NAME COLON namelist DCOLON NAME cmdlist = {
101 			append($1, $3, $5, $6);
102 		}
103 		| error
104 		;
105 
106 namelist:	  NAME = {
107 			$$ = makenl($1);
108 		}
109 		| LP names RP = {
110 			$$ = $2;
111 		}
112 		;
113 
114 names:		  /* VOID */ {
115 			$$ = last_n = NULL;
116 		}
117 		| names NAME = {
118 			if (last_n == NULL)
119 				$$ = last_n = makenl($2);
120 			else {
121 				last_n->n_next = makenl($2);
122 				last_n = last_n->n_next;
123 				$$ = $1;
124 			}
125 		}
126 		;
127 
128 cmdlist:	  /* VOID */ {
129 			$$ = last_sc = NULL;
130 		}
131 		| cmdlist cmd = {
132 			if (last_sc == NULL)
133 				$$ = last_sc = $2;
134 			else {
135 				last_sc->sc_next = $2;
136 				last_sc = $2;
137 				$$ = $1;
138 			}
139 		}
140 		;
141 
142 cmd:		  INSTALL options opt_namelist SM = {
143 			struct namelist *nl;
144 
145 			$1->sc_options = $2 | options;
146 			if ($3 != NULL) {
147 				nl = expand($3, E_VARS);
148 				if (nl) {
149 					if (nl->n_next != NULL)
150 					    yyerror("only one name allowed\n");
151 					$1->sc_name = nl->n_name;
152 					free(nl);
153 				} else
154 					$1->sc_name = NULL;
155 			}
156 			$$ = $1;
157 		}
158 		| NOTIFY namelist SM = {
159 			if ($2 != NULL)
160 				$1->sc_args = expand($2, E_VARS);
161 			$$ = $1;
162 		}
163 		| EXCEPT namelist SM = {
164 			if ($2 != NULL)
165 				$1->sc_args = expand($2, E_ALL);
166 			$$ = $1;
167 		}
168 		| PATTERN namelist SM = {
169 			if ($2 != NULL)
170 				$1->sc_args = expand($2, E_VARS);
171 			$$ = $1;
172 		}
173 		| SPECIAL opt_namelist STRING SM = {
174 			if ($2 != NULL)
175 				$1->sc_args = expand($2, E_ALL);
176 			$1->sc_name = $3;
177 			$$ = $1;
178 		}
179 		;
180 
181 options:	  /* VOID */ = {
182 			$$ = 0;
183 		}
184 		| options OPTION = {
185 			$$ |= $2;
186 		}
187 		;
188 
189 opt_namelist:	  /* VOID */ = {
190 			$$ = NULL;
191 		}
192 		| namelist = {
193 			$$ = $1;
194 		}
195 		;
196 
197 %%
198 
199 int	yylineno = 1;
200 extern	FILE *fin;
201 
202 int	yylex(void);
203 
204 int
yylex(void)205 yylex(void)
206 {
207 	static char yytext[INMAX];
208 	int c;
209 	char *cp1, *cp2;
210 	static char quotechars[] = "[]{}*?$";
211 
212 again:
213 	switch (c = getc(fin)) {
214 	case EOF:  /* end of file */
215 		return(0);
216 
217 	case '#':  /* start of comment */
218 		while ((c = getc(fin)) != EOF && c != '\n')
219 			;
220 		if (c == EOF)
221 			return(0);
222 		/* FALLTHROUGH */
223 	case '\n':
224 		yylineno++;
225 		/* FALLTHROUGH */
226 	case ' ':
227 	case '\t':  /* skip blanks */
228 		goto again;
229 
230 	case '=':  /* EQUAL */
231 		return(EQUAL);
232 
233 	case '(':  /* LP */
234 		return(LP);
235 
236 	case ')':  /* RP */
237 		return(RP);
238 
239 	case ';':  /* SM */
240 		return(SM);
241 
242 	case '-':  /* -> */
243 		if ((c = getc(fin)) == '>')
244 			return(ARROW);
245 		ungetc(c, fin);
246 		c = '-';
247 		break;
248 
249 	case '"':  /* STRING */
250 		cp1 = yytext;
251 		cp2 = &yytext[INMAX - 1];
252 		for (;;) {
253 			if (cp1 >= cp2) {
254 				yyerror("command string too long\n");
255 				break;
256 			}
257 			c = getc(fin);
258 			if (c == EOF || c == '"')
259 				break;
260 			if (c == '\\') {
261 				if ((c = getc(fin)) == EOF) {
262 					*cp1++ = '\\';
263 					break;
264 				}
265 			}
266 			if (c == '\n') {
267 				yylineno++;
268 				c = ' '; /* can't send '\n' */
269 			}
270 			*cp1++ = c;
271 		}
272 		if (c != '"')
273 			yyerror("missing closing '\"'\n");
274 		*cp1 = '\0';
275 		yylval.string = makestr(yytext);
276 		return(STRING);
277 
278 	case ':':  /* : or :: */
279 		if ((c = getc(fin)) == ':')
280 			return(DCOLON);
281 		ungetc(c, fin);
282 		return(COLON);
283 	}
284 	cp1 = yytext;
285 	cp2 = &yytext[INMAX - 1];
286 	for (;;) {
287 		if (cp1 >= cp2) {
288 			yyerror("input line too long\n");
289 			break;
290 		}
291 		if (c == '\\') {
292 			if ((c = getc(fin)) != EOF) {
293 				if (any(c, quotechars))
294 					c |= QUOTE;
295 			} else {
296 				*cp1++ = '\\';
297 				break;
298 			}
299 		}
300 		*cp1++ = c;
301 		c = getc(fin);
302 		if (c == EOF || any(c, " \"'\t()=;:\n")) {
303 			ungetc(c, fin);
304 			break;
305 		}
306 	}
307 	*cp1 = '\0';
308 	if (yytext[0] == '-' && yytext[2] == '\0') {
309 		switch (yytext[1]) {
310 		case 'b':
311 			yylval.intval = COMPARE;
312 			return(OPTION);
313 
314 		case 'R':
315 			yylval.intval = REMOVE;
316 			return(OPTION);
317 
318 		case 'v':
319 			yylval.intval = VERIFY;
320 			return(OPTION);
321 
322 		case 'w':
323 			yylval.intval = WHOLE;
324 			return(OPTION);
325 
326 		case 'y':
327 			yylval.intval = YOUNGER;
328 			return(OPTION);
329 
330 		case 'h':
331 			yylval.intval = FOLLOW;
332 			return(OPTION);
333 
334 		case 'i':
335 			yylval.intval = IGNLNKS;
336 			return(OPTION);
337 		}
338 	}
339 	if (!strcmp(yytext, "install"))
340 		c = INSTALL;
341 	else if (!strcmp(yytext, "notify"))
342 		c = NOTIFY;
343 	else if (!strcmp(yytext, "except"))
344 		c = EXCEPT;
345 	else if (!strcmp(yytext, "except_pat"))
346 		c = PATTERN;
347 	else if (!strcmp(yytext, "special"))
348 		c = SPECIAL;
349 	else {
350 		yylval.string = makestr(yytext);
351 		return(NAME);
352 	}
353 	yylval.subcmd = makesubcmd(c);
354 	return(c);
355 }
356 
357 int
any(int c,const char * str)358 any(int c, const char *str)
359 {
360 	while (*str)
361 		if (c == *str++)
362 			return(1);
363 	return(0);
364 }
365 
366 /*
367  * Insert or append ARROW command to list of hosts to be updated.
368  */
369 void
insert(char * label,struct namelist * files,struct namelist * hosts,struct subcmd * subcmds)370 insert(char *label, struct namelist *files, struct namelist *hosts,
371        struct subcmd *subcmds)
372 {
373 	struct cmd *c, *prev, *nc;
374 	struct namelist *h, *nexth;
375 
376 	files = expand(files, E_VARS|E_SHELL);
377 	hosts = expand(hosts, E_ALL);
378 	for (h = hosts; h != NULL; nexth = h->n_next, free(h), h = nexth) {
379 		/*
380 		 * Search command list for an update to the same host.
381 		 */
382 		for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
383 			if (strcmp(c->c_name, h->n_name) == 0) {
384 				do {
385 					prev = c;
386 					c = c->c_next;
387 				} while (c != NULL &&
388 					strcmp(c->c_name, h->n_name) == 0);
389 				break;
390 			}
391 		}
392 		/*
393 		 * Insert new command to update host.
394 		 */
395 		nc = ALLOC(cmd);
396 		if (nc == NULL)
397 			fatal("ran out of memory\n");
398 		nc->c_type = ARROW;
399 		nc->c_name = h->n_name;
400 		nc->c_label = label;
401 		nc->c_files = files;
402 		nc->c_cmds = subcmds;
403 		nc->c_next = c;
404 		if (prev == NULL)
405 			cmds = nc;
406 		else
407 			prev->c_next = nc;
408 		/* update last_cmd if appending nc to cmds */
409 		if (c == NULL)
410 			last_cmd = nc;
411 	}
412 }
413 
414 /*
415  * Append DCOLON command to the end of the command list since these are always
416  * executed in the order they appear in the distfile.
417  */
418 void
append(char * label,struct namelist * files,char * stamp,struct subcmd * subcmds)419 append(char *label, struct namelist *files, char *stamp,
420        struct subcmd *subcmds)
421 {
422 	struct cmd *c;
423 
424 	c = ALLOC(cmd);
425 	if (c == NULL)
426 		fatal("ran out of memory\n");
427 	c->c_type = DCOLON;
428 	c->c_name = stamp;
429 	c->c_label = label;
430 	c->c_files = expand(files, E_ALL);
431 	c->c_cmds = subcmds;
432 	c->c_next = NULL;
433 	if (cmds == NULL)
434 		cmds = last_cmd = c;
435 	else {
436 		last_cmd->c_next = c;
437 		last_cmd = c;
438 	}
439 }
440 
441 /*
442  * Error printing routine in parser.
443  */
444 void
yyerror(const char * s)445 yyerror(const char *s)
446 {
447 
448 	++nerrs;
449 	fflush(stdout);
450 	fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
451 }
452 
453 /*
454  * Return a copy of the string.
455  */
456 static char *
makestr(char * str)457 makestr(char *str)
458 {
459 	char *cp, *s;
460 
461 	str = cp = malloc(strlen(s = str) + 1);
462 	if (cp == NULL)
463 		fatal("ran out of memory\n");
464 	while ((*cp++ = *s++) != 0)
465 		;
466 	return(str);
467 }
468 
469 /*
470  * Allocate a namelist structure.
471  */
472 struct namelist *
makenl(char * name)473 makenl(char *name)
474 {
475 	struct namelist *nl;
476 
477 	nl = ALLOC(namelist);
478 	if (nl == NULL)
479 		fatal("ran out of memory\n");
480 	nl->n_name = name;
481 	nl->n_next = NULL;
482 	return(nl);
483 }
484 
485 void
freenl(struct namelist * nl)486 freenl(struct namelist *nl)
487 {
488 	if (nl == NULL)
489 		return;
490 	freenl(nl->n_next);
491 	free(nl);
492 }
493 
494 void
freesubcmd(struct subcmd * cmd)495 freesubcmd(struct subcmd *cmd)
496 {
497 	if (cmd == NULL)
498 		return;
499 	freesubcmd(cmd->sc_next);
500 	free(cmd);
501 }
502 
503 /*
504  * Make a sub command for lists of variables, commands, etc.
505  */
506 struct subcmd *
makesubcmd(int type)507 makesubcmd(int type)
508 {
509 	struct subcmd *sc;
510 
511 	sc = ALLOC(subcmd);
512 	if (sc == NULL)
513 		fatal("ran out of memory\n");
514 	sc->sc_type = type;
515 	sc->sc_args = NULL;
516 	sc->sc_next = NULL;
517 	sc->sc_name = NULL;
518 	return(sc);
519 }
520