aboutsummaryrefslogtreecommitdiffstats
path: root/c/sh.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-07-18 00:38:38 +0200
committeruser <user@clank>2026-07-18 00:38:38 +0200
commitba2d7ae1d7b319645b10aaa31a7f664f9f80c25c (patch)
treee01e6c7c710a661defe2515ff9657f08cddcb283 /c/sh.c
parenttools: gbtype prints hub errors to stderr and exits 1 (diff)
downloadgbos-ba2d7ae1d7b319645b10aaa31a7f664f9f80c25c.tar.gz
gbos-ba2d7ae1d7b319645b10aaa31a7f664f9f80c25c.tar.xz
gbos-ba2d7ae1d7b319645b10aaa31a7f664f9f80c25c.zip
refactor: c/ -> usr/, compiled program blobs out of the source tree
Userland sources live in usr/ (fits the Unix theme better than 'c'). SDCC output .bin blobs land in build/usr/ with the other build artifacts instead of littering the source dir; programs.asm INCBINs them from there. Byte-identical ROM.
Diffstat (limited to 'c/sh.c')
-rw-r--r--c/sh.c200
1 files changed, 0 insertions, 200 deletions
diff --git a/c/sh.c b/c/sh.c
deleted file mode 100644
index 3178894..0000000
--- a/c/sh.c
+++ /dev/null
@@ -1,200 +0,0 @@
-#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);
- }
-}