From 51b9afea71fa527b5b87657cb567d570c7bae393 Mon Sep 17 00:00:00 2001 From: Julian Hurst Date: Wed, 23 Aug 2023 22:35:14 +0200 Subject: Initial commit --- sync.ha | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 sync.ha diff --git a/sync.ha b/sync.ha new file mode 100644 index 0000000..de35dc5 --- /dev/null +++ b/sync.ha @@ -0,0 +1,195 @@ +use fmt; +use log; +use net; +use net::tcp; +use net::ip; +use types; +use bufio; +use shlex; +use io; +use unix::poll; +use unix::signal; +use encoding::utf8; +use strings; +use regex; +use strconv; +use fs; +use os; +use strings; + +type nofreefd = !void; +type invalidquery = !str; +type quit = void; +type saveposerr = !(fs::error | io::error); +type error = !(invalidquery | shlex::syntaxerr | utf8::invalid | strconv::overflow | strconv::invalid | saveposerr); + +fn savepos(filename: str, position: str) (void | saveposerr) = { + let f = os::open(filename, fs::flag::WRONLY, fs::flag::CREATE, fs::flag::TRUNC)?; + defer io::close(f)!; + os::chmod(filename, fs::mode::USER_RW | fs::mode::GROUP_R | fs::mode::OTHER_R)?; + fmt::fprint(f, position)?; +}; + +fn handlequery(query: const []u8) (void | []u8 | quit | error) = { + log::printfln("query: {}", strings::fromutf8_unsafe(query)); + let s = strings::fromutf8(query)?; + let spl = shlex::split(s)?; + defer strings::freeall(spl); + if (len(spl) == 0) { + return "Empty query": invalidquery; + }; + switch (spl[0]) { + case "save" => + if (len(spl) != 3) { + return "save takes 2 arguments (filename, position)": invalidquery; + }; + //let re = regex::compile("[0-9]{2}:[0-9]{2}:[0-9]{2}")?; + //defer regex::finish(&re); + //const res = regex::replace(&re, spl[2], "")?; + //if (res != "") { + // return "save position format: hh:mm:ss": invalidquery; + //}; + //let re2 = regex::compile("[0-9]{2}")?; + //defer regex::finish(&re2); + //const result = regex::findall(&re2, spl[2]); + //if (len(result) != 3) { + // return "save position format: hh:mm:ss": invalidquery; + //}; + //if (strconv::stou(result[1][0].content)? >= 60 || strconv::stou(result[2][0].content)? >= 60) { + // return "save position format: hh:mm:ss (mm < 60 && ss < 60)": invalidquery; + //}; + //log::printfln("Saving {} with position {}", spl[1], spl[2]); + savepos(spl[1], spl[2])?; + case "get" => + if (len(spl) != 2) { + return "get takes 1 argument (filename)": invalidquery; + }; + let f = os::open(spl[1])?; + let buf = io::drain(f)?; + log::printfln("sending buf: {}", strings::fromutf8_unsafe(buf)); + return buf; + case "quit" => + return quit; + case => + return "Unknown query": invalidquery; + }; +}; + +fn logerror(err: (void | error)) void = { + match (err) { + case void => + return; + case let e: invalidquery => + log::println(e); + case shlex::syntaxerr => + log::println("Syntax error"); + case utf8::invalid => + log::println("Invalid utf8"); + case let e: strconv::error => + log::println(strconv::strerror(e)); + case let e: fs::error => + log::println(fs::strerror(e)); + case let e: io::error => + log::println(io::strerror(e)); + }; +}; + +fn close(pollfds: []poll::pollfd) void = { + for (let i = 2z; i < len(pollfds); i += 1) { + if (pollfds[i].fd >= 0) { + net::close(pollfds[i].fd)!; + }; + }; + //if (pollfds[0].fd >= 0) net::close(pollfds[0].fd)!; +}; + +fn nextfreefd(fds: []poll::pollfd) (size | nofreefd) = { + for (let i = 0z; i < len(fds); i += 1) { + if (fds[i].fd < 0) { + return i; + }; + }; + return nofreefd; +}; + +export fn main() void = { + let addr = ip::parse("127.0.0.1")!; + let sock = tcp::listen(addr, 6789u16)!; + defer net::close(sock)!; + + signal::block(signal::sig::INT); + let sfd = signal::signalfd(signal::sig::INT)!; + + // Use array, growable slice doesn't work with poll (new accept socket + // fds overwrite old accept socket fds for some reason) + let pollfds: [200]poll::pollfd = [ + poll::pollfd { + fd = sock, + events = poll::event::POLLIN, + ... + }, + poll::pollfd { + fd = sfd, + events = poll::event::POLLIN, + ... + }, poll::pollfd{fd = -1, ...}... + ]; + + defer close(pollfds); + + for (true) { + let i = poll::poll(pollfds, poll::INDEF)!; + if (i > 0) { + if (pollfds[0].revents & poll::event::POLLIN != 0) { + let s= tcp::accept(sock)!; + const fd = nextfreefd(pollfds)!; + pollfds[fd] = poll::pollfd { + fd = s, + events = poll::event::POLLIN, + ... + }; + }; + if (pollfds[1].revents & poll::event::POLLIN != 0) { + signal::read(pollfds[1].fd)!; + break; + }; + for (let j = 2z; j < len(pollfds); j += 1) { + if (pollfds[j].revents & poll::event::POLLIN != 0) { + let s = pollfds[j].fd; + log::printfln("client at index {} with fd {}", j, s: int); + let line = match (bufio::scanline(s)) { + case let line: []u8 => + yield line; + case io::EOF => + log::printfln("Closing connection to client at index {} with fd {}", j, s: int); + net::close(s)!; + pollfds[j].fd = -1; + break; + case let e: io::error => + log::println(io::strerror(e)); + break; + }; + defer free(line); + match (handlequery(line)) { + case let e: error => + logerror(e); + case quit => + log::printfln("Closing connection to client at index {} with fd {}", j, s: int); + net::close(s)!; + pollfds[j].fd = -1; + case void => + yield; + case let buf: []u8 => + defer free(buf); + match (fmt::fprintfln(s, "{}", strings::fromutf8_unsafe(buf))) { + case let e: io::error => + log::println(io::strerror(e)); + case size => + yield; + }; + }; + }; + }; + }; + }; +}; -- cgit v1.2.3