xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.bin/rdist/gram.y (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 %{
2 /*
3  * Copyright (c) 1983 Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by the University of California, Berkeley.  The name of the
12  * University may not be used to endorse or promote products derived
13  * from this software without specific prior written permission.
14  *
15  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
16  * Use is subject to license terms.
17  */
18 
19 #include "defs.h"
20 
21 struct	cmd *cmds = NULL;
22 struct	cmd *last_cmd;
23 struct	namelist *last_n;
24 struct	subcmd *last_sc;
25 
26 static void append(char *label, struct namelist *files, char *stamp,
27     struct subcmd *subcmds);
28 void yyerror(char *s);
29 
30 %}
31 
32 %term EQUAL	1
33 %term LP	2
34 %term RP	3
35 %term SM	4
36 %term ARROW	5
37 %term COLON	6
38 %term DCOLON	7
39 %term NAME	8
40 %term STRING	9
41 %term INSTALL	10
42 %term NOTIFY	11
43 %term EXCEPT	12
44 %term PATTERN	13
45 %term SPECIAL	14
46 %term OPTION	15
47 
48 %union {
49 	int intval;
50 	char *string;
51 	struct subcmd *subcmd;
52 	struct namelist *namel;
53 }
54 
55 %type <intval> OPTION, options
56 %type <string> NAME, STRING
57 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
58 %type <namel> namelist, names, opt_namelist
59 
60 %%
61 
62 file:		  /* VOID */
63 		| file command
64 		;
65 
66 command:	  NAME EQUAL namelist = {
67 			(void) lookup($1, INSERT, $3);
68 		}
69 		| namelist ARROW namelist cmdlist = {
70 			insert(NULL, $1, $3, $4);
71 		}
72 		| NAME COLON namelist ARROW namelist cmdlist = {
73 			insert($1, $3, $5, $6);
74 		}
75 		| namelist DCOLON NAME cmdlist = {
76 			append(NULL, $1, $3, $4);
77 		}
78 		| NAME COLON namelist DCOLON NAME cmdlist = {
79 			append($1, $3, $5, $6);
80 		}
81 		| error
82 		;
83 
84 namelist:	  NAME = {
85 			$$ = makenl($1);
86 		}
87 		| LP names RP = {
88 			$$ = $2;
89 		}
90 		;
91 
92 names:		  /* VOID */ {
93 			$$ = last_n = NULL;
94 		}
95 		| names NAME = {
96 			if (last_n == NULL)
97 				$$ = last_n = makenl($2);
98 			else {
99 				last_n->n_next = makenl($2);
100 				last_n = last_n->n_next;
101 				$$ = $1;
102 			}
103 		}
104 		;
105 
106 cmdlist:	  /* VOID */ {
107 			$$ = last_sc = NULL;
108 		}
109 		| cmdlist cmd = {
110 			if (last_sc == NULL)
111 				$$ = last_sc = $2;
112 			else {
113 				last_sc->sc_next = $2;
114 				last_sc = $2;
115 				$$ = $1;
116 			}
117 		}
118 		;
119 
120 cmd:		  INSTALL options opt_namelist SM = {
121 			register struct namelist *nl;
122 
123 			$1->sc_options = $2 | options;
124 			if ($3 != NULL) {
125 				nl = expand($3, E_VARS);
126 				if (nl && nl->n_next != NULL)
127 					yyerror("only one name allowed\n");
128 				$1->sc_name = nl ? nl->n_name: NULL;
129 				if (nl)
130 					free(nl);
131 			}
132 			$$ = $1;
133 		}
134 		| NOTIFY namelist SM = {
135 			if ($2 != NULL)
136 				$1->sc_args = expand($2, E_VARS);
137 			$$ = $1;
138 		}
139 		| EXCEPT namelist SM = {
140 			if ($2 != NULL)
141 				$1->sc_args = expand($2, E_ALL);
142 			$$ = $1;
143 		}
144 		| PATTERN namelist SM = {
145 			struct namelist *nl;
146 			char *cp, *re_comp();
147 
148 			/*
149 			 *	We dup the namelist in $2 because expand()
150 			 *	destroys the list referred to in its first
151 			 *	argument.
152 			 */
153 			for (nl = expand(dupnl($2), E_VARS); nl != NULL;
154 				nl = nl->n_next)
155 				if ((cp = re_comp(nl->n_name)) != NULL)
156 					yyerror(cp);
157 			$1->sc_args = expand($2, E_VARS);
158 			$$ = $1;
159 		}
160 		| SPECIAL opt_namelist STRING SM = {
161 			if ($2 != NULL)
162 				$1->sc_args = expand($2, E_ALL);
163 			$1->sc_name = $3;
164 			$$ = $1;
165 		}
166 		;
167 
168 options:	  /* VOID */ = {
169 			$$ = 0;
170 		}
171 		| options OPTION = {
172 			$$ |= $2;
173 		}
174 		;
175 
176 opt_namelist:	  /* VOID */ = {
177 			$$ = NULL;
178 		}
179 		| namelist = {
180 			$$ = $1;
181 		}
182 		;
183 
184 %%
185 
186 int	yylineno = 1;
187 extern	FILE *fin;
188 
189 int
190 yylex()
191 {
192 	static char yytext[INMAX];
193 	register int c;
194 	register char *cp1, *cp2;
195 	static char quotechars[] = "[]{}*?$";
196 
197 again:
198 	switch (c = getc(fin)) {
199 	case EOF:  /* end of file */
200 		return(0);
201 
202 	case '#':  /* start of comment */
203 		while ((c = getc(fin)) != EOF && c != '\n')
204 			;
205 		if (c == EOF)
206 			return(0);
207 		/* FALLTHROUGH */
208 	case '\n':
209 		yylineno++;
210 	case ' ':
211 	case '\t':  /* skip blanks */
212 		goto again;
213 
214 	case '=':  /* EQUAL */
215 		return(EQUAL);
216 
217 	case '(':  /* LP */
218 		return(LP);
219 
220 	case ')':  /* RP */
221 		return(RP);
222 
223 	case ';':  /* SM */
224 		return(SM);
225 
226 	case '-':  /* -> */
227 		if ((c = getc(fin)) == '>')
228 			return(ARROW);
229 		ungetc(c, fin);
230 		c = '-';
231 		break;
232 
233 	case '"':  /* STRING */
234 		cp1 = yytext;
235 		cp2 = &yytext[INMAX - 1];
236 		for (;;) {
237 			if (cp1 >= cp2) {
238 				yyerror("command string too long\n");
239 				break;
240 			}
241 			c = getc(fin);
242 			if (c == EOF || c == '"')
243 				break;
244 			if (c == '\\') {
245 				if ((c = getc(fin)) == EOF) {
246 					*cp1++ = '\\';
247 					break;
248 				}
249 			}
250 			if (c == '\n') {
251 				yylineno++;
252 				c = ' '; /* can't send '\n' */
253 			}
254 			*cp1++ = c;
255 		}
256 		if (c != '"')
257 			yyerror("missing closing '\"'\n");
258 		*cp1 = '\0';
259 		yylval.string = makestr(yytext);
260 		return(STRING);
261 
262 	case ':':  /* : or :: */
263 		if ((c = getc(fin)) == ':')
264 			return(DCOLON);
265 		ungetc(c, fin);
266 		return(COLON);
267 	}
268 	cp1 = yytext;
269 	cp2 = &yytext[INMAX - 1];
270 	for (;;) {
271 		if (cp1 >= cp2) {
272 			yyerror("input line too long\n");
273 			break;
274 		}
275 		if (c == '\\') {
276 			if ((c = getc(fin)) != EOF) {
277 				if (any(c, quotechars))
278 					c |= QUOTE;
279 			} else {
280 				*cp1++ = '\\';
281 				break;
282 			}
283 		}
284 		*cp1++ = c;
285 		c = getc(fin);
286 		if (c == EOF || any(c, " \"'\t()=;:\n")) {
287 			ungetc(c, fin);
288 			break;
289 		}
290 	}
291 	*cp1 = '\0';
292 	if (yytext[0] == '-' && yytext[2] == '\0') {
293 		switch (yytext[1]) {
294 		case 'b':
295 			yylval.intval = COMPARE;
296 			return(OPTION);
297 
298 		case 'R':
299 			yylval.intval = REMOVE;
300 			return(OPTION);
301 
302 		case 'v':
303 			yylval.intval = VERIFY;
304 			return(OPTION);
305 
306 		case 'w':
307 			yylval.intval = WHOLE;
308 			return(OPTION);
309 
310 		case 'y':
311 			yylval.intval = YOUNGER;
312 			return(OPTION);
313 
314 		case 'h':
315 			yylval.intval = FOLLOW;
316 			return(OPTION);
317 
318 		case 'i':
319 			yylval.intval = IGNLNKS;
320 			return(OPTION);
321 		}
322 	}
323 	if (!strcmp(yytext, "install"))
324 		c = INSTALL;
325 	else if (!strcmp(yytext, "notify"))
326 		c = NOTIFY;
327 	else if (!strcmp(yytext, "except"))
328 		c = EXCEPT;
329 	else if (!strcmp(yytext, "except_pat"))
330 		c = PATTERN;
331 	else if (!strcmp(yytext, "special"))
332 		c = SPECIAL;
333 	else {
334 		yylval.string = makestr(yytext);
335 		return(NAME);
336 	}
337 	yylval.subcmd = makesubcmd(c);
338 	return(c);
339 }
340 
341 int
342 any(c, str)
343 	register int c;
344 	register char *str;
345 {
346 	while (*str)
347 		if (c == *str++)
348 			return(1);
349 	return(0);
350 }
351 
352 /*
353  * Insert or append ARROW command to list of hosts to be updated.
354  */
355 void
356 insert(label, files, hosts, subcmds)
357 	char *label;
358 	struct namelist *files, *hosts;
359 	struct subcmd *subcmds;
360 {
361 	register struct cmd *c, *prev, *nc;
362 	register struct namelist *h, *oldh;
363 
364 	files = expand(files, E_VARS|E_SHELL);
365 	hosts = expand(hosts, E_ALL);
366 if (debug) {
367 	printf("insert:  files = ");
368 	prnames(files);
369 	printf("insert:  hosts = ");
370 	prnames(hosts);
371 	if (cmds)
372 		prcmd(cmds);
373 	else
374 		printf("insert:  cmds NULL\n");
375 }
376 	for (h = hosts; h != NULL; oldh = h, h = h->n_next, free(oldh)) {
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 static void
417 append(label, files, stamp, subcmds)
418 	char *label;
419 	struct namelist *files;
420 	char *stamp;
421 	struct subcmd *subcmds;
422 {
423 	register struct cmd *c;
424 
425 	c = ALLOC(cmd);
426 	if (c == NULL)
427 		fatal("ran out of memory\n");
428 	c->c_type = DCOLON;
429 	c->c_name = stamp;
430 	c->c_label = label;
431 	c->c_files = expand(files, E_ALL);
432 	c->c_cmds = subcmds;
433 	c->c_next = NULL;
434 	if (cmds == NULL)
435 		cmds = last_cmd = c;
436 	else {
437 		last_cmd->c_next = c;
438 		last_cmd = c;
439 	}
440 }
441 
442 /*
443  * Error printing routine in parser.
444  */
445 void
446 yyerror(s)
447 	char *s;
448 {
449 	extern int yychar;
450 
451 	nerrs++;
452 	fflush(stdout);
453 	fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
454 }
455 
456 /*
457  * Return a copy of the string.
458  */
459 char *
460 makestr(str)
461 	char *str;
462 {
463 	register char *cp, *s;
464 
465 	str = cp = malloc(strlen(s = str) + 1);
466 	if (cp == NULL)
467 		fatal("ran out of memory\n");
468 	while (*cp++ = *s++)
469 		;
470 	return(str);
471 }
472 
473 /*
474  * Allocate a namelist structure.
475  */
476 struct namelist *
477 makenl(name)
478 	char *name;
479 {
480 	register struct namelist *nl;
481 
482 	nl = ALLOC(namelist);
483 	if (nl == NULL)
484 		fatal("ran out of memory\n");
485 	nl->n_name = name;
486 	nl->n_next = NULL;
487 	return(nl);
488 }
489 
490 /*
491  * Duplicate an existing namelist structure.  Only used by the PATTERN
492  * code, and then only because expand() is destructive.
493  */
494 struct namelist *
495 dupnl(old)
496 	struct namelist *old;
497 {
498 	struct namelist *n;
499 	struct namelist *new, *newhead = (struct namelist *) NULL;
500 	struct namelist *prev = (struct namelist *) NULL;
501 
502 	for (n = old; n; n = n->n_next) {
503 		new = ALLOC(namelist);
504 		if (new == (struct namelist *) NULL)
505 			fatal("ran out of memory\n");
506 		if (newhead == (struct namelist *) NULL)
507 			newhead = new;
508 		if (n->n_name) {
509 			if ((new->n_name = strdup(n->n_name)) == (char *) NULL)
510 				fatal("ran out of memory\n");
511 		} else
512 			new->n_name = (char *) NULL;
513 		if (prev)
514 			prev->n_next = new;
515 		prev = new;
516 	}
517 	if (prev)
518 		prev->n_next = (struct namelist *) NULL;
519 
520 	return (newhead);
521 }
522 
523 /*
524  * Make a sub command for lists of variables, commands, etc.
525  */
526 struct subcmd *
527 makesubcmd(type, name)
528 	int type;
529 	register char *name;
530 {
531 	register char *cp;
532 	register struct subcmd *sc;
533 
534 	sc = ALLOC(subcmd);
535 	if (sc == NULL)
536 		fatal("ran out of memory\n");
537 	sc->sc_type = type;
538 	sc->sc_args = NULL;
539 	sc->sc_next = NULL;
540 	sc->sc_name = NULL;
541 	return(sc);
542 }
543