aboutsummaryrefslogtreecommitdiffstats
path: root/usr/more.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-22 16:02:55 +0200
committeruser <user@clank>2026-09-22 16:02:55 +0200
commit43e43b6dcec29e2b008d20135f5d02523b62fbcf (patch)
treed647acd07596e290c70a9884c1dc5d8c20a31067 /usr/more.c
parentnet: three TCP receive bugs - caller corruption, data loss, early EOF (diff)
downloadgbos-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.c83
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);
+}