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