Compare commits
10 Commits
da0032b531
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ef3ef0950f | |||
| 78bdf578bf | |||
| cd3f296fc5 | |||
| ea527a2065 | |||
| d921a2b144 | |||
| 31d7c63114 | |||
| e575f1b3d3 | |||
| e55136e433 | |||
| 9b31e4081d | |||
| 017983d672 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/bulb
|
||||||
31
Makefile
Normal file
31
Makefile
Normal 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
|
||||||
@@ -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;
|
||||||
|
|||||||
17
doc/bulb.1
17
doc/bulb.1
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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)?;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user