#include "gbos.h" /* gbos shell: prompt -> read a line -> parse -> run, with I/O redirection ( > file, < file ) and pipes ( cmd1 | cmd2, via a temp file ). Operators must be space-separated (e.g. cat readme > out ). */ #define ARGV0 ((char *)0xA000) /* where a child reads its command line */ /* split s into space-separated tokens (NUL-terminated in place). */ static unsigned char tokenize(char *s, char **tok, unsigned char max) { unsigned char n = 0; for (;;) { while (*s == ' ') *s++ = 0; if (*s == 0) break; if (n < max) tok[n++] = s; while (*s && *s != ' ') s++; } return n; } static unsigned char isop(char *t, char c) { return t[0] == c && t[1] == 0; } static unsigned char iscmd(char *t, char a, char b) { return t[0] == a && t[1] == b && t[2] == 0; } /* keep the prompt's path string roughly in sync with chdir(). */ static void set_cwd(char *cwd, char *arg) { char *d, *s, *last; if (arg[0] == '/') { /* absolute */ d = cwd; s = arg; while (*s) *d++ = *s++; *d = 0; } else if (arg[0] == '.' && arg[1] == '.' && arg[2] == 0) { last = cwd; d = cwd; while (*d) { if (*d == '/') last = d; d++; } if (last == cwd) { cwd[0] = '/'; cwd[1] = 0; } else *last = 0; } else if (arg[0] == '.' && arg[1] == 0) { /* stay */ } else { /* relative: append */ d = cwd; while (*d) d++; if (!(cwd[0] == '/' && cwd[1] == 0)) *d++ = '/'; s = arg; while (*s) *d++ = *s++; *d = 0; } } /* build the child command line "name\0arg1 arg2\0" at 0xA000 */ static void set_cmdline(char **tok, unsigned char start, unsigned char end) { char *p = ARGV0, *t; unsigned char i; t = tok[start]; while (*t) *p++ = *t++; *p++ = 0; /* NUL after the command word */ for (i = start + 1; i < end; i++) { if (i > start + 1) *p++ = ' '; t = tok[i]; while (*t) *p++ = *t++; } *p = 0; } /* run tok[start..end): parse its own >/< redirects, honor forced in/out. If bg, launch in the background (don't wait; leave fds to the child). */ static void run(char **tok, unsigned char start, unsigned char end, unsigned char infd, unsigned char outfd, unsigned char bg) { unsigned char i, cmdend = end, myin = NOFD, myout = NOFD, id, pid; for (i = start; i < end; i++) { if (isop(tok[i], '>')) { if (i + 1 < end) myout = open(tok[i + 1], O_WRITE); if (i < cmdend) cmdend = i; } else if (isop(tok[i], '<')) { if (i + 1 < end) myin = open(tok[i + 1], O_READ); if (i < cmdend) cmdend = i; } } id = lookup(tok[start]); if (id == NOFD) { puts(tok[start]); puts(": not found"); nl(); goto done; } set_cmdline(tok, start, cmdend); pid = fork(); if (pid == 0) { if (myin != NOFD) setin(myin); else if (infd != NOFD) setin(infd); if (myout != NOFD) setout(myout); else if (outfd != NOFD) setout(outfd); exec(id); } if (bg) { putc('['); putu(pid); puts("]"); nl(); return; } /* background */ /* wait for THIS child; report any background jobs that finish meanwhile */ for (;;) { unsigned char r = wait(); if (r == pid || r == NOFD) break; putc('['); putu(r); puts(" done]"); nl(); } done: if (myin != NOFD) close(myin); if (myout != NOFD) close(myout); } /* run a multi-stage pipeline (a | b | c): wire stages together with real kernel pipes, launch them ALL concurrently, then wait for them. */ static void run_pipeline(char **tok, unsigned char nt) { unsigned char pids[8], npid = 0; unsigned char cfd[8], ncfd = 0; /* file-redirect fds; closed after all exit */ unsigned char start = 0, prev_r = NOFD; for (;;) { unsigned char end = start, last, i, cmdend, fin = NOFD, fout = NOFD; unsigned char my_out = NOFD, next_r = NOFD, id, pid; while (end < nt && !isop(tok[end], '|')) end++; last = (end >= nt); if (!last) { /* pipe from this stage to the next */ unsigned char pr = pipe(); if (pr != NOFD) { next_r = pr; my_out = pr + 1; } } cmdend = end; /* this stage's own >/< redirects */ for (i = start; i < end; i++) { if (isop(tok[i], '>')) { if (i + 1 < end) fout = open(tok[i+1], O_WRITE); if (i < cmdend) cmdend = i; } else if (isop(tok[i], '<')) { if (i + 1 < end) fin = open(tok[i+1], O_READ); if (i < cmdend) cmdend = i; } } id = lookup(tok[start]); if (id == NOFD) { puts(tok[start]); puts(": not found"); nl(); if (prev_r != NOFD) close(prev_r); /* orphaned pipe ends -> EOF/EPIPE */ if (my_out != NOFD) close(my_out); if (fin != NOFD) close(fin); if (fout != NOFD) close(fout); } else { set_cmdline(tok, start, cmdend); pid = fork(); if (pid == 0) { if (fin != NOFD) setin(fin); else if (prev_r != NOFD) setin(prev_r); if (fout != NOFD) setout(fout); else if (my_out != NOFD) setout(my_out); exec(id); } if (npid < 8) pids[npid++] = pid; if (fin != NOFD && ncfd < 8) cfd[ncfd++] = fin; if (fout != NOFD && ncfd < 8) cfd[ncfd++] = fout; } prev_r = next_r; if (last) break; start = end + 1; } { unsigned char got = 0; /* wait for all stages */ while (got < npid) { unsigned char r = wait(), j, mine = 0; if (r == NOFD) break; for (j = 0; j < npid; j++) if (pids[j] == r) { mine = 1; break; } if (mine) got++; else { putc('['); putu(r); puts(" done]"); nl(); } } } { unsigned char k; for (k = 0; k < ncfd; k++) close(cfd[k]); } } void main(void) { char line[80]; char *tok[16]; char cwd[40]; cwd[0] = '/'; cwd[1] = 0; /* bring up networking first: run the DHCP client and wait for it to finish (got a lease, or gave up). Only then drop to the prompt. */ { unsigned char d = lookup("dhcp"); if (d != NOFD) { if (fork() == 0) exec(d); else wait(); } } for (;;) { unsigned char nt, i, pipepos, c, bg; /* reap finished background jobs */ { unsigned char p; while ((p = reap())) { putc('['); putu(p); puts(" done]"); nl(); } } puts(cwd); puts("# "); /* read a line (echoing), stop at newline; exit on EOF */ i = 0; for (;;) { c = readc(); if (c == EOF) poweroff(); if (c == '\r' || c == '\n') { nl(); break; } if (c == 8 || c == 127) { /* backspace: drop last char */ if (i > 0) { i--; putc(8); } continue; } if (i < 79) { line[i++] = c; putc(c); } } line[i] = 0; nt = tokenize(line, tok, 16); if (nt == 0) continue; if (iscmd(tok[0], 'c', 'd')) { /* cd builtin */ char *arg = (nt >= 2) ? tok[1] : "/"; if (chdir(arg) == 0) set_cwd(cwd, arg); else { puts("cd: no such directory"); nl(); } continue; } if ((tok[0][0] == 'e' && tok[0][1] == 'x') || (tok[0][0] == 'q' && tok[0][1] == 'u')) poweroff(); bg = 0; if (isop(tok[nt - 1], '&')) { bg = 1; nt--; if (nt == 0) continue; } pipepos = 0; for (i = 1; i < nt; i++) if (isop(tok[i], '|')) { pipepos = i; break; } if (pipepos) run_pipeline(tok, nt); else run(tok, 0, nt, NOFD, NOFD, bg); } }