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