Program works now

This commit is contained in:
Sasha Koshka 2024-10-09 18:47:24 -04:00
parent 157890be44
commit 9cef615779
2 changed files with 106 additions and 31 deletions

View File

@ -2,7 +2,9 @@ use errors;
use fmt; use fmt;
use fs; use fs;
use getopt; use getopt;
use io;
use os; use os;
use path;
use strconv; use strconv;
use strings; use strings;
use unix; use unix;
@ -12,12 +14,11 @@ const default_board = "general";
let name = ""; let name = "";
@init fn name() void = name = os::args[0]; @init fn name() void = name = os::args[0];
let path: []str = []; let bulb_path: []str = [];
@init fn path() void = path = strings::split(os::tryenv("BULBPATH", "/var/bulb"), ":"); @init fn bulb_path() void = bulb_path = strings::split(os::tryenv("BULBPATH", "/var/bulb"), ":");
@fini fn path() void = free(path); @fini fn bulb_path() void = free(bulb_path);
export fn main() void = { export fn main() void = {
// parse args
const cmd = getopt::parse( const cmd = getopt::parse(
os::args, os::args,
"bulletin board", "bulletin board",
@ -39,7 +40,7 @@ export fn main() void = {
} else if (subcmd.0 == "read") { } else if (subcmd.0 == "read") {
match (subcmd_read(subcmd.1)) { match (subcmd_read(subcmd.1)) {
case errors::invalid => case errors::invalid =>
getopt::printhelp(cmd); getopt::printhelp(os::stdout, name, cmd.help)!;
os::exit(os::status::FAILURE); os::exit(os::status::FAILURE);
case void => void; case void => void;
}; };
@ -60,28 +61,15 @@ fn subcmd_post(cmd: *getopt::command) void = {
}; };
}; };
let location = match(look_up_board(board)) { let user_name = if (anonymous) void else get_user_name();
case let location: str => let message = strings::join(" ", cmd.args...);
yield location; defer free(message);
case void =>
fmt::errorf("{}: The board {} does not exist", name, board)!;
return;
};
let file = match(os::open(location, fs::flag::APPEND)) { match (post(board, user_name, message)) {
case let file: io::file => case let err: error =>
defer io::close(file); fmt::errorf("{}: Could not read {}: {}\n", name, board, strerror(err))!;
yield file; case void => void;
case let err: fs::error =>
fmt::errorf(
"{}: Can't make post: {}",
name, fs::strerror(err));
return;
}; };
let user =
fmt::fprintf(file, "{}: {}", user, message)!;
}; };
fn subcmd_read(cmd: *getopt::command) (void | errors::invalid) = { fn subcmd_read(cmd: *getopt::command) (void | errors::invalid) = {
@ -99,12 +87,16 @@ fn subcmd_read(cmd: *getopt::command) (void | errors::invalid) = {
case => abort(); case => abort();
}; };
}; };
// TODO match (read(board, number)) {
case let err: error =>
fmt::errorf("{}: Could not read {}: {}\n", name, board, strerror(err))!;
case void => void;
};
}; };
fn subcmd_ls(cmd: *getopt::command) void = { fn subcmd_ls(cmd: *getopt::command) void = {
for (let directory .. path) { for (let directory .. bulb_path) {
match (list_boards_in(directory)) { match (list_boards_in(directory)) {
case let err: fs::error => case let err: fs::error =>
fmt::errorf( fmt::errorf(
@ -125,6 +117,20 @@ fn list_boards_in(directory: str) (void | fs::error) = {
}; };
}; };
//fn look_up_board(board: str, path: []str) (str | void) { fn look_up_board(board: str) (str | no_such_board) = {
// TODO static let buf = path::buffer { ... };
//}; for (let directory .. bulb_path) {
let location = path::set(&buf, directory, board)!;
match (os::stat(location)) {
case fs::filestat => return location;
case => void;
};
};
return no_such_board;
};
fn get_user_name() str = {
// FIXME: could not find support for getting a username from a uid in
// hare. when it is added, use that here.
return os::tryenv("USER", "anonymous");
};

69
cmd/bulb/operation.ha Normal file
View File

@ -0,0 +1,69 @@
use fs;
use fmt;
use io;
use os;
type no_such_board = !void;
type error = (io::error | fs::error | no_such_board);
fn strerror(err: error) str = match(err) {
case no_such_board => return "No such board";
case let err: fs::error => return fs::strerror(err);
case let err: io::error => return io::strerror(err);
};
fn read(board: str, number: int) (void | error) = {
let location = look_up_board(board)?;
let file = os::open(location)?;
defer io::close(file)!;
seek_to_nth_last_line(file, number)?;
io::copy(os::stdout, file)?;
};
fn post(board: str, user_name: (str | void), message: str) (void | error) = {
let location = look_up_board(board)?;
let file = os::open(location, fs::flag::APPEND | fs::flag::WRONLY)?;
defer io::close(file)!;
let user = match(user_name) {
case let user: str => yield(user);
case void => yield "anonymous";
};
fmt::fprintf(file, "{}: {}\n", user, message)?;
};
fn seek_to_nth_last_line(file: io::handle, number: int) (void | io::error) = {
let buffer: [256]u8 = [0...];
let end = io::seek(file, 0, io::whence::END)?;
let step = len(buffer): io::off;
let current = end;
for (true) {
// read a chunk in reverse, or stop if there is nothing left
let buffer = buffer[..];
if (current - step > 0) {
current -= step;
} else {
buffer = buffer[..current];
current = 0;
};
io::seek(file, current, io::whence::SET)?;
let length = match (io::read(file, buffer)?) {
case let length: size => yield length;
case io::EOF => break;
};
io::seek(file, current, io::whence::SET)?;
// look for line breaks in that chunk
let buffer = buffer[..length];
for (let index = len(buffer): int - 1; index >= 0; index -= 1) {
if (buffer[index: size] == '\n') number -= 1;
if (number <= 0) {
io::seek(file, current + index: io::off, io::whence::SET)?;
return void;
};
};
};
return void;
};