aboutsummaryrefslogtreecommitdiffstats
path: root/usr/cut.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-24 22:45:04 +0200
committeruser <user@clank>2026-09-24 22:45:04 +0200
commit4cd933009dd6ce87fe710acfca6b1f786d51040c (patch)
tree67e50c2ab347ddc2ac3e5b6a5cf5a4cfc44cc8d0 /usr/cut.c
parentfs: mount the builtins at /bin (diff)
downloadgbos-4cd933009dd6ce87fe710acfca6b1f786d51040c.tar.gz
gbos-4cd933009dd6ce87fe710acfca6b1f786d51040c.tar.xz
gbos-4cd933009dd6ce87fe710acfca6b1f786d51040c.zip
usr: touch, tr, rev, cut, uniq
/# ls -l /bin > b /# cut -d \s -f 1 b | uniq -c 52 - - touch FILE...: gbfs has no timestamps (an MBC5 cart has no RTC), so touch does the half that means something here - make an empty file. O_APPEND is exactly right: it creates, and it does not truncate what is already there. - tr [-d] SET1 [SET2]: ranges (a-z), -d to delete, and a short SET2 padded with its last character, like tr(1). ASCII only - a 128-entry map. - rev, uniq [-c]: one line each, from a file or stdin like the other filters. Two things the tests caught. A bare "-" is a SET, not a flag, so `tr . -` has to skip flag-matching only when a letter follows the dash. And uniq compared an empty "line" against the last run at EOF and flushed a phantom blank one - a stray "1" in uniq -c output. tr and cut also take \s \t \n \r escapes, because the shell has no quoting: `-d ' '` arrives as three tokens, so a space or tab delimiter was simply untypeable. Shell quoting would be the better fix; this makes the tools usable today without touching the parser. Banks 49-53; 10 program slots left in the 1 MB ROM.
Diffstat (limited to 'usr/cut.c')
-rw-r--r--usr/cut.c89
1 files changed, 89 insertions, 0 deletions
diff --git a/usr/cut.c b/usr/cut.c
new file mode 100644
index 0000000..18de461
--- /dev/null
+++ b/usr/cut.c
@@ -0,0 +1,89 @@
+#include "gbos.h"
+/* cut -f N [-d C] [file]: print field N of each line.
+ *
+ * The delimiter defaults to TAB, like cut(1); -d takes the first character of
+ * its argument, so `-d :` works - and since the shell has no quoting, `-d \\s`
+ * is how you say space (\\t tab, \\n newline). A line with no delimiter is passed through
+ * whole (what GNU cut does without -s), which is usually what you want when
+ * filtering mixed output on a 40-column screen. */
+
+#define MAXLINE 80
+
+/* The shell has no quoting, so a space or a tab cannot be typed as an
+ argument: "-d ' '" arrives as three tokens. Escapes are how you name them
+ here - \s space, \t tab, \n newline, \\ backslash. */
+static char unesc1(const char *s, unsigned char *used) {
+ *used = 1;
+ if (s[0] != '\\' || !s[1]) return s[0];
+ *used = 2;
+ switch (s[1]) {
+ case 's': return ' ';
+ case 't': return '\t';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ default: return s[1];
+ }
+}
+
+void main(void) {
+ char *argv[8];
+ char line[MAXLINE];
+ unsigned char argc = argv_parse(argv, 8);
+ char *fv = optval(argv, argc, 'f');
+ char *dv = optval(argv, argc, 'd');
+ unsigned char dused;
+ char delim = dv ? unesc1(dv, &dused) : '\t';
+ unsigned char want = fv ? atou(fv) : 1;
+ unsigned char len = 0, fd = NOFD, i, field, start, end;
+ char *fname = 0;
+ int ch;
+
+ if (want == 0) want = 1;
+ for (i = 0; i < argc; i++) {
+ if (argv[i][0] == '-') continue;
+ if (fv == argv[i] || dv == argv[i]) continue;
+ fname = argv[i];
+ break;
+ }
+ if (fname) {
+ fd = open(fname, O_READ);
+ if (fd == EISDIR) {
+ puts("cut: is a directory");
+ nl();
+ sexit(2);
+ }
+ if (fd == NOFD) {
+ puts("cut: no such file");
+ nl();
+ sexit(2);
+ }
+ }
+ for (;;) {
+ if (fd == NOFD) {
+ char c = readc();
+ ch = (c == EOF) ? -1 : c;
+ } else ch = fgetc(fd);
+ if (ch == '\r') continue;
+ if (ch >= 0 && ch != '\n') {
+ if (len < MAXLINE) line[len++] = (char)ch;
+ continue;
+ }
+ if (ch < 0 && len == 0) break;
+ /* walk to the wanted field */
+ field = 1;
+ start = 0;
+ for (i = 0; i <= len; i++) {
+ if (i == len || line[i] == delim) {
+ if (field == want) break;
+ field++;
+ start = (unsigned char)(i + 1);
+ }
+ }
+ end = i;
+ if (field == want && start <= len) writes(line + start, (unsigned char)(end - start));
+ nl();
+ len = 0;
+ if (ch < 0) break;
+ }
+ if (fd != NOFD) close(fd);
+}