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