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