1 %{
2 /* $NetBSD: gram.y,v 1.14 2019/02/03 03:19:29 mrg Exp $ */
3
4 /*
5 * Copyright (c) 1983, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <sys/cdefs.h>
34 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#)gram.y 8.1 (Berkeley) 6/9/93";
37 #else
38 __RCSID("$NetBSD: gram.y,v 1.14 2019/02/03 03:19:29 mrg Exp $");
39 #endif
40 #endif /* not lint */
41
42 #include "defs.h"
43
44 struct cmd *cmds = NULL;
45 struct cmd *last_cmd;
46 struct namelist *last_n;
47 struct subcmd *last_sc;
48
49 static char *makestr(char *);
50 void append(char *, struct namelist *, char *, struct subcmd *);
51
52 %}
53
54 %term EQUAL 1
55 %term LP 2
56 %term RP 3
57 %term SM 4
58 %term ARROW 5
59 %term COLON 6
60 %term DCOLON 7
61 %term NAME 8
62 %term STRING 9
63 %term INSTALL 10
64 %term NOTIFY 11
65 %term EXCEPT 12
66 %term PATTERN 13
67 %term SPECIAL 14
68 %term OPTION 15
69
70 %union {
71 int intval;
72 char *string;
73 struct subcmd *subcmd;
74 struct namelist *namel;
75 }
76
77 %type <intval> OPTION, options
78 %type <string> NAME, STRING
79 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
80 %type <namel> namelist, names, opt_namelist
81
82 %%
83
84 file: /* VOID */
85 | file command
86 ;
87
88 command: NAME EQUAL namelist = {
89 (void) lookup($1, INSERT, $3);
90 }
91 | namelist ARROW namelist cmdlist = {
92 insert(NULL, $1, $3, $4);
93 }
94 | NAME COLON namelist ARROW namelist cmdlist = {
95 insert($1, $3, $5, $6);
96 }
97 | namelist DCOLON NAME cmdlist = {
98 append(NULL, $1, $3, $4);
99 }
100 | NAME COLON namelist DCOLON NAME cmdlist = {
101 append($1, $3, $5, $6);
102 }
103 | error
104 ;
105
106 namelist: NAME = {
107 $$ = makenl($1);
108 }
109 | LP names RP = {
110 $$ = $2;
111 }
112 ;
113
114 names: /* VOID */ {
115 $$ = last_n = NULL;
116 }
117 | names NAME = {
118 if (last_n == NULL)
119 $$ = last_n = makenl($2);
120 else {
121 last_n->n_next = makenl($2);
122 last_n = last_n->n_next;
123 $$ = $1;
124 }
125 }
126 ;
127
128 cmdlist: /* VOID */ {
129 $$ = last_sc = NULL;
130 }
131 | cmdlist cmd = {
132 if (last_sc == NULL)
133 $$ = last_sc = $2;
134 else {
135 last_sc->sc_next = $2;
136 last_sc = $2;
137 $$ = $1;
138 }
139 }
140 ;
141
142 cmd: INSTALL options opt_namelist SM = {
143 struct namelist *nl;
144
145 $1->sc_options = $2 | options;
146 if ($3 != NULL) {
147 nl = expand($3, E_VARS);
148 if (nl) {
149 if (nl->n_next != NULL)
150 yyerror("only one name allowed\n");
151 $1->sc_name = nl->n_name;
152 free(nl);
153 } else
154 $1->sc_name = NULL;
155 }
156 $$ = $1;
157 }
158 | NOTIFY namelist SM = {
159 if ($2 != NULL)
160 $1->sc_args = expand($2, E_VARS);
161 $$ = $1;
162 }
163 | EXCEPT namelist SM = {
164 if ($2 != NULL)
165 $1->sc_args = expand($2, E_ALL);
166 $$ = $1;
167 }
168 | PATTERN namelist SM = {
169 if ($2 != NULL)
170 $1->sc_args = expand($2, E_VARS);
171 $$ = $1;
172 }
173 | SPECIAL opt_namelist STRING SM = {
174 if ($2 != NULL)
175 $1->sc_args = expand($2, E_ALL);
176 $1->sc_name = $3;
177 $$ = $1;
178 }
179 ;
180
181 options: /* VOID */ = {
182 $$ = 0;
183 }
184 | options OPTION = {
185 $$ |= $2;
186 }
187 ;
188
189 opt_namelist: /* VOID */ = {
190 $$ = NULL;
191 }
192 | namelist = {
193 $$ = $1;
194 }
195 ;
196
197 %%
198
199 int yylineno = 1;
200 extern FILE *fin;
201
202 int yylex(void);
203
204 int
yylex(void)205 yylex(void)
206 {
207 static char yytext[INMAX];
208 int c;
209 char *cp1, *cp2;
210 static char quotechars[] = "[]{}*?$";
211
212 again:
213 switch (c = getc(fin)) {
214 case EOF: /* end of file */
215 return(0);
216
217 case '#': /* start of comment */
218 while ((c = getc(fin)) != EOF && c != '\n')
219 ;
220 if (c == EOF)
221 return(0);
222 /* FALLTHROUGH */
223 case '\n':
224 yylineno++;
225 /* FALLTHROUGH */
226 case ' ':
227 case '\t': /* skip blanks */
228 goto again;
229
230 case '=': /* EQUAL */
231 return(EQUAL);
232
233 case '(': /* LP */
234 return(LP);
235
236 case ')': /* RP */
237 return(RP);
238
239 case ';': /* SM */
240 return(SM);
241
242 case '-': /* -> */
243 if ((c = getc(fin)) == '>')
244 return(ARROW);
245 ungetc(c, fin);
246 c = '-';
247 break;
248
249 case '"': /* STRING */
250 cp1 = yytext;
251 cp2 = &yytext[INMAX - 1];
252 for (;;) {
253 if (cp1 >= cp2) {
254 yyerror("command string too long\n");
255 break;
256 }
257 c = getc(fin);
258 if (c == EOF || c == '"')
259 break;
260 if (c == '\\') {
261 if ((c = getc(fin)) == EOF) {
262 *cp1++ = '\\';
263 break;
264 }
265 }
266 if (c == '\n') {
267 yylineno++;
268 c = ' '; /* can't send '\n' */
269 }
270 *cp1++ = c;
271 }
272 if (c != '"')
273 yyerror("missing closing '\"'\n");
274 *cp1 = '\0';
275 yylval.string = makestr(yytext);
276 return(STRING);
277
278 case ':': /* : or :: */
279 if ((c = getc(fin)) == ':')
280 return(DCOLON);
281 ungetc(c, fin);
282 return(COLON);
283 }
284 cp1 = yytext;
285 cp2 = &yytext[INMAX - 1];
286 for (;;) {
287 if (cp1 >= cp2) {
288 yyerror("input line too long\n");
289 break;
290 }
291 if (c == '\\') {
292 if ((c = getc(fin)) != EOF) {
293 if (any(c, quotechars))
294 c |= QUOTE;
295 } else {
296 *cp1++ = '\\';
297 break;
298 }
299 }
300 *cp1++ = c;
301 c = getc(fin);
302 if (c == EOF || any(c, " \"'\t()=;:\n")) {
303 ungetc(c, fin);
304 break;
305 }
306 }
307 *cp1 = '\0';
308 if (yytext[0] == '-' && yytext[2] == '\0') {
309 switch (yytext[1]) {
310 case 'b':
311 yylval.intval = COMPARE;
312 return(OPTION);
313
314 case 'R':
315 yylval.intval = REMOVE;
316 return(OPTION);
317
318 case 'v':
319 yylval.intval = VERIFY;
320 return(OPTION);
321
322 case 'w':
323 yylval.intval = WHOLE;
324 return(OPTION);
325
326 case 'y':
327 yylval.intval = YOUNGER;
328 return(OPTION);
329
330 case 'h':
331 yylval.intval = FOLLOW;
332 return(OPTION);
333
334 case 'i':
335 yylval.intval = IGNLNKS;
336 return(OPTION);
337 }
338 }
339 if (!strcmp(yytext, "install"))
340 c = INSTALL;
341 else if (!strcmp(yytext, "notify"))
342 c = NOTIFY;
343 else if (!strcmp(yytext, "except"))
344 c = EXCEPT;
345 else if (!strcmp(yytext, "except_pat"))
346 c = PATTERN;
347 else if (!strcmp(yytext, "special"))
348 c = SPECIAL;
349 else {
350 yylval.string = makestr(yytext);
351 return(NAME);
352 }
353 yylval.subcmd = makesubcmd(c);
354 return(c);
355 }
356
357 int
any(int c,const char * str)358 any(int c, const char *str)
359 {
360 while (*str)
361 if (c == *str++)
362 return(1);
363 return(0);
364 }
365
366 /*
367 * Insert or append ARROW command to list of hosts to be updated.
368 */
369 void
insert(char * label,struct namelist * files,struct namelist * hosts,struct subcmd * subcmds)370 insert(char *label, struct namelist *files, struct namelist *hosts,
371 struct subcmd *subcmds)
372 {
373 struct cmd *c, *prev, *nc;
374 struct namelist *h, *nexth;
375
376 files = expand(files, E_VARS|E_SHELL);
377 hosts = expand(hosts, E_ALL);
378 for (h = hosts; h != NULL; nexth = h->n_next, free(h), h = nexth) {
379 /*
380 * Search command list for an update to the same host.
381 */
382 for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
383 if (strcmp(c->c_name, h->n_name) == 0) {
384 do {
385 prev = c;
386 c = c->c_next;
387 } while (c != NULL &&
388 strcmp(c->c_name, h->n_name) == 0);
389 break;
390 }
391 }
392 /*
393 * Insert new command to update host.
394 */
395 nc = ALLOC(cmd);
396 if (nc == NULL)
397 fatal("ran out of memory\n");
398 nc->c_type = ARROW;
399 nc->c_name = h->n_name;
400 nc->c_label = label;
401 nc->c_files = files;
402 nc->c_cmds = subcmds;
403 nc->c_next = c;
404 if (prev == NULL)
405 cmds = nc;
406 else
407 prev->c_next = nc;
408 /* update last_cmd if appending nc to cmds */
409 if (c == NULL)
410 last_cmd = nc;
411 }
412 }
413
414 /*
415 * Append DCOLON command to the end of the command list since these are always
416 * executed in the order they appear in the distfile.
417 */
418 void
append(char * label,struct namelist * files,char * stamp,struct subcmd * subcmds)419 append(char *label, struct namelist *files, char *stamp,
420 struct subcmd *subcmds)
421 {
422 struct cmd *c;
423
424 c = ALLOC(cmd);
425 if (c == NULL)
426 fatal("ran out of memory\n");
427 c->c_type = DCOLON;
428 c->c_name = stamp;
429 c->c_label = label;
430 c->c_files = expand(files, E_ALL);
431 c->c_cmds = subcmds;
432 c->c_next = NULL;
433 if (cmds == NULL)
434 cmds = last_cmd = c;
435 else {
436 last_cmd->c_next = c;
437 last_cmd = c;
438 }
439 }
440
441 /*
442 * Error printing routine in parser.
443 */
444 void
yyerror(const char * s)445 yyerror(const char *s)
446 {
447
448 ++nerrs;
449 fflush(stdout);
450 fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
451 }
452
453 /*
454 * Return a copy of the string.
455 */
456 static char *
makestr(char * str)457 makestr(char *str)
458 {
459 char *cp, *s;
460
461 str = cp = malloc(strlen(s = str) + 1);
462 if (cp == NULL)
463 fatal("ran out of memory\n");
464 while ((*cp++ = *s++) != 0)
465 ;
466 return(str);
467 }
468
469 /*
470 * Allocate a namelist structure.
471 */
472 struct namelist *
makenl(char * name)473 makenl(char *name)
474 {
475 struct namelist *nl;
476
477 nl = ALLOC(namelist);
478 if (nl == NULL)
479 fatal("ran out of memory\n");
480 nl->n_name = name;
481 nl->n_next = NULL;
482 return(nl);
483 }
484
485 void
freenl(struct namelist * nl)486 freenl(struct namelist *nl)
487 {
488 if (nl == NULL)
489 return;
490 freenl(nl->n_next);
491 free(nl);
492 }
493
494 void
freesubcmd(struct subcmd * cmd)495 freesubcmd(struct subcmd *cmd)
496 {
497 if (cmd == NULL)
498 return;
499 freesubcmd(cmd->sc_next);
500 free(cmd);
501 }
502
503 /*
504 * Make a sub command for lists of variables, commands, etc.
505 */
506 struct subcmd *
makesubcmd(int type)507 makesubcmd(int type)
508 {
509 struct subcmd *sc;
510
511 sc = ALLOC(subcmd);
512 if (sc == NULL)
513 fatal("ran out of memory\n");
514 sc->sc_type = type;
515 sc->sc_args = NULL;
516 sc->sc_next = NULL;
517 sc->sc_name = NULL;
518 return(sc);
519 }
520