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