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; }; }; }; }; }; }; };