diff options
| author | user <user@clank> | 2026-09-22 16:02:55 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-22 16:02:55 +0200 |
| commit | 43e43b6dcec29e2b008d20135f5d02523b62fbcf (patch) | |
| tree | d647acd07596e290c70a9884c1dc5d8c20a31067 /usr/more.c | |
| parent | net: three TCP receive bugs - caller corruption, data loss, early EOF (diff) | |
| download | gbos-43e43b6dcec29e2b008d20135f5d02523b62fbcf.tar.gz gbos-43e43b6dcec29e2b008d20135f5d02523b62fbcf.tar.xz gbos-43e43b6dcec29e2b008d20135f5d02523b62fbcf.zip | |
usr: grep, more, cp, nc - and make pollcon pump the link
The shell has had pipes for a while with nothing to pipe *through*, and
an 18-row screen with no way to stop output scrolling off it.
- grep [-vin] PAT [file]: substring match (no regex), stdin or a file,
grep(1)'s exit convention. The other end of the pipe, at last.
- more [file]: pages 17 lines at a time, --More-- bar in white-on-blue,
any key pages, q quits. Counts *screen* rows, so wrapped lines pay
their real cost. Keys come from pollin/pollcon, not stdin, so
`cat big | more` still has a keyboard.
- cp SRC DST: the fs has rm/mkdir but no way to duplicate a file. mv is
this plus rm (there is no rename). Rejects SRC == DST, which would
otherwise truncate the source via O_WRITE before reading a byte.
- nc [-s] HOST PORT: raw TCP. Interactive (START sends the line with a
CRLF, /q quits) or -s to pump stdin and print the reply with its own
wall-clock idle timeout, because the kernel's blocking recv counts
pumps, not seconds. Generalizes what chat/irc hardcode:
`nc towel.blinkenlights.nl 23` works.
sys_pollcon now pumps the link before draining the console ring. The
ring is filled *by* net_pump, so a program that never touches the
network polled a ring nothing would ever fill: host-injected keys
(gbtype/gbdemo) hung `more` forever, and the Game Boy stopped answering
pings for as long as it ran. KGetc already pumps for this reason.
New tools take banks 35-38. Locals over statics in all four: a program's
_DATA starts at $A000, where the shell leaves the command line.
Diffstat (limited to 'usr/more.c')
| -rw-r--r-- | usr/more.c | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/usr/more.c b/usr/more.c new file mode 100644 index 0000000..6f6afa7 --- /dev/null +++ b/usr/more.c @@ -0,0 +1,83 @@ +#include "gbos.h" +/* more [file]: page a file (or stdin) one screenful at a time. + * + * The terminal is 40x18, so anything longer than a screen scrolls off before + * you can read it (try `ls` on a full disk, or `cat` on a wget dump). This + * prints 17 lines, then paints a --More-- bar and waits for a keypress: + * SELECT opens the on-screen keyboard, any key pages forward, 'q' quits. + * + * Keys come from pollin()/pollcon(), NOT stdin - so `cat file | more` still + * has a keyboard even though its stdin is the pipe. (pollcon also means the + * host can drive it over the link with tools/gbtype.) */ + +#define PAGE 17 /* TROWS - 1: leave a row for the bar */ +#define TCOLS 40 + +static void sgr(unsigned char n) { /* emit ESC [ n m */ + putc(27); + putc('['); + putu(n); + putc('m'); +} + +/* show the bar, block until a key arrives; 0 = the user asked to quit */ +static unsigned char waitkey(void) { + unsigned char c; + sgr(44); + sgr(37); /* white on blue */ + puts("--More--"); + sgr(0); + for (;;) { + c = pollin(); /* on-screen keyboard */ + if (!c) c = pollcon(); /* link-injected console bytes */ + if (c) break; + yield(); /* cooperative: let other procs run */ + } + puts("\r \r"); /* wipe the bar, stay on the same row */ + return (c == 'q' || c == 'Q') ? 0 : 1; +} + +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + unsigned char fd = NOFD, rows = 0, col = 0; + int ch; + + if (argc > 0 && argv[0][0] != '-') { + fd = open(argv[0], O_READ); + if (fd == EISDIR) { + puts("more: is a directory"); + nl(); + sexit(2); + } + if (fd == NOFD) { + puts("more: no such file"); + nl(); + sexit(2); + } + } + + for (;;) { + if (fd == NOFD) { + char c = readc(); + ch = (c == EOF) ? -1 : c; + } else ch = fgetc(fd); + if (ch < 0) break; + putc((char)ch); + + /* count screen rows, not input lines: a 100-char line wraps to three */ + if (ch == '\n') { + rows++; + col = 0; + } else if (ch != '\r' && ++col == TCOLS) { + rows++; + col = 0; + } + + if (rows >= PAGE) { + if (!waitkey()) break; + rows = 0; + } + } + if (fd != NOFD) close(fd); +} |
