Compare commits

..

10 Commits

Author SHA1 Message Date
ef3ef0950f Fix manpage install location 2024-10-11 17:38:07 -04:00
78bdf578bf Manpage fixes 2024-10-11 17:29:50 -04:00
cd3f296fc5 More compatibility fixes 2024-10-11 17:16:33 -04:00
ea527a2065 Don't use for each loops
The hare compiler on OpenBSD doesn't support them :(
2024-10-11 17:08:16 -04:00
d921a2b144 Escape LF in board file, refuse to store or print control chars 2024-10-11 16:54:56 -04:00
31d7c63114 Add Makefile 2024-10-11 16:26:59 -04:00
e575f1b3d3 Add .gitignore 2024-10-11 16:26:52 -04:00
e55136e433 More man page tweaks 2024-10-11 16:26:30 -04:00
9b31e4081d Improve man page 2024-10-11 16:23:55 -04:00
017983d672 Mark the bulb module as internal 2024-10-11 16:23:37 -04:00
5 changed files with 107 additions and 19 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/bulb

31
Makefile Normal file
View File

@@ -0,0 +1,31 @@
.POSIX:
.SUFFIXES:
HARE=hare
HAREFLAGS=
DESTDIR=
PREFIX=/usr/local
BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/man
all: bulb
bulb:
$(HARE) build $(HAREFLAGS) -o $@ cmd/$@/
check:
$(HARE) test $(HAREFLAGS)
clean:
rm -f bulb
install:
mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1
install -Dm755 bulb $(DESTDIR)$(BINDIR)/bulb
install -Dm755 doc/bulb.1 $(DESTDIR)$(MANDIR)/man1
uninstall:
rm -f $(DESTDIR)$(BINDIR)/bulb
rm -f $(DESTDIR)$(MANDIR)/man1/bulb.1
.PHONY: all check clean install uninstall

View File

