commit 2c66e68df793d05c857b32e3e0bd005ae2a00eeb
parent d4fdb88ba11e5b638eeb0be9faf177fa3c747447
Author: thing1 <thing1@seacrossedlovers.xyz>
Date: Mon, 23 Feb 2026 21:55:53 +0000
fixedcat and added uniq
Diffstat:
4 files changed, 102 insertions(+), 12 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,7 +7,7 @@ DESTDIR=
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
-all: bin/ls bin/rainbow bin/cat
+all: bin/ls bin/rainbow bin/cat bin/uniq
clean:
rm -rf bin/*
@@ -25,3 +25,5 @@ bin/cat: cmd/cat.ha
+bin/uniq: cmd/uniq.ha
+ $(HARE) build $(HAREFLAGS) -o $@ cmd/uniq.ha
diff --git a/cmd/cat.ha b/cmd/cat.ha
@@ -6,20 +6,26 @@ use fs;
use util;
-export fn main() void = {
+fn print(h: io::handle) void = {
let b: []u8 = [0];
- for (let f .. os::args[1..]) {
- let file = match(os::open(f)) {
- case let f: io::file => yield f;
- case let e: fs::error => util::die(fs::strerror(e), f);
+ for (let i = 0z; true; i += 1) {
+ match (io::read(h, b)) {
+ case size => fmt::print(b[0]: rune)!;
+ case => break;
};
+ };
+};
- for (let i = 0z; true; i += 1) {
- match (io::read(file, b)) {
- case size => fmt::print(b[0]: rune)!;
- case => break;
- };
- };
+export fn main() void = {
+ for (let f .. os::args[1..]) {
+ io::close(match(os::open(f)) {
+ case let f: io::file => print(f); yield f;
+ case let e: fs::error => util::die(fs::strerror(e), f);
+ }
+ )!;
};
+
+ if (len(os::args) == 1)
+ print(os::stdin);
};
diff --git a/cmd/uniq.ha b/cmd/uniq.ha
@@ -0,0 +1,63 @@
+use sort;
+use sort::cmp;
+use fmt;
+use io;
+use os;
+use strings;
+use bufio;
+use encoding::utf8;
+use getopt;
+
+use util;
+
+let delim = "\n";
+let newline = true;
+
+fn run() void = {
+ let sc = bufio::newscanner(os::stdin);
+ defer bufio::finish(&sc);
+
+ let lines: []str = [];
+ defer free(lines);
+
+ for (true) {
+ match (bufio::scan_line(&sc)) {
+ case let s: str => append(lines, s)!;
+ case io::EOF => break;
+ case let e: utf8::invalid => util::die(utf8::strerror(e));
+ case let e: io::error => util::die(io::strerror(e));
+ };
+ };
+
+ sort::sort(lines, size(str), &cmp::strs)!;
+
+ let prev = "";
+ for (let i = 0z; i < len(lines); i += 1) {
+ if (lines[i] == prev) continue
+ else fmt::printf("{}{}", lines[i], if (i != len(lines) - 1) delim else "")!;
+ prev = lines[i];
+ };
+};
+
+export fn main() void = {
+ let cmd = getopt::parse(os::args,
+ "unique",
+ ('d', "delim", "set a deliminer between printed tokens"),
+ ('n', "print a newline on exit (default)"),
+ ('N', "don't print a newline on exit")
+ );
+ defer getopt::finish(&cmd);
+
+ for (let opt .. cmd.opts) {
+ switch (opt.0) {
+ case 'd' => delim = util::escape(opt.1);
+ case 'n' => newline = true;
+ case 'N' => newline = false;
+ case => abort();
+ };
+ };
+
+ run();
+
+ if (newline) fmt::println()!;
+};
diff --git a/util/escape.ha b/util/escape.ha
@@ -0,0 +1,19 @@
+use strings;
+
+export fn escape(s: str) str = {
+ let bytes = strings::toutf8(s);
+ if (len(bytes) <= 1) return s;
+ if (bytes[0]: rune != '\\')
+ return s;
+
+
+ switch (bytes[1]: rune) {
+ case 'n' => return "\n";
+ case 't' => return "\t";
+ case 'v' => return "\v";
+ case 'r' => return "\r";
+ case '0' => return "\0";
+ case '\\' => return "\\";
+ case => return s;
+ };
+};