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/cp.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/cp.c')
| -rw-r--r-- | usr/cp.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/usr/cp.c b/usr/cp.c new file mode 100644 index 0000000..61a1e0b --- /dev/null +++ b/usr/cp.c @@ -0,0 +1,62 @@ +#include "gbos.h" +/* cp SRC DST: copy a file. There is no rename syscall, so `mv` is this plus + * rm - and O_WRITE truncates (see fs.asm sys_open .trunc), which is exactly + * why the SRC == DST case below has to be rejected before opening DST. */ + +static unsigned char streq(const char *a, const char *b) { + while (*a && *a == *b) { + a++; + b++; + } + return (unsigned char)(*a == *b); +} + +void main(void) { + char *argv[4]; + unsigned char argc = argv_parse(argv, 4); + unsigned char in, out; + int ch; + + if (argc < 2) { + puts("usage: cp SRC DST"); + nl(); + sexit(2); + } + if (streq(argv[0], argv[1])) { + puts("cp: SRC and DST are the same file"); + nl(); + sexit(2); + } + + in = open(argv[0], O_READ); + if (in == EISDIR) { + puts("cp: SRC is a directory"); + nl(); + sexit(2); + } + if (in == NOFD) { + puts("cp: no such file: "); + puts(argv[0]); + nl(); + sexit(2); + } + + out = open(argv[1], O_WRITE); /* creates, or truncates an existing file */ + if (out == EISDIR) { + puts("cp: DST is a directory"); + nl(); + close(in); + sexit(2); + } + if (out == NOFD) { + puts("cp: cannot create "); + puts(argv[1]); + nl(); + close(in); + sexit(2); /* out of inodes/blocks, or a bad path */ + } + + while ((ch = fgetc(in)) >= 0) fputc(out, (char)ch); + close(in); + close(out); /* flushes the inode */ +} |
