Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.bin / rdist / gram.y
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: src/usr.bin/rdist/gram.y,v 1.6 1999/08/28 01:05:07 peter Exp $";
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
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
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
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
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
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 *
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 *
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 *
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 }