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 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 */ 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 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 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 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 * 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 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 * 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 * 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 * 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 * 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