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