* Silence -Wold-style-definition (i.e., ansify).
[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  * @(#)gram.y   8.1 (Berkeley) 6/9/93
35  * $FreeBSD: src/usr.bin/rdist/gram.y,v 1.6 1999/08/28 01:05:07 peter Exp $
36  * $DragonFly: src/usr.bin/rdist/gram.y,v 1.5 2008/10/16 01:52:33 swildner Exp $
37  */
38
39 #include <sys/types.h>
40
41 #include <limits.h>
42 #include <regex.h>
43
44 #include "defs.h"
45
46 struct  cmd *cmds = NULL;
47 struct  cmd *last_cmd;
48 struct  namelist *last_n;
49 struct  subcmd *last_sc;
50
51 static char  *makestr(char *);
52
53 %}
54
55 %term EQUAL     1
56 %term LP        2
57 %term RP        3
58 %term SM        4
59 %term ARROW     5
60 %term COLON     6
61 %term DCOLON    7
62 %term NAME      8
63 %term STRING    9
64 %term INSTALL   10
65 %term NOTIFY    11
66 %term EXCEPT    12
67 %term PATTERN   13
68 %term SPECIAL   14
69 %term OPTION    15
70
71 %union {
72         int intval;
73         char *string;
74         struct subcmd *subcmd;
75         struct namelist *namel;
76 }
77
78 %type <intval> OPTION, options
79 %type <string> NAME, STRING
80 %type <subcmd> INSTALL, NOTIFY, EXCEPT, PATTERN, SPECIAL, cmdlist, cmd
81 %type <namel> namelist, names, opt_namelist
82
83 %%
84
85 file:             /* VOID */
86                 | file command
87                 ;
88
89 command:          NAME EQUAL namelist = {
90                         (void) lookup($1, INSERT, $3);
91                 }
92                 | namelist ARROW namelist cmdlist = {
93                         insert(NULL, $1, $3, $4);
94                 }
95                 | NAME COLON namelist ARROW namelist cmdlist = {
96                         insert($1, $3, $5, $6);
97                 }
98                 | namelist DCOLON NAME cmdlist = {
99                         append(NULL, $1, $3, $4);
100                 }
101                 | NAME COLON namelist DCOLON NAME cmdlist = {
102                         append($1, $3, $5, $6);
103                 }
104                 | error
105                 ;
106
107 namelist:         NAME = {
108                         $$ = makenl($1);
109                 }
110                 | LP names RP = {
111                         $$ = $2;
112                 }
113                 ;
114
115 names:            /* VOID */ {
116                         $$ = last_n = NULL;
117                 }
118                 | names NAME = {
119                         if (last_n == NULL)
120                                 $$ = last_n = makenl($2);
121                         else {
122                                 last_n->n_next = makenl($2);
123                                 last_n = last_n->n_next;
124                                 $$ = $1;
125                         }
126                 }
127                 ;
128
129 cmdlist:          /* VOID */ {
130                         $$ = last_sc = NULL;
131                 }
132                 | cmdlist cmd = {
133                         if (last_sc == NULL)
134                                 $$ = last_sc = $2;
135                         else {
136                                 last_sc->sc_next = $2;
137                                 last_sc = $2;
138                                 $$ = $1;
139                         }
140                 }
141                 ;
142
143 cmd:              INSTALL options opt_namelist SM = {
144                         struct namelist *nl;
145
146                         $1->sc_options = $2 | options;
147                         if ($3 != NULL) {
148                                 nl = expand($3, E_VARS);
149                                 if (nl) {
150                                         if (nl->n_next != NULL)
151                                             yyerror("only one name allowed\n");
152                                         $1->sc_name = nl->n_name;
153                                         free(nl);
154                                 } else
155                                         $1->sc_name = NULL;
156                         }
157                         $$ = $1;
158                 }
159                 | NOTIFY namelist SM = {
160                         if ($2 != NULL)
161                                 $1->sc_args = expand($2, E_VARS);
162                         $$ = $1;
163                 }
164                 | EXCEPT namelist SM = {
165                         if ($2 != NULL)
166                                 $1->sc_args = expand($2, E_ALL);
167                         $$ = $1;
168                 }
169                 | PATTERN namelist SM = {
170                         struct namelist *nl;
171                         regex_t rx;
172                         int val;
173                         char errbuf[_POSIX2_LINE_MAX];
174
175                         for (nl = $2; nl != NULL; nl = nl->n_next) {
176                                 if ((val = regcomp(&rx, nl->n_name, 
177                                                   REG_EXTENDED))) {
178                                         regerror(val, &rx, errbuf, 
179                                                  sizeof errbuf);
180                                         yyerror(errbuf);
181                                 }
182                                 regfree(&rx);
183                         }
184                         $1->sc_args = expand($2, E_VARS);
185                         $$ = $1;
186                 }
187                 | SPECIAL opt_namelist STRING SM = {
188                         if ($2 != NULL)
189                                 $1->sc_args = expand($2, E_ALL);
190                         $1->sc_name = $3;
191                         $$ = $1;
192                 }
193                 ;
194
195 options:          /* VOID */ = {
196                         $$ = 0;
197                 }
198                 | options OPTION = {
199                         $$ |= $2;
200                 }
201                 ;
202
203 opt_namelist:     /* VOID */ = {
204                         $$ = NULL;
205                 }
206                 | namelist = {
207                         $$ = $1;
208                 }
209                 ;
210
211 %%
212
213 int     yylineno = 1;
214 extern  FILE *fin;
215
216 int
217 yylex(void)
218 {
219         static char yytext[INMAX];
220         int c;
221         char *cp1, *cp2;
222         static char quotechars[] = "[]{}*?$";
223         
224 again:
225         switch (c = getc(fin)) {
226         case EOF:  /* end of file */
227                 return(0);
228
229         case '#':  /* start of comment */
230                 while ((c = getc(fin)) != EOF && c != '\n')
231                         ;
232                 if (c == EOF)
233                         return(0);
234         case '\n':
235                 yylineno++;
236         case ' ':
237         case '\t':  /* skip blanks */
238                 goto again;
239
240         case '=':  /* EQUAL */
241                 return(EQUAL);
242
243         case '(':  /* LP */
244                 return(LP);
245
246         case ')':  /* RP */
247                 return(RP);
248
249         case ';':  /* SM */
250                 return(SM);
251
252         case '-':  /* -> */
253                 if ((c = getc(fin)) == '>')
254                         return(ARROW);
255                 ungetc(c, fin);
256                 c = '-';
257                 break;
258
259         case '"':  /* STRING */
260                 cp1 = yytext;
261                 cp2 = &yytext[INMAX - 1];
262                 for (;;) {
263                         if (cp1 >= cp2) {
264                                 yyerror("command string too long\n");
265                                 break;
266                         }
267                         c = getc(fin);
268                         if (c == EOF || c == '"')
269                                 break;
270                         if (c == '\\') {
271                                 if ((c = getc(fin)) == EOF) {
272                                         *cp1++ = '\\';
273                                         break;
274                                 }
275                         }
276                         if (c == '\n') {
277                                 yylineno++;
278                                 c = ' '; /* can't send '\n' */
279                         }
280                         *cp1++ = c;
281                 }
282                 if (c != '"')
283                         yyerror("missing closing '\"'\n");
284                 *cp1 = '\0';
285                 yylval.string = makestr(yytext);
286                 return(STRING);
287
288         case ':':  /* : or :: */
289                 if ((c = getc(fin)) == ':')
290                         return(DCOLON);
291                 ungetc(c, fin);
292                 return(COLON);
293         }
294         cp1 = yytext;
295         cp2 = &yytext[INMAX - 1];
296         for (;;) {
297                 if (cp1 >= cp2) {
298                         yyerror("input line too long\n");
299                         break;
300                 }
301                 if (c == '\\') {
302                         if ((c = getc(fin)) != EOF) {
303                                 if (any(c, quotechars))
304                                         c |= QUOTE;
305                         } else {
306                                 *cp1++ = '\\';
307                                 break;
308                         }
309                 }
310                 *cp1++ = c;
311                 c = getc(fin);
312                 if (c == EOF || any(c, " \"'\t()=;:\n")) {
313                         ungetc(c, fin);
314                         break;
315                 }
316         }
317         *cp1 = '\0';
318         if (yytext[0] == '-' && yytext[2] == '\0') {
319                 switch (yytext[1]) {
320                 case 'b':
321                         yylval.intval = COMPARE;
322                         return(OPTION);
323
324                 case 'R':
325                         yylval.intval = REMOVE;
326                         return(OPTION);
327
328                 case 'v':
329                         yylval.intval = VERIFY;
330                         return(OPTION);
331
332                 case 'w':
333                         yylval.intval = WHOLE;
334                         return(OPTION);
335
336                 case 'y':
337                         yylval.intval = YOUNGER;
338                         return(OPTION);
339
340                 case 'h':
341                         yylval.intval = FOLLOW;
342                         return(OPTION);
343
344                 case 'i':
345                         yylval.intval = IGNLNKS;
346                         return(OPTION);
347                 }
348         }
349         if (!strcmp(yytext, "install"))
350                 c = INSTALL;
351         else if (!strcmp(yytext, "notify"))
352                 c = NOTIFY;
353         else if (!strcmp(yytext, "except"))
354                 c = EXCEPT;
355         else if (!strcmp(yytext, "except_pat"))
356                 c = PATTERN;
357         else if (!strcmp(yytext, "special"))
358                 c = SPECIAL;
359         else {
360                 yylval.string = makestr(yytext);
361                 return(NAME);
362         }
363         yylval.subcmd = makesubcmd(c);
364         return(c);
365 }
366
367 int
368 any(int c, char *str)
369 {
370         while (*str)
371                 if (c == *str++)
372                         return(1);
373         return(0);
374 }
375
376 /*
377  * Insert or append ARROW command to list of hosts to be updated.
378  */
379 void
380 insert(char *label, struct namelist *files, struct namelist *hosts, struct subcmd *subcmds)
381 {
382         struct cmd *c, *prev, *nc;
383         struct namelist *h, *next_h;
384
385         files = expand(files, E_VARS|E_SHELL);
386         hosts = expand(hosts, E_ALL);
387         for (h = hosts; h != NULL; next_h = h->n_next, free(h), h = next_h) {
388                 /*
389                  * Search command list for an update to the same host.
390                  */
391                 for (prev = NULL, c = cmds; c!=NULL; prev = c, c = c->c_next) {
392                         if (strcmp(c->c_name, h->n_name) == 0) {
393                                 do {
394                                         prev = c;
395                                         c = c->c_next;
396                                 } while (c != NULL &&
397                                         strcmp(c->c_name, h->n_name) == 0);
398                                 break;
399                         }
400                 }
401                 /*
402                  * Insert new command to update host.
403                  */
404                 nc = ALLOC(cmd);
405                 if (nc == NULL)
406                         fatal("ran out of memory\n");
407                 nc->c_type = ARROW;
408                 nc->c_name = h->n_name;
409                 nc->c_label = label;
410                 nc->c_files = files;
411                 nc->c_cmds = subcmds;
412                 nc->c_next = c;
413                 if (prev == NULL)
414                         cmds = nc;
415                 else
416                         prev->c_next = nc;
417                 /* update last_cmd if appending nc to cmds */
418                 if (c == NULL)
419                         last_cmd = nc;
420         }
421 }
422
423 /*
424  * Append DCOLON command to the end of the command list since these are always
425  * executed in the order they appear in the distfile.
426  */
427 void
428 append(char *label, struct namelist *files, char *stamp, struct subcmd *subcmds)
429 {
430         struct cmd *c;
431
432         c = ALLOC(cmd);
433         if (c == NULL)
434                 fatal("ran out of memory\n");
435         c->c_type = DCOLON;
436         c->c_name = stamp;
437         c->c_label = label;
438         c->c_files = expand(files, E_ALL);
439         c->c_cmds = subcmds;
440         c->c_next = NULL;
441         if (cmds == NULL)
442                 cmds = last_cmd = c;
443         else {
444                 last_cmd->c_next = c;
445                 last_cmd = c;
446         }
447 }
448
449 /*
450  * Error printing routine in parser.
451  */
452 void
453 yyerror(char *s)
454 {
455         ++nerrs;
456         fflush(stdout);
457         fprintf(stderr, "rdist: line %d: %s\n", yylineno, s);
458 }
459
460 /*
461  * Return a copy of the string.
462  */
463 static char *
464 makestr(char *str)
465 {
466         char *cp, *s;
467
468         str = cp = malloc(strlen(s = str) + 1);
469         if (cp == NULL)
470                 fatal("ran out of memory\n");
471         while ((*cp++ = *s++))
472                 ;
473         return(str);
474 }
475
476 /*
477  * Allocate a namelist structure.
478  */
479 struct namelist *
480 makenl(char *name)
481 {
482         struct namelist *nl;
483
484         nl = ALLOC(namelist);
485         if (nl == NULL)
486                 fatal("ran out of memory\n");
487         nl->n_name = name;
488         nl->n_next = NULL;
489         return(nl);
490 }
491
492 /*
493  * Make a sub command for lists of variables, commands, etc.
494  */
495 struct subcmd *
496 makesubcmd(int type)
497 {
498         struct subcmd *sc;
499
500         sc = ALLOC(subcmd);
501         if (sc == NULL)
502                 fatal("ran out of memory\n");
503         sc->sc_type = type;
504         sc->sc_args = NULL;
505         sc->sc_next = NULL;
506         sc->sc_name = NULL;
507         return(sc);
508 }