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