lav

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit f719a4a7d0ade946b6e1807afeea57012c44f224
Author: thing1 <thing1@seacrossedlovers.xyz>
Date:   Mon, 22 Sep 2025 15:13:21 +0100

init commit

Diffstat:
A.gitignore | 1+
AMakefile | 31+++++++++++++++++++++++++++++++
Acmd/lav/lav.ha | 38++++++++++++++++++++++++++++++++++++++
Ahttp/http.ha | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alav | 0
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.