diff options
| author | user <user@clank> | 2026-09-24 22:45:04 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-24 22:45:04 +0200 |
| commit | 4cd933009dd6ce87fe710acfca6b1f786d51040c (patch) | |
| tree | 67e50c2ab347ddc2ac3e5b6a5cf5a4cfc44cc8d0 /usr/cut.c | |
| parent | fs: mount the builtins at /bin (diff) | |
| download | gbos-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.c | 89 |
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); +} |
