xref: /original-bsd/usr.bin/rdist/gram.y (revision f82e54c4)
1 %{
2 #ifndef lint
3 static	char *sccsid = "@(#)gram.y	4.11 (Berkeley) 84/06/28";
4 #endif
5 
6 #include "defs.h"
7 
8 struct	cmd *cmds = NULL;
9 struct	cmd *last_cmd;
10 struct	namelist *last_n;
11 struct	subcmd *last_sc;
12 
13 %}
14 
15 %term EQUAL	1
16 %term LP	2
17 %term RP	3
18 %term SM	4
19 %term ARROW	5
20 %term COLON	6
21 %term DCOLON	7
22 %term NAME	8
23 %term STRING	9
24 %term INSTALL	10
25 %term NOTIFY	11
26 %term EXCEPT	12
27 %term PATTERN	13
28 %term SPECIAL	14
29 %term OPTION	15
30 
31 %union {
32 	int intval;
33 	char *string;
34 	struct subcmd *subcmd;
35 	struct namelist *namel;
36 }
37 
38 %type <intval> OPTION, options
39 %type <string> NAME, STRING
40 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
41 %type <namel> namelist, names, opt_namelist
42 
43 %%
44 
45 file:		  /* VOID */
46 		| file command
47 		;
48 
49 command:	  NAME EQUAL namelist = {
50 			(void) lookup($1, INSERT, $3);
51 		}
52 		| namelist ARROW namelist cmdlist = {
53 			insert(NULL, $1, $3, $4);
54 		}
55 		| NAME COLON namelist ARROW namelist cmdlist = {
56 			insert($1, $3, $5, $6);
57 		}
58 		| namelist DCOLON NAME cmdlist = {
59 			append(NULL, $1, $3, $4);
60 		}
61 		| NAME COLON namelist DCOLON NAME cmdlist = {
62 			append($1, $3, $5, $6);
63 		}
64 		| error
65 		;
66 
67 namelist:	  NAME = {
68 			$$ = makenl($1);
69 		}
70 		| LP names RP = {
71 			$$ = $2;
72 		}
73 		;
74 
75 names:		  /* VOID */ {
76 			$$ = last_n = NULL;
77 		}
78 		| names NAME = {
79 			if (last_n == NULL)
80 				$$ = last_n = makenl($2);
81 			else {
82 				last_n->n_next = makenl($2);
83 				last_n = last_n->n_next;
84 				$$ = $1;
85 			}
86 		}
87 		;
88 
89 cmdlist:	  /* VOID */ {
90 			$$ = last_sc = NULL;
91 		}
92 		| cmdlist cmd = {
93 			if (last_sc == NULL)
94 				$$ = last_sc = $2;
95 			else {
96 				last_sc->sc_next = $2;
97 				last_sc = $2;
98 				$$ = $1;
99 			}
100 		}
101 		;
102 
103 cmd:		  INSTALL options opt_namelist SM = {
104 			register struct namelist *nl;
105 
106 			$1->sc_options = $2 | options;
107 			if ($3 != NULL) {
108 				nl = expand($3, E_VARS);
109 				if (nl->n_next != NULL)
110 					yyerror("only one name allowed\n");
111 				$1->sc_name = nl->n_name;
112 				free(nl);
113 			}
114 			$$ = $1;
115 		}
116 		| NOTIFY namelist SM = {
117 			if ($2 != NULL)
118 				$1->sc_args = expand($2, E_VARS);
119 			$$ = $1;
120 		}
121 		| EXCEPT namelist SM = {
122 			if ($2 != NULL)
123 				$1->sc_args = expand($2, E_ALL);
124 			$$ = $1;
125 		}
126 		| PATTERN namelist SM = {
127 			struct namelist *nl;
128 			char *cp, *re_comp();
129 
130 			for (nl = $2; nl != NULL; nl = nl->n_next)
131 				if ((cp = re_comp(nl->n_name)) != NULL)
132 					yyerror(cp);
133 			$1->sc_args = $2;
134 			$$ = $1;
135 		}
136 		| SPECIAL opt_namelist STRING SM = {
137 			if ($2 != NULL)
138 				$1->sc_args = expand($2, E_ALL);
139 			$1->sc_name = $3;
140 			$$ = $1;
141 		}
142 		;
143 
144 options:	  /* VOID */ = {
145 			$$ = 0;
146 		}
147 		| options OPTION = {
148 			$$ |= $2;
149 		}
150 		;
151 
152 opt_namelist:	  /* VOID */ = {
153 			$$ = NULL;
154 		}
155 		| namelist = {
156 			$$ = $1;
157 		}
158 		;
159 
160 %%
161 
162 int	yylineno = 1;
163 extern	FILE *fin;
164 
165 yylex()
166 {
167 	static char yytext[INMAX];
168 	register int c;
169 	register char *cp1, *cp2;
170 	static char quotechars[] = "[]{}*?$";
171 
172 again:
173 	switch (c = getc(fin)) {
174 	case EOF:  /* end of file */
175 		return(0);
176 
177 	case '#':  /* start of comment */
178 		while ((c = getc(fin)) != EOF && c != '\n')
179 			;
180 		if (c == EOF)
181 			return(0);
182 	case '\n':
183 		yylineno++;
184 	case ' ':
185 	case '\t':  /* skip blanks */
186 		goto again;
187 
188 	case '=':  /* EQUAL */
189 		return(EQUAL);
190 
191 	case '(':  /* LP */
192 		return(LP);
193 
194 	case ')':  /* RP */
195 		return(RP);
196 
197 	case ';':  /* SM */
198 		return(SM);
199 
200 	case '-':  /* -> */
201 		if ((c = getc(fin)) == '>')
202 			return(ARROW);
203 		ungetc(c, fin);
204 		c = '-';
205 		break;
206 
207 	case '"':  /* STRING */
208 		cp1 = yytext;
209 		cp2 = &yytext[INMAX - 1];
210 		for (;;) {
211 			if (cp1 >= cp2) {
212 				yyerror("command string too long\n");
213 				break;
214 			}
215 			c = getc(fin);
216 			if (c == EOF || c == '"')
217 				break;
218 			if (c == '\\') {
219 				if ((c = getc(fin)) == EOF) {
220 					*cp1++ = '\\';
221 					break;
222 				}
223 			}
224 			if (c == '\n') {
225 				yylineno++;
226 				c = ' '; /* can't send '\n' */
227 			}
228 			*cp1++ = c;
229 		}
230 		if (c != '"')
231 			yyerror("missing closing '\"'\n");
232 		*cp1 = '\0';
233 		yylval.string = makestr(yytext);
234 		return(STRING);
235 
236 	case ':':  /* : or :: */
237 		if ((c = getc(fin)) == ':')
238 			return(DCOLON);
239 		ungetc(c, fin);
240 		return(COLON);
241 	}
242 	cp1 = yytext;
243 	cp2 = &yytext[INMAX - 1];
244 	for (;;) {
245 		if (cp1 >= cp2) {
246 			yyerror("input line too long\n");
247 			break;
248 		}
249 		if (c == '\\') {
250 			if ((c = getc(fin)) != EOF) {
251 				if (any(c, quotechars))
252 					c |= QUOTE;
253 			} else {
254 				*cp1++ = '\\';
255 				break;
256 			}
257 		}
258 		*cp1++ = c;
259 		c = getc(fin);
260 		if (c == EOF || any(c, " \"'\t()=;:\n")) {
261 			ungetc(c, fin);
262 			break;
263 		}
264 	}
265 	*cp1 = '\0';
266 	if (yytext[0] == '-' && yytext[2] == '\0') {
267 		switch (yytext[1]) {
268 		case 'b':
269 			yylval.intval = COMPARE;
270 			return(OPTION);
271 
272 		case 'R':
273 			yylval.intval = REMOVE;
274 			return(OPTION);
275 
276 		case 'v':
277 			yylval.intval = VERIFY;
278 			return(OPTION);
279 
280 		case 'w':
281 			yylval.intval = WHOLE;
282 			return(OPTION);
283 
284 		case 'y':
285 			yylval.intval = YOUNGER;
286 			return(OPTION);
287 
288 		case 'h':
289 			yylval.intval = FOLLOW;
290 			return(OPTION);
291 
292 		case 'i':
293 			yylval.intval = IGNLNKS;
294 			return(OPTION);
295 		}
296 	}
297 	if (!strcmp(yytext, "install"))
298 		c = INSTALL;
299 	else if (!strcmp(yytext, "notify"))
300 		c = NOTIFY;
301 	else if (!strcmp(yytext, "except"))
302 		c = EXCEPT;
303 	else if (!strcmp(yytext, "exp_pat"))
304 		c = PATTERN;
305 	else if (!strcmp(yytext, "special"))
306 		c = SPECIAL;
307 	else {
308 		yylval.string = makestr(yytext);
309 		return(NAME);
310 	}
311 	yylval.subcmd = makesubcmd(c);
312 	return(c);
313 }
314 
315 any(c, str)
316 	register int c;
317 	register char *str;
318 {
319 	while (*str)
320 		if (c == *str++)
321 			return(1);
322 	return(0);
323 }
324 
325 /*
326  * Insert or append ARROW command to list of hosts to be updated.
327  */
328 insert(label, files, hosts, subcmds)
329 	char *label;
330 	struct namelist *files, *hosts;
331 	struct subcmd *subcmds;
332 {
333 	register struct cmd *c, *prev, *nc;
334 	register struct namelist *h;
335 
336 	files = expand(files, E_VARS|E_SHELL);
337 	hosts = expand(hosts, E_ALL);
338 	for (h = hosts; h != NULL; free(h), h = h->n_next) {
339 		/*
340 		 * Search command list for an update to the same host.
341 		 */
342 		for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
343 			if (strcmp(c->c_name, h->n_name) == 0) {
344 				do {
345 					prev = c;
346 					c = c->c_next;
347 				} while (c != NULL &&
348 					strcmp(c->c_name, h->n_name) == 0);
349 				break;
350 			}
351 		}
352 		/*
353 		 * Insert new command to update host.
354 		 */
355 		nc = ALLOC(cmd);
356 		if (nc == NULL)
357 			fatal("ran out of memory\n");
358 		nc->c_type = ARROW;
359 		nc->c_name = h->n_name;
360 		nc->c_label = label;
361 		nc->c_files = files;
362 		nc->c_cmds = subcmds;
363 		nc->c_next = c;
364 		if (prev == NULL)
365 			cmds = nc;
366 		else
367 			prev->c_next = nc;
368 		/* update last_cmd if appending nc to cmds */
369 		if (c == NULL)
370 			last_cmd = nc;
371 	}
372 }
373 
374 /*
375  * Append DCOLON command to the end of the command list since these are always
376  * executed in the order they appear in the distfile.
377  */
378 append(label, files, stamp, subcmds)
379 	char *label;
380 	struct namelist *files;
381 	char *stamp;
382 	struct subcmd *subcmds;
383 {
384 	register struct cmd *c;
385 
386 	c = ALLOC(cmd);
387 	if (c == NULL)
388 		fatal("ran out of memory\n");
389 	c->c_type = DCOLON;
390 	c->c_name = stamp;
391 	c->c_label = label;
392 	c->c_files = expand(files, E_ALL);
393 	c->c_cmds = subcmds;
394 	c->c_next = NULL;
395 	if (cmds == NULL)
396 		cmds = last_cmd = c;
397 	else {
398 		last_cmd->c_next = c;
399 		last_cmd = c;
400 	}
401 }
402 
403 /*
404  * Error printing routine in parser.
405  */
406 yyerror(s)
407 	char *s;
408 {
409 	extern int yychar;
410 
411 	nerrs++;
412 	fflush(stdout);
413 	fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
414 }
415 
416 /*
417  * Return a copy of the string.
418  */
419 char *
420 makestr(str)
421 	char *str;
422 {
423 	register char *cp, *s;
424 
425 	str = cp = malloc(strlen(s = str) + 1);
426 	if (cp == NULL)
427 		fatal("ran out of memory\n");
428 	while (*cp++ = *s++)
429 		;
430 	return(str);
431 }
432 
433 /*
434  * Allocate a namelist structure.
435  */
436 struct namelist *
437 makenl(name)
438 	char *name;
439 {
440 	register struct namelist *nl;
441 
442 	nl = ALLOC(namelist);
443 	if (nl == NULL)
444 		fatal("ran out of memory\n");
445 	nl->n_name = name;
446 	nl->n_next = NULL;
447 	return(nl);
448 }
449 
450 /*
451  * Make a sub command for lists of variables, commands, etc.
452  */
453 struct subcmd *
454 makesubcmd(type, name)
455 	int type;
456 	register char *name;
457 {
458 	register char *cp;
459 	register struct subcmd *sc;
460 
461 	sc = ALLOC(subcmd);
462 	if (sc == NULL)
463 		fatal("ran out of memory\n");
464 	sc->sc_type = type;
465 	sc->sc_args = NULL;
466 	sc->sc_next = NULL;
467 	sc->sc_name = NULL;
468 	return(sc);
469 }
470