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