@@ -1,8 +1,8 @@
use bulb;
use errors; use errors;
use fmt; use fmt;
use fs; use fs;
use getopt; use getopt;
use internal::bulb;
use io; use io;
use os; use os;
use path; use path;
@@ -32,7 +32,8 @@ export fn main() void = {
let post = false; let post = false;
let anonymous = false; let anonymous = false;
for (let opt .. cmd.opts) { for (let index = 0z; index < len(cmd.opts); index += 1) {
let opt = cmd.opts[index];
switch (opt.0) { switch (opt.0) {
case 'b' => case 'b' =>
board = opt.1; board = opt.1;

View File

@@ -35,12 +35,23 @@ Posts a message read from the standard input. See the STANDARD INPUT section.
.IP \fB-u\fP .IP \fB-u\fP
Operates undercover. All posts made will be under the name \(lqanonymous\(rq. Operates undercover. All posts made will be under the name \(lqanonymous\(rq.
.\" .\"
.SH ENVIRONMENT
The BULBPATH environment variable specifies a colon-separated list of
directories (much like PATH) in which to search for boards. Directories at the
start take precedence over directories at the end. It has a value of
.I /var/bulb
by default.
.SH FILES
.I /var/bulb/*
.\"
.SH STANDARD INPUT .SH STANDARD INPUT
When the When the
.B -p .B -p
option is supplied, the standard input will be read until EOF. It will be posted option is supplied, the standard input will be read until EOF. The resulting
under the name of the current user in whichever board the user selected. text will be posted under the name of the current user in whichever board is
selected.
.\" .\"
.SH STANDARD OUTPUT .SH STANDARD OUTPUT
@@ -71,7 +82,7 @@ that the receiving user(s) be both logged in and monitoring their terminal for
any messages to ever get through. The any messages to ever get through. The
.BR bulb (1) .BR bulb (1)
command was written to support asynchronous, persistent conversations between command was written to support asynchronous, persistent conversations between
users of the same system. It is also possible to create bulletins, MOTDs, etc. users of the same system.
.\" .\"
.SH AUTHOR .SH AUTHOR

View File

@@ -1,3 +1,4 @@
use ascii;
use bufio; use bufio;
use fs; use fs;
use fmt; use fmt;
@@ -5,6 +6,18 @@ use io;
use os; use os;
use path; use path;
use strings; use strings;
use types;
// TODO
// two issues:
// A: posts are stored exactly as they are formatted
// B: posts need to be sanitized from control chars going in and going out
// C: specifying a number of posts to read only works with lines because it
// cant separate out the individual posts
//
// all of these can be fixed by escaping line breaks as the post is written to
// the board, and then rendering them once they get to the reading stage.
// sanitize the text at both stages.
let bulb_path: []str = []; let bulb_path: []str = [];
@init fn bulb_path() void = bulb_path = strings::split(os::tryenv("BULBPATH", "/var/bulb"), ":"); @init fn bulb_path() void = bulb_path = strings::split(os::tryenv("BULBPATH", "/var/bulb"), ":");
@@ -29,7 +42,37 @@ export fn read(output: io::handle, board: str, number: int) (void | error) = {
// we add 1 to account for the blank line that will always be at the // we add 1 to account for the blank line that will always be at the
// bottom // bottom
seek_to_nth_last_line(file, number + 1)?; seek_to_nth_last_line(file, number + 1)?;
io::copy(output, file)?;
static let output_wbuf: [os::BUFSZ]u8 = [0...];
let output = bufio::init(output, [], output_wbuf);
let file = bufio::newscanner(file, types::SIZE_MAX);
defer bufio::finish(&file);
let saw_escape = false;
for (true) {
let byte = match (bufio::scan_byte(&file)?) {
case let byte: u8 => yield byte;
case io::EOF => break;
};
if (byte == '\x1b') {
saw_escape = true;
} else {
if (byte == '\n') {
if (saw_escape) {
fmt::fprint(&output, "\n ")?;
} else {
fmt::fprint(&output, "\n")?;
};
} else if (!ascii::iscntrl(byte: rune)) {
let fake_buffer: [1]u8 = [byte];
io::write(&output, fake_buffer)?;
};
saw_escape = false;
};
};
bufio::flush(&output)?;
}; };
export fn post(input: io::handle, board: str, user_name: (str | void)) (void | error) = { export fn post(input: io::handle, board: str, user_name: (str | void)) (void | error) = {
@@ -45,26 +88,25 @@ export fn post(input: io::handle, board: str, user_name: (str | void)) (void | e
static let file_wbuf: [os::BUFSZ]u8 = [0...]; static let file_wbuf: [os::BUFSZ]u8 = [0...];
let file = bufio::init(file, [], file_wbuf); let file = bufio::init(file, [], file_wbuf);
let scanner = bufio::newscanner(input); let input = bufio::newscanner(input, types::SIZE_MAX);
defer bufio::finish(&scanner); defer bufio::finish(&input);
let saw_break = false; let saw_break = false;
fmt::fprintf(&file, "{}: ", user)?; fmt::fprintf(&file, "{}: ", user)?;
for (true) { for (true) {
let byte = match (bufio::scan_byte(&scanner)?) { let byte = match (bufio::scan_byte(&input)?) {
case let byte: u8 => yield byte; case let byte: u8 => yield byte;
case io::EOF => break; case io::EOF => break;
}; };
if (saw_break) { if (saw_break) {
fmt::fprint(&file, "\n ")?; fmt::fprint(&file, "\x1b\n")?;
saw_break = false; saw_break = false;
}; };
switch (byte) { if (byte == '\n') {
case '\n' =>
saw_break = true; saw_break = true;
case => } else if (!ascii::iscntrl(byte: rune)) {
let fake_buffer: [1]u8 = [byte]; let fake_buffer: [1]u8 = [byte];
io::write(&file, fake_buffer)?; io::write(&file, fake_buffer)?;
}; };
@@ -75,14 +117,16 @@ export fn post(input: io::handle, board: str, user_name: (str | void)) (void | e
}; };
export fn list(output: io::handle) (void | error) = { export fn list(output: io::handle) (void | error) = {
for (let directory .. bulb_path) list_boards_in(output, directory)?; for (let index = 0z; index < len(bulb_path); index += 1) {
list_boards_in(output, bulb_path[index])?;
};
}; };
export fn look_up_board(board: str) (str | no_such_board | invalid_board) = { export fn look_up_board(board: str) (str | no_such_board | invalid_board) = {
validate_board_name(board)?; validate_board_name(board)?;
static let buf = path::buffer { ... }; static let buf = path::buffer { ... };
for (let directory .. bulb_path) { for (let index = 0z; index < len(bulb_path); index += 1) {
let location = path::set(&buf, directory, board)!; let location = path::set(&buf, bulb_path[index], board)!;
match (os::stat(location)) { match (os::stat(location)) {
case fs::filestat => return location; case fs::filestat => return location;
case => void; case => void;
@@ -109,8 +153,8 @@ export fn validate_board_name(board: str) (void | invalid_board) = {
fn list_boards_in(output: io:: handle, directory: str) (void | error) = { fn list_boards_in(output: io:: handle, directory: str) (void | error) = {
let entries = os::readdir(directory)?; let entries = os::readdir(directory)?;
defer fs::dirents_free(entries); defer fs::dirents_free(entries);
for (let entry .. entries) { for (let index = 0z; index < len(entries); index += 1) {
fmt::fprintln(output, entry.name)?; fmt::fprintln(output, entries[index].name)?;
}; };
}; };