sh.om (10321B)
1 struct shell { 2 a: *alloc; 3 prog: *byte; 4 argv: **byte; 5 argc: int; 6 scriptfd: int; 7 script: *byte; 8 c: int; 9 tt: int; 10 buf: *byte; 11 len: int; 12 cap: int; 13 status: int; 14 } 15 16 enum { 17 T_EOF, 18 T_LF, 19 T_IN, 20 T_OUT, 21 T_PIPE, 22 T_OR, 23 T_AND, 24 T_BG, 25 T_WORD, 26 T_NOT, 27 T_LPAR, 28 T_RPAR, 29 } 30 31 func feedc(s: *shell) { 32 var b: byte; 33 var ret: int; 34 35 if s.script[0] { 36 s.c = s.script[0] as int; 37 s.script = &s.script[1]; 38 return; 39 } 40 41 if s.scriptfd >= 0 { 42 ret = read(s.scriptfd, &b, 1); 43 if ret == 1 { 44 s.c = b as int; 45 } else if ret == 0 { 46 s.c = -1; 47 } else { 48 exit(3); 49 } 50 return; 51 } 52 53 s.c = -1; 54 } 55 56 func feedw(s: *shell) { 57 if s.len == s.cap { 58 die("too long"); 59 } 60 if s.c == -1 { 61 die("unexpected EOF"); 62 } 63 s.buf[s.len] = s.c as byte; 64 s.len = s.len + 1; 65 feedc(s); 66 } 67 68 func feed_quote(s: *shell) { 69 feedc(s); 70 loop { 71 if s.c == '\"' { 72 feedc(s); 73 break; 74 } 75 76 if s.c == '\\' { 77 feedc(s); 78 feedw(s); 79 continue; 80 } 81 82 if s.c == '$' { 83 feed_var(s); 84 continue; 85 } 86 87 feedw(s); 88 } 89 } 90 91 func feed_var(s: *shell) { 92 feedw(s); 93 94 if s.c != '{' { 95 die("variable missing {"); 96 } 97 feedw(s); 98 99 if !( 100 (s.c >= 'A' && s.c <= 'Z') 101 || (s.c >= 'a' && s.c <= 'z') 102 || (s.c == '_' && s.c <= 'z') 103 ) { 104 die("invalid name"); 105 } 106 107 loop { 108 if s.c == '}' { 109 feedw(s); 110 break; 111 } 112 113 if !( 114 (s.c >= 'A' && s.c <= 'Z') 115 || (s.c >= 'a' && s.c <= 'z') 116 || (s.c >= '0' && s.c <= '9') 117 || (s.c == '_' && s.c <= 'z') 118 ) { 119 die("invalid name"); 120 } 121 122 feedw(s); 123 } 124 } 125 126 func feed(s: *shell) { 127 loop { 128 if s.c < 0 { 129 s.tt = T_EOF; 130 return; 131 } 132 133 if s.c == '#' { 134 loop { 135 feedc(s); 136 if s.c == '\n' || s.c < 0 { 137 break; 138 } 139 } 140 continue; 141 } 142 143 if s.c == '\n' { 144 feedc(s); 145 s.tt = T_LF; 146 return; 147 } 148 149 if s.c == ' ' || s.c == '\t' || s.c == '\r' { 150 feedc(s); 151 continue; 152 } 153 154 break; 155 } 156 157 if s.c == '<' { 158 feedc(s); 159 s.tt = T_IN; 160 return; 161 } 162 163 if s.c == '>' { 164 feedc(s); 165 s.tt = T_OUT; 166 return; 167 } 168 169 if s.c == '&' { 170 feedc(s); 171 s.tt = T_BG; 172 if s.c == '&' { 173 feedc(s); 174 s.tt = T_AND; 175 } 176 return; 177 } 178 179 if s.c == '|' { 180 feedc(s); 181 s.tt = T_PIPE; 182 if s.c == '|' { 183 feedc(s); 184 s.tt = T_OR; 185 } 186 return; 187 } 188 189 if s.c == '!' { 190 feedc(s); 191 s.tt = T_NOT; 192 return; 193 } 194 195 if s.c == ';' { 196 feedc(s); 197 s.tt = T_LF; 198 return; 199 } 200 201 if s.c == '(' { 202 feedc(s); 203 s.tt = T_LPAR; 204 return; 205 } 206 207 if s.c == ')' { 208 feedc(s); 209 s.tt = T_RPAR; 210 return; 211 } 212 213 s.len = 0; 214 215 s.tt = T_WORD; 216 loop { 217 if ( 218 s.c > ' ' && s.c < '~' 219 && s.c != '&' && s.c != '|' && s.c != ';' 220 && s.c != '<' && s.c != '>' && s.c != '"' 221 && s.c != '\'' && s.c != '\\' && s.c != '*' 222 && s.c != '?' && s.c != '[' && s.c != '#' 223 && s.c != ']' && s.c != '%' && s.c != '$' 224 && s.c != '(' && s.c != ')' && s.c != '$' 225 ) { 226 feedw(s); 227 continue; 228 } 229 230 if s.c == '\\' { 231 feedc(s); 232 feedw(s); 233 continue; 234 } 235 236 if s.c == '"' { 237 feed_quote(s); 238 continue; 239 } 240 241 if s.c == '$' { 242 feed_var(s); 243 continue; 244 } 245 246 break; 247 } 248 249 if !s.len { 250 die("invalid char"); 251 } 252 } 253 254 enum { 255 C_NOP, 256 C_IN, 257 C_OUT, 258 C_CMD, 259 C_ARG, 260 C_FOR, 261 C_SUBSHELL, 262 C_AND, 263 C_OR, 264 C_PIPE, 265 } 266 267 struct cmd { 268 kind: int; 269 word: *byte; 270 next: *cmd; 271 arg: *cmd; 272 redir: *cmd; 273 cond: *cmd; 274 body: *cmd; 275 other: *cmd; 276 } 277 278 func mkcmd(s: *shell, kind: int): *cmd { 279 var c: *cmd; 280 c = alloc(s.a, sizeof(*c)) as *cmd; 281 bzero(c as *byte, sizeof(*c)); 282 c.kind = kind; 283 return c; 284 } 285 286 // command = LF 287 // | and_or LF 288 func parse_command(s: *shell): *cmd { 289 var c: *cmd; 290 291 if s.tt == T_LF { 292 c = mkcmd(s, C_NOP); 293 feed(s); 294 return c; 295 } 296 297 c = parse_and_or(s); 298 if !c { 299 return nil; 300 } 301 302 if s.tt != T_LF { 303 die("command has no end"); 304 } 305 feed(s); 306 307 return c; 308 } 309 310 // and_or = pipeline 311 // | pipeline '||' and_or 312 // | pipeine '&&' and_or 313 func parse_and_or(s: *shell): *cmd { 314 var c: *cmd; 315 var link: **cmd; 316 var p: *cmd; 317 var t: *cmd; 318 319 c = nil; 320 link = &c; 321 322 c = parse_pipeline(s); 323 if !c { 324 return nil; 325 } 326 327 loop { 328 if s.tt == T_AND { 329 feed(s); 330 p = parse_pipeline(s); 331 if ! p { 332 die("expected pipeline"); 333 } 334 t = mkcmd(s, C_AND); 335 t.cond = *link; 336 t.other = p; 337 *link = t; 338 link = &t.other; 339 } 340 341 if s.tt == T_OR { 342 feed(s); 343 p = parse_pipeline(s); 344 if ! p { 345 die("expected pipeline"); 346 } 347 t = mkcmd(s, C_OR); 348 t.cond = *link; 349 t.other = p; 350 *link = t; 351 link = &t.other; 352 } 353 354 return c; 355 } 356 } 357 358 // pipeline = '!' pipeline 359 // | compound 360 // | compound '|' pipeline 361 func parse_pipeline(s: *shell): *cmd { 362 var neg: int; 363 var c: *cmd; 364 var link: **cmd; 365 var p: *cmd; 366 var t: *cmd; 367 368 c = nil; 369 link = &c; 370 371 if s.tt == T_NOT { 372 loop { 373 if s.tt != T_NOT { 374 break; 375 } 376 feed(s); 377 neg = neg + 1; 378 } 379 380 c = parse_compound(s); 381 if !c { 382 die("expected compound"); 383 } 384 } else { 385 c = parse_compound(s); 386 if !c { 387 return nil; 388 } 389 } 390 391 loop { 392 if s.tt != T_PIPE { 393 return c; 394 } 395 feed(s); 396 397 p = parse_compound(s); 398 if !p { 399 die("expected compound"); 400 } 401 402 t = mkcmd(s, C_PIPE); 403 t.cond = *link; 404 t.body = p; 405 *link = t; 406 link = &t.body; 407 } 408 } 409 410 // compound = subshell | if | for | simple 411 func parse_compound(s: *shell): *cmd { 412 var c: *cmd; 413 414 c = parse_subshell(s); 415 if c { 416 return c; 417 } 418 419 c = parse_for(s); 420 if c { 421 return c; 422 } 423 424 c = parse_simple(s); 425 if c { 426 return c; 427 } 428 429 return nil; 430 } 431 432 // subshell = '(' command_list ')' 433 // | subshell redir_list 434 func parse_subshell(s: *shell): *cmd { 435 var body: *cmd; 436 var link: **cmd; 437 var t: *cmd; 438 439 body = nil; 440 link = &body; 441 442 if s.tt != T_LPAR { 443 return nil; 444 } 445 feed(s); 446 447 loop { 448 t = parse_command(s); 449 if !t { 450 break; 451 } 452 *link = t; 453 link = &t.next; 454 } 455 456 if s.tt != T_RPAR { 457 die("expected )"); 458 } 459 feed(s); 460 461 t = mkcmd(s, C_SUBSHELL); 462 t.redir = parse_redir_list(s); 463 return t; 464 } 465 466 func parse_keyword(s: *shell, key: *byte): int { 467 if s.tt != T_WORD { 468 return 0; 469 } 470 471 if s.len != strlen(key) { 472 return 0; 473 } 474 475 if memcmp(s.buf, key, s.len) { 476 return 0; 477 } 478 479 feed(s); 480 481 return 1; 482 } 483 484 // for = 'for' NAME 'in' word_list LF 'do' command_list 'done' 485 // | for redir_list 486 func parse_for(s: *shell): *cmd { 487 var arg: *cmd; 488 var arg_link: **cmd; 489 var body: *cmd; 490 var body_link: **cmd; 491 var t: *cmd; 492 var w: *byte; 493 494 arg = nil; 495 arg_link = &arg; 496 body = nil; 497 body_link = &body; 498 499 if !parse_keyword(s, "for") { 500 return nil; 501 } 502 503 w = take(s); 504 if !w { 505 die("expceted name"); 506 } 507 508 if !parse_keyword(s, "in") { 509 die("expected in"); 510 } 511 512 loop { 513 if s.tt == T_LF { 514 feed(s); 515 break; 516 } 517 518 w = take(s); 519 if !w { 520 die("expected word"); 521 } 522 t = mkcmd(s, C_ARG); 523 t.word = w; 524 *arg_link = t; 525 arg_link = &t.next; 526 } 527 528 if !parse_keyword(s, "do") { 529 die("expected do"); 530 } 531 532 loop { 533 if parse_keyword(s, "done") { 534 break; 535 } 536 537 t = parse_command(s); 538 if !t { 539 die("expected command"); 540 } 541 *body_link = t; 542 body_link = &t.next; 543 } 544 545 t = mkcmd(s, C_FOR); 546 t.arg = arg; 547 t.body = body; 548 t.redir = parse_redir_list(s); 549 return t; 550 } 551 552 // simple = word 553 // | redir 554 // | word simple 555 // | redir simple 556 func parse_simple(s: *shell): *cmd { 557 var arg: *cmd; 558 var arg_link: **cmd; 559 var redir: *cmd; 560 var redir_link: **cmd; 561 var c: *cmd; 562 var t: *cmd; 563 var w: *byte; 564 565 arg = nil; 566 arg_link = &arg; 567 redir = nil; 568 redir_link = &redir; 569 570 w = take(s); 571 if w { 572 t = mkcmd(s, C_ARG); 573 t.word = w; 574 *arg_link = t; 575 arg_link = &t.next; 576 } else { 577 t = parse_redir(s); 578 if t { 579 *redir_link = t; 580 redir_link = &t.next; 581 } else { 582 return nil; 583 } 584 } 585 586 loop { 587 w = take(s); 588 if w { 589 t = mkcmd(s, C_ARG); 590 t.word = w; 591 *arg_link = t; 592 arg_link = &t.next; 593 continue; 594 } 595 596 t = parse_redir(s); 597 if t { 598 *redir_link = t; 599 redir_link = &t.next; 600 continue; 601 } 602 603 t = mkcmd(s, C_CMD); 604 t.arg = arg; 605 t.redir = redir; 606 return t; 607 } 608 } 609 610 // redir_list = 611 // | redir redir_list 612 func parse_redir_list(s: *shell): *cmd { 613 var c: *cmd; 614 var t: *cmd; 615 var link: **cmd; 616 617 c = nil; 618 link = &c; 619 620 loop { 621 t = parse_redir(s); 622 if !t { 623 return c; 624 } 625 *link = t; 626 link = &t.next; 627 } 628 } 629 630 func take(s: *shell): *byte { 631 var w: *byte; 632 633 if s.tt != T_WORD { 634 return nil; 635 } 636 637 w = alloc(s.a, s.len + 1); 638 memcpy(w, s.buf, s.len); 639 w[s.len] = 0 as byte; 640 feed(s); 641 642 return w; 643 } 644 645 // redir = '>' word 646 // | '<' word 647 func parse_redir(s: *shell): *cmd { 648 var c: *cmd; 649 var w: *byte; 650 651 if s.tt == T_IN { 652 feed(s); 653 c = mkcmd(s, C_IN); 654 w = take(s); 655 if !w { 656 die("expected word"); 657 } 658 c.word = w; 659 return c; 660 } 661 662 if s.tt == T_OUT { 663 feed(s); 664 c = mkcmd(s, C_OUT); 665 w = take(s); 666 if !w { 667 die("expected word"); 668 } 669 c.word = w; 670 return c; 671 } 672 673 return nil; 674 } 675 676 func execute_command(s: *shell, c: *cmd) { 677 if c.kind == C_NOP { 678 return; 679 } else if c.kind == C_CMD { 680 } else if c.kind == C_FOR { 681 c = c.body; 682 loop { 683 if !c { 684 break; 685 } 686 execute_command(s, c); 687 c = c.next; 688 } 689 } else if c.kind == C_SUBSHELL { 690 c = c.body; 691 loop { 692 if !c { 693 break; 694 } 695 execute_command(s, c); 696 c = c.next; 697 } 698 } else if c.kind == C_AND { 699 execute_command(s, c.cond); 700 if s.status == 0 { 701 execute_command(s, c.other); 702 } 703 } else if c.kind == C_OR { 704 execute_command(s, c.cond); 705 if s.status != 0 { 706 execute_command(s, c.other); 707 } 708 } else if c.kind == C_PIPE { 709 execute_command(s, c.cond); 710 c = c.body; 711 loop { 712 if !c { 713 break; 714 } 715 execute_command(s, c); 716 c = c.body; 717 } 718 } else { 719 die("invalid"); 720 } 721 } 722 723 func main(argc: int, argv: **byte, envp: **byte) { 724 var a: alloc; 725 var s: shell; 726 var c: *cmd; 727 var i: int; 728 var fd: int; 729 setup_alloc(&a); 730 731 bzero((&s) as *byte, sizeof(s)); 732 s.a = &a; 733 734 if argc <= 1 { 735 s.scriptfd = 0; 736 s.script = ""; 737 s.prog = ""; 738 s.argc = 0; 739 s.argv = nil; 740 } else if strcmp(argv[1], "--") == 0 { 741 s.scriptfd = 0; 742 s.script = ""; 743 s.prog = ""; 744 s.argc = argc - 2; 745 s.argv = &argv[2]; 746 } else if argc > 2 && strcmp(argv[1], "-c") == 0 { 747 s.scriptfd = -1; 748 s.script = argv[2]; 749 s.prog = ""; 750 s.argc = argc - 3; 751 s.argv = &argv[3]; 752 } else { 753 fd = open(argv[1], O_RDONLY, 0); 754 if fd < 0 { 755 die("could not open script"); 756 } 757 s.scriptfd = fd; 758 s.script = ""; 759 s.prog = argv[1]; 760 s.argc = argc - 2; 761 s.argv = &argv[2]; 762 } 763 764 s.buf = alloc(&a, 4096); 765 s.len = 0; 766 s.cap = 4096; 767 768 feedc(&s); 769 feed(&s); 770 771 loop { 772 if s.tt == T_EOF { 773 exit(s.status); 774 } 775 776 c = parse_command(&s); 777 if !c { 778 exit(127); 779 } 780 781 execute_command(&s, c); 782 } 783 }