commit f719a4a7d0ade946b6e1807afeea57012c44f224
Author: thing1 <thing1@seacrossedlovers.xyz>
Date: Mon, 22 Sep 2025 15:13:21 +0100
init commit
Diffstat:
5 files changed, 148 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1 @@
+./lav
diff --git a/Makefile b/Makefile
@@ -0,0 +1,31 @@
+.POSIX:
+.SUFFIXES:
+HARE=hare
+HAREFLAGS=
+
+DESTDIR=
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+
+HARE_SOURCES != find . -name '*.ha'
+
+all: lav
+
+lav: $(HARE_SOURCES)
+ $(HARE) build $(HAREFLAGS) -o $@ cmd/$@/
+
+check:
+ $(HARE) test $(HAREFLAGS)
+
+clean:
+ rm -f lav
+
+install:
+ install -Dm755 lav $(DESTDIR)$(BINDIR)/lav
+
+uninstall:
+ rm -f $(DESTDIR)$(BINDIR)/lav
+
+.PHONY: all check clean install uninstall
+
+
diff --git a/cmd/lav/lav.ha b/cmd/lav/lav.ha
@@ -0,0 +1,38 @@
+use fmt;
+use io;
+use os;
+use strings;
+use net;
+use net::dial;
+use net::uri;
+use encoding::utf8;
+
+use http;
+
+def url = "http://seacrossedlovers.xyz";
+
+export fn main() void = {
+ let u = match(uri::parse(url)) {
+ case let u: uri::uri => yield &u;
+ case uri::invalid => fmt::fatalf("failed to parse url: {}", url);
+ case => fmt::fatalf("Unknown erorr while parsing url");
+ };
+ defer uri::finish(u);
+ let sock = match (dial::dial_uri("tcp", u)) {
+ case let s: net::socket => yield s;
+ case let err: dial::error => fmt::fatalf("Couldn't connect to server: {}", dial::strerror(err));
+ };
+ defer io::close(sock)!;
+
+ let res = http::get(sock, "/")!;
+ let response = match(http::parseres(res)) {
+ case let r: http::response => yield r;
+ case => fmt::fatal("failed to parse response");
+ };
+ defer (http::freeres(&response));
+
+ fmt::printfln("{}", response.status)!;
+ for (let attribute .. response.attributes) {
+ fmt::printfln("{}: {}", attribute[0], attribute[1])!;
+ };
+};
diff --git a/http/http.ha b/http/http.ha
@@ -0,0 +1,78 @@
+use fmt;
+use net;
+use io;
+use strings;
+use strconv;
+use encoding::utf8;
+
+export type invalid = !void;
+
+export type response = struct {
+ status: int,
+ attributes: [][]str
+};
+
+export fn get(sock: io::handle, resource: str) (str | io::error | utf8::invalid) = {
+ let req = fmt::asprintf("GET {} HTTP/1.1\r\nUser-Agent: curl/8.6.0\r\n\r\n", resource)!;
+ defer free(req);
+ let utf8 = strings::toutf8(req);
+
+ match (io::write(sock, utf8)) {
+ case let e: io::error => return e;
+ case => yield;
+ };
+
+ let res: []u8 = match(io::drain(sock)) {
+ case let buf: []u8 => yield buf;
+ case let e: io::error => return e;
+ };
+
+ return strings::fromutf8(res);
+};
+
+export fn freeres(res: *response) void = {
+ for (let attribute .. res.attributes) {
+ strings::freeall(attribute);
+ };
+};
+
+export fn parseres(res: str) (response | invalid) = {
+ let header = strings::index(res, "\r\n\r\n") as size;
+ let lines = strings::split(strings::sub(res, 0, header), "\r\n")!;
+ defer free(lines);
+
+ let tok = &strings::tokenize(lines[0], " ");
+ match (strings::next_token(tok)) {
+ case done => return invalid;
+ case => yield;
+ };
+ let s = match(strings::next_token(tok)) {
+ case let s: str => yield match (strconv::stoi(s)) {
+ case let i: int => yield i;
+ case => return invalid;
+ };
+ };
+
+ let response = response {
+ status = s,
+ attributes = [],
+ };
+
+ let i = 0z;
+ for (let line .. lines[1..]) {
+ let split = match (strings::splitn(line, ": ", 2)) {
+ case let s: []str => yield s;
+ case nomem => fmt::fatal("Out of memory!");
+ };
+ if (len(split) >= 2) {
+ split[0] = strings::ltrim(split[0]);
+ split[1] = strings::ltrim(split[1]);
+ append(response.attributes, strings::dupall(split) as []str)!;
+ i += 1;
+ };
+
+ free(split);
+ };
+
+ return response;
+};
diff --git a/lav b/lav
Binary files differ.