aboutsummaryrefslogtreecommitdiffstats
path: root/usr/cp.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/cp.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 '')
-rw-r--r--usr/cp.c62
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 */
+}