os

An operating system
git clone https://erai.gay/code/os/
Log | Files | Refs | README | LICENSE

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 }