xref: /netbsd/usr.bin/rdist/gram.y (revision 6550d01e)
1 %{
2 /*	$NetBSD: gram.y,v 1.13 2009/04/13 04:35:36 lukem 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.13 2009/04/13 04:35:36 lukem 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
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 	case '\n':
223 		yylineno++;
224 	case ' ':
225 	case '\t':  /* skip blanks */
226 		goto again;
227 
228 	case '=':  /* EQUAL */
229 		return(EQUAL);
230 
231 	case '(':  /* LP */
232 		return(LP);
233 
234 	case ')':  /* RP */
235 		return(RP);
236 
237 	case ';':  /* SM */
238 		return(SM);
239 
240 	case '-':  /* -> */
241 		if ((c = getc(fin)) == '>')
242 			return(ARROW);
243 		ungetc(c, fin);
244 		c = '-';
245 		break;
246 
247 	case '"':  /* STRING */
248 		cp1 = yytext;
249 		cp2 = &yytext[INMAX - 1];
250 		for (;;) {
251 			if (cp1 >= cp2) {
252 				yyerror("command string too long\n");
253 				break;
254 			}
255 			c = getc(fin);
256 			if (c == EOF || c == '"')
257 				break;
258 			if (c == '\\') {
259 				if ((c = getc(fin)) == EOF) {
260 					*cp1++ = '\\';
261 					break;
262 				}
263 			}
264 			if (c == '\n') {
265 				yylineno++;
266 				c = ' '; /* can't send '\n' */
267 			}
268 			*cp1++ = c;
269 		}
270 		if (c != '"')
271 			yyerror("missing closing '\"'\n");
272 		*cp1 = '\0';
273 		yylval.string = makestr(yytext);
274 		return(STRING);
275 
276 	case ':':  /* : or :: */
277 		if ((c = getc(fin)) == ':')
278 			return(DCOLON);
279 		ungetc(c, fin);
280 		return(COLON);
281 	}
282 	cp1 = yytext;
283 	cp2 = &yytext[INMAX - 1];
284 	for (;;) {
285 		if (cp1 >= cp2) {
286 			yyerror("input line too long\n");
287 			break;
288 		}
289 		if (c == '\\') {
290 			if ((c = getc(fin)) != EOF) {
291 				if (any(c, quotechars))
292 					c |= QUOTE;
293 			} else {
294 				*cp1++ = '\\';
295 				break;
296 			}
297 		}
298 		*cp1++ = c;
299 		c = getc(fin);
300 		if (c == EOF || any(c, " \"'\t()=;:\n")) {
301 			ungetc(c, fin);
302 			break;
303 		}
304 	}
305 	*cp1 = '\0';
306 	if (yytext[0] == '-' && yytext[2] == '\0') {
307 		switch (yytext[1]) {
308 		case 'b':
309 			yylval.intval = COMPARE;
310 			return(OPTION);
311 
312 		case 'R':
313 			yylval.intval = REMOVE;
314 			return(OPTION);
315 
316 		case 'v':
317 			yylval.intval = VERIFY;
318 			return(OPTION);
319 
320 		case 'w':
321 			yylval.intval = WHOLE;
322 			return(OPTION);
323 
324 		case 'y':
325 			yylval.intval = YOUNGER;
326 			return(OPTION);
327 
328 		case 'h':
329 			yylval.intval = FOLLOW;
330 			return(OPTION);
331 
332 		case 'i':
333 			yylval.intval = IGNLNKS;
334 			return(OPTION);
335 		}
336 	}
337 	if (!strcmp(yytext, "install"))
338 		c = INSTALL;
339 	else if (!strcmp(yytext, "notify"))
340 		c = NOTIFY;
341 	else if (!strcmp(yytext, "except"))
342 		c = EXCEPT;
343 	else if (!strcmp(yytext, "except_pat"))
344 		c = PATTERN;
345 	else if (!strcmp(yytext, "special"))
346 		c = SPECIAL;
347 	else {
348 		yylval.string = makestr(yytext);
349 		return(NAME);
350 	}
351 	yylval.subcmd = makesubcmd(c);
352 	return(c);
353 }
354 
355 int
356 any(int c, const char *str)
357 {
358 	while (*str)
359 		if (c == *str++)
360 			return(1);
361 	return(0);
362 }
363 
364 /*
365  * Insert or append ARROW command to list of hosts to be updated.
366  */
367 void
368 insert(char *label, struct namelist *files, struct namelist *hosts,
369        struct subcmd *subcmds)
370 {
371 	struct cmd *c, *prev, *nc;
372 	struct namelist *h, *nexth;
373 
374 	files = expand(files, E_VARS|E_SHELL);
375 	hosts = expand(hosts, E_ALL);
376 	for (h = hosts; h != NULL; nexth = h->n_next, free(h), h = nexth) {
377 		/*
378 		 * Search command list for an update to the same host.
379 		 */
380 		for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
381 			if (strcmp(c->c_name, h->n_name) == 0) {
382 				do {
383 					prev = c;
384 					c = c->c_next;
385 				} while (c != NULL &&
386 					strcmp(c->c_name, h->n_name) == 0);
387 				break;
388 			}
389 		}
390 		/*
391 		 * Insert new command to update host.
392 		 */
393 		nc = ALLOC(cmd);
394 		if (nc == NULL)
395 			fatal("ran out of memory\n");
396 		nc->c_type = ARROW;
397 		nc->c_name = h->n_name;
398 		nc->c_label = label;
399 		nc->c_files = files;
400 		nc->c_cmds = subcmds;
401 		nc->c_next = c;
402 		if (prev == NULL)
403 			cmds = nc;
404 		else
405 			prev->c_next = nc;
406 		/* update last_cmd if appending nc to cmds */
407 		if (c == NULL)
408 			last_cmd = nc;
409 	}
410 }
411 
412 /*
413  * Append DCOLON command to the end of the command list since these are always
414  * executed in the order they appear in the distfile.
415  */
416 void
417 append(char *label, struct namelist *files, char *stamp,
418        struct subcmd *subcmds)
419 {
420 	struct cmd *c;
421 
422 	c = ALLOC(cmd);
423 	if (c == NULL)
424 		fatal("ran out of memory\n");
425 	c->c_type = DCOLON;
426 	c->c_name = stamp;
427 	c->c_label = label;
428 	c->c_files = expand(files, E_ALL);
429 	c->c_cmds = subcmds;
430 	c->c_next = NULL;
431 	if (cmds == NULL)
432 		cmds = last_cmd = c;
433 	else {
434 		last_cmd->c_next = c;
435 		last_cmd = c;
436 	}
437 }
438 
439 /*
440  * Error printing routine in parser.
441  */
442 void
443 yyerror(const char *s)
444 {
445 
446 	++nerrs;
447 	fflush(stdout);
448 	fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
449 }
450 
451 /*
452  * Return a copy of the string.
453  */
454 static char *
455 makestr(char *str)
456 {
457 	char *cp, *s;
458 
459 	str = cp = malloc(strlen(s = str) + 1);
460 	if (cp == NULL)
461 		fatal("ran out of memory\n");
462 	while ((*cp++ = *s++) != 0)
463 		;
464 	return(str);
465 }
466 
467 /*
468  * Allocate a namelist structure.
469  */
470 struct namelist *
471 makenl(char *name)
472 {
473 	struct namelist *nl;
474 
475 	nl = ALLOC(namelist);
476 	if (nl == NULL)
477 		fatal("ran out of memory\n");
478 	nl->n_name = name;
479 	nl->n_next = NULL;
480 	return(nl);
481 }
482 
483 void
484 freenl(struct namelist *nl)
485 {
486 	if (nl == NULL)
487 		return;
488 	freenl(nl->n_next);
489 	free(nl);
490 }
491 
492 void
493 freesubcmd(struct subcmd *cmd)
494 {
495 	if (cmd == NULL)
496 		return;
497 	freesubcmd(cmd->sc_next);
498 	free(cmd);
499 }
500 
501 /*
502  * Make a sub command for lists of variables, commands, etc.
503  */
504 struct subcmd *
505 makesubcmd(int type)
506 {
507 	struct subcmd *sc;
508 
509 	sc = ALLOC(subcmd);
510 	if (sc == NULL)
511 		fatal("ran out of memory\n");
512 	sc->sc_type = type;
513 	sc->sc_args = NULL;
514 	sc->sc_next = NULL;
515 	sc->sc_name = NULL;
516 	return(sc);
517 }
518