optimizations #161

Open
emma wants to merge 44 commits from optimizations into main
17 changed files with 289 additions and 223 deletions

View File

@ -95,7 +95,7 @@ build/o/libstrerror.rlib: build src/libstrerror.rs
src/libstrerror.rs src/libstrerror.rs
build/o/libsysexits.rlib: build/include/sysexits.h build/o/libsysexits.rlib: build/include/sysexits.h
bindgen --default-macro-constant-type signed --use-core --formatter=none \ bindgen --fit-macro-constant-types --default-macro-constant-type unsigned --use-core --formatter=none \
build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ - build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
# bandage solution until bindgen(1) gets stdin support # bandage solution until bindgen(1) gets stdin support

View File

@ -13,7 +13,7 @@
int main(void) { int main(void) {
#ifdef __OpenBSD__ #ifdef __OpenBSD__
pledge(NULL, NULL); (void)pledge("stdio", "");
#endif #endif
return 1; return 1;
} }

View File

@ -18,8 +18,8 @@
use std::{ use std::{
env::args, env::args,
io::{ Read, stdin, stdout, Write }, io::{ Error, Read, Write, stdin, stdout },
process::{ Command, exit, Stdio }, process::{ Command, ExitCode, Stdio, exit },
}; };
extern crate getopt; extern crate getopt;
@ -32,25 +32,36 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd; #[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; #[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
fn main() { fn err(argv0: &String, e: Error) {
eprintln!("{}: {}", argv0, e.strerror());
}
fn usage(argv0: &String) -> u8 {
eprintln!("Usage: {} [-d delimiter] index command [args...]", argv0);
EX_USAGE
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
let mut d = '\u{1E}'.to_string(); /* ASCII record separator */ let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut optind = 1; let mut optind = 1;
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio proc exec"); let promises = Promises::new("exec proc stdio unveil");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror()); err(&argv[0], e);
exit(EX_OSERR); return ExitCode::from(EX_OSERR);
}
if let Err(e) = unveil(None, None) {
err(&argv[0], e);
return ExitCode::from(EX_OSERR);
} }
} }
let usage = format!( if argv.len() == 1 { return ExitCode::from(usage(&argv[0])); }
"Usage: {} [-d delimiter] index command [args...]",
argv[0],
);
while let Some(opt) = argv.getopt("d:") { while let Some(opt) = argv.getopt("d:") {
match opt.opt() { match opt.opt() {
@ -60,8 +71,7 @@ fn main() {
optind = opt.ind(); optind = opt.ind();
}, },
_ => { _ => {
eprintln!("{}", usage); return ExitCode::from(usage(&argv[0]));
exit(EX_USAGE);
} }
}; };
} }
@ -69,7 +79,7 @@ fn main() {
/* parse the specified index as a number we can use */ /* parse the specified index as a number we can use */
let index = argv[optind].parse::<usize>().unwrap_or_else(|e| { let index = argv[optind].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[1], e); eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR); exit(EX_DATAERR.into());
}); });
/* index of the argv[0] for the operator command */ /* index of the argv[0] for the operator command */
@ -77,15 +87,14 @@ fn main() {
/* argv[0] of the operator command */ /* argv[0] of the operator command */
let operator = argv.get(command_arg).unwrap_or_else(|| { let operator = argv.get(command_arg).unwrap_or_else(|| {
eprintln!("{}", usage); exit(usage(&argv[0]).into());
exit(EX_USAGE);
}); });
/* read entire standard input into memory */ /* read entire standard input into memory */
let mut buf = String::new(); let mut buf = String::new();
if let Err(e) = stdin().read_to_string(&mut buf) { if let Err(e) = stdin().read_to_string(&mut buf) {
eprintln!("{}: {}", argv[0], e.strerror()); err(&argv[0], e);
exit(EX_IOERR); exit(EX_IOERR.into());
}; };
/* split the buffer by the delimiter (by default, '\u{1E}') */ /* split the buffer by the delimiter (by default, '\u{1E}') */
@ -105,18 +114,14 @@ fn main() {
.stdout(Stdio::piped()) /* piped stdout to handle output ourselves */ .stdout(Stdio::piped()) /* piped stdout to handle output ourselves */
.spawn() .spawn()
.unwrap_or_else( |e| { .unwrap_or_else( |e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); err(&argv[0], e);
exit(EX_UNAVAILABLE); exit(EX_UNAVAILABLE.into());
}); });
/* get field we want to pipe into spawned program */ /* get field we want to pipe into spawned program */
let field = fields.get(index).unwrap_or_else(|| { let field = fields.get(index).unwrap_or_else(|| {
eprintln!( eprintln!("{}: {}: no such index in input", argv[0], index);
"{}: {}: No such index in input", exit(EX_DATAERR.into());
argv[0],
index.to_string(),
);
exit(EX_DATAERR);
}); });
/* get the stdin of the newly spawned program and feed it the field val */ /* get the stdin of the newly spawned program and feed it the field val */
@ -126,8 +131,8 @@ fn main() {
} }
let output = spawned.wait_with_output().unwrap_or_else(|e| { let output = spawned.wait_with_output().unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); err(&argv[0], e);
exit(EX_IOERR); exit(EX_IOERR.into());
}); });
/* get the output with which the original field will be replaced */ /* get the output with which the original field will be replaced */
@ -143,8 +148,8 @@ fn main() {
/* convert the output of the program to UTF-8 */ /* convert the output of the program to UTF-8 */
let new_field = String::from_utf8(replace).unwrap_or_else(|e| { let new_field = String::from_utf8(replace).unwrap_or_else(|e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e); eprintln!("{}: {}", argv[0], e);
exit(EX_IOERR); exit(EX_IOERR.into());
}); });
/* store the new field in the old fields vector */ /* store the new field in the old fields vector */
@ -154,7 +159,9 @@ fn main() {
stdout().write_all( stdout().write_all(
fields.join(&d.to_string()).as_bytes() fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e| { ).unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror()); err(&argv[0], e);
exit(EX_IOERR); exit(EX_IOERR.into());
}); });
ExitCode::SUCCESS
} }

View File

@ -19,7 +19,7 @@
use std::{ use std::{
cmp::Ordering, cmp::Ordering,
env::args, env::args,
io::{ stdin, stdout, Write }, io::{ Write, stdin, stdout },
process::{ ExitCode, exit }, process::{ ExitCode, exit },
}; };
@ -31,7 +31,7 @@ use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd; #[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; #[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
/* list of SI prefixes */ /* list of SI prefixes */
const LIST: [(u32, &str); 10] = [ const LIST: [(u32, &str); 10] = [
@ -47,6 +47,16 @@ const LIST: [(u32, &str); 10] = [
(30, "Q"), /* quetta */ (30, "Q"), /* quetta */
]; ];
fn err(argv0: &String, message: String, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, message);
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {}", argv0);
ExitCode::from(EX_USAGE)
}
fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> { fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
/* preserve decimal places in output by casting to a float */ /* preserve decimal places in output by casting to a float */
let mut out = (input as f64, (0_u32, "")); let mut out = (input as f64, (0_u32, ""));
@ -81,53 +91,48 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
if let Some(_) = argv.get(1) { if let Some(_) = argv.get(1) { return usage(&argv[0]); }
eprintln!("Usage: {}", argv[0]);
return ExitCode::from(EX_USAGE as u8);
}
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio"); let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e.strerror(), Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8); }
if let Err(e) = unveil(None, None) {
return err(&argv[0], e.strerror(), Some(EX_OSERR));
} }
} }
let mut buf = String::new(); let mut buf = String::new();
while let Ok(_) = stdin().read_line(&mut buf) { if let Err(e) = stdin().read_line(&mut buf) {
if buf.is_empty() { return ExitCode::SUCCESS; } return err(&argv[0], e.strerror(), Some(EX_IOERR));
}
let n: u128 = match buf.trim().parse() { if buf.is_empty() { return ExitCode::SUCCESS; }
Ok(f) => {
buf.clear();
f
},
Err(err) => {
eprintln!("{}: {}", argv[0], err);
return ExitCode::from(EX_DATAERR as u8);
},
};
let (number, prefix) = match convert(n) { let n: u128 = match buf.trim().parse() {
Ok(x) => x, Ok(f) => {
Err(err) => { buf.clear();
eprintln!("{}: {}", argv[0], err); f
return ExitCode::from(EX_SOFTWARE as u8); },
}, Err(e) => return err(&argv[0], e.to_string(), Some(EX_DATAERR)),
}; };
let si_prefix = format!("{}B", prefix.1); let (number, prefix) = convert(n).unwrap_or_else(|e| {
let _ = err(&argv[0], e.to_string(), None);
exit(EX_SOFTWARE.into());
});
/* round output number to one decimal place */ let si_prefix = prefix.1.to_owned() + "B";
let out = ((number * 10.0).round() / 10.0).to_string();
stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes()) /* round output number to one decimal place */
.unwrap_or_else(|e| { let rounded = (number * 10.0).round() / 10.0;
eprintln!("{}: {}", argv[0], e.strerror()); let out = rounded.to_string() + " " + &si_prefix + &'\n'.to_string();
exit(EX_IOERR);
}); if let Err(e) = stdout().write_all(out.as_bytes()) {
return err(&argv[0], e.strerror(), Some(EX_IOERR));
} }
ExitCode::SUCCESS ExitCode::SUCCESS

View File

@ -26,27 +26,35 @@ extern crate getopt;
extern crate sysexits; extern crate sysexits;
use getopt::GetOpt; use getopt::GetOpt;
use sysexits::EX_USAGE; use sysexits::{ EX_DATAERR, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd; #[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] extern crate strerror; #[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; #[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
#[cfg(target_os="openbsd")] use strerror::StrError; #[cfg(target_os="openbsd")] use strerror::StrError;
fn usage(s: &str) -> ExitCode { fn err(argv0: &String, e: String, code: u8) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", s); eprintln!("{}: {}", argv0, e);
ExitCode::from(EX_USAGE as u8) ExitCode::from(code)
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", argv0);
ExitCode::from(EX_USAGE)
} }
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio"); let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e.strerror(), EX_OSERR);
return ExitCode::from(EX_OSERR as u8); }
if let Err(e) = unveil(None, None) {
return err(&argv[0], e.strerror(), EX_OSERR);
} }
} }
@ -78,8 +86,8 @@ fn main() -> ExitCode {
match arg.parse::<usize>() { /* parse current operand */ match arg.parse::<usize>() { /* parse current operand */
Ok(n) => currn = n, Ok(n) => currn = n,
Err(e) => { Err(e) => {
eprintln!("{}: {}: {}", &argv[0], arg, e); let error = arg.to_owned() + ": " + &e.to_string();
return ExitCode::from(EX_USAGE as u8); return err(&argv[0], error, EX_DATAERR);
} }
} }

View File

@ -43,6 +43,10 @@ impl Promises {
} }
} }
impl Default for Promises {
fn default() -> Self { Promises::new("") }
}
pub fn pledge( pub fn pledge(
promises: Option<Promises>, execpromises: Option<Promises> promises: Option<Promises>, execpromises: Option<Promises>
) -> Result<(), Error> { ) -> Result<(), Error> {
@ -65,14 +69,12 @@ pub fn pledge(
pub struct UnveilPerms(CString); pub struct UnveilPerms(CString);
impl UnveilPerms { impl UnveilPerms {
pub fn new(permissions: Vec<char>) -> Self { pub fn new<T: IntoIterator<Item = char>>(permissions: T) -> Self {
if permissions.is_empty() { let perms = CString::new(
return UnveilPerms(CString::new("").unwrap()); permissions.into_iter().collect::<String>()
} ).unwrap();
UnveilPerms( UnveilPerms(perms)
CString::new(permissions.iter().collect::<String>()).unwrap()
)
} }
} }
@ -83,9 +85,9 @@ pub fn unveil(
let path_c = path.map(CString::new).map(Result::unwrap); let path_c = path.map(CString::new).map(Result::unwrap);
let arg1 = path_c.map(|p| p.into_raw() as *const c_char).unwrap_or(null()); let arg1 = path_c.map(|p| p.into_raw() as *const c_char).unwrap_or(null());
let arg2 = permissions let arg2 = permissions.map(|p| {
.map(|p| p.0.into_raw() as *const c_char) p.0.into_raw() as *const c_char
.unwrap_or(null()); }).unwrap_or(null());
unsafe { unsafe {
match openbsd::unveil(arg1, arg2) { match openbsd::unveil(arg1, arg2) {

View File

@ -20,9 +20,9 @@
use std::{ use std::{
env::args, env::args,
fs::File, fs::File,
io::{ stdin, stdout, stderr, BufWriter, Read, Write }, io::{ Error, BufWriter, Read, Write, stderr, stdin, stdout },
os::fd::{ AsRawFd, FromRawFd }, os::fd::{ AsRawFd, FromRawFd },
process::{ exit, ExitCode }, process::{ ExitCode, exit},
}; };
extern crate getopt; extern crate getopt;
@ -47,15 +47,23 @@ use ArgMode::*;
enum ArgMode { In, Out } enum ArgMode { In, Out }
fn err(argv0: &String, e: Error, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn usage(argv0: &String) -> ExitCode {
eprintln!("Usage: {} [-aetu] [-i input] [-o output]", argv0);
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>(); let argv = args().collect::<Vec<_>>();
let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]);
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("cpath rpath stdio unveil wpath"); let promises = Promises::new("cpath rpath stdio unveil wpath");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8);
} }
} }
@ -85,8 +93,7 @@ fn main() -> ExitCode {
mode = Some(Out); /* latest argument == -o */ mode = Some(Out); /* latest argument == -o */
}, },
Err(_) | Ok(_) => { Err(_) | Ok(_) => {
eprintln!("{}", usage); return usage(&argv[0]);
return ExitCode::from(EX_USAGE as u8);
}, },
}; };
@ -108,32 +115,28 @@ fn main() -> ExitCode {
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
for input in &ins { for input in &ins {
let perms = UnveilPerms::new(vec!['r']); let perms = UnveilPerms::new(['r']);
if let Err(e) = unveil(Some(&input), Some(perms)) { if let Err(e) = unveil(Some(&input), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8);
} }
} }
for output in &outs { for output in &outs {
let perms = UnveilPerms::new(vec!['c', 'w']); let perms = UnveilPerms::new(['c', 'w']);
if let Err(e) = unveil(Some(&output), Some(perms)) { if let Err(e) = unveil(Some(&output), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8);
} }
} }
if let Err(e) = unveil(None, None) { if let Err(e) = unveil(None, None) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8);
} }
} }
if ins.is_empty() && outs.is_empty() && argv.len() > optind { if ins.is_empty() && outs.is_empty() && argv.len() > optind {
eprintln!("Usage: {}", usage); return usage(&argv[0]);
return ExitCode::from(EX_USAGE as u8);
} }
/* use stdin if no inputs are specified */ /* use stdin if no inputs are specified */
@ -153,8 +156,8 @@ fn main() -> ExitCode {
match File::open(file) { match File::open(file) {
Ok(f) => f, Ok(f) => f,
Err(e) => { Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror()); let _ = err(&(argv[0].clone() + ": " + file), e, None);
exit(EX_IOERR); exit(EX_IOERR.into());
}, },
} }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
@ -180,8 +183,8 @@ fn main() -> ExitCode {
match options { match options {
Ok(f) => return f, Ok(f) => return f,
Err(e) => { Err(e) => {
eprintln!("{}: {}: {}", argv[0], file, e.strerror()); let _ = err(&(argv[0].clone() + ": " + file), e, None);
exit(EX_IOERR); exit(EX_IOERR.into());
}, },
}; };
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
@ -205,21 +208,19 @@ fn main() -> ExitCode {
for file in inputs { for file in inputs {
for byte in file.bytes().map(|b| { for byte in file.bytes().map(|b| {
b.unwrap_or_else(|e| { b.unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror()); let _ = err(&argv[0], e, None);
exit(EX_IOERR); exit(EX_IOERR.into());
}) })
}) { }) {
for out in &mut outputs { for out in &mut outputs {
if let Err(e) = out.write(&[byte]) { if let Err(e) = out.write(&[byte]) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_IOERR));
return ExitCode::from(EX_IOERR as u8);
} }
if u { if u {
/* immediately flush the output for -u */ /* immediately flush the output for -u */
if let Err(e) = out.flush() { if let Err(e) = out.flush() {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], e, Some(EX_IOERR));
return ExitCode::from(EX_IOERR as u8);
} }
} }
} }

View File

@ -20,7 +20,7 @@
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin, #include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin,
* stdout, EOF */ * stdout, EOF */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */ #include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
#include <unistd.h> /* pledge(2), getopt(3) */ #include <unistd.h> /* NULL, getopt(3), pledge(2), unveil(2) */
char *program_name = "npc"; char *program_name = "npc";
@ -44,7 +44,7 @@ int main(int argc, char *argv[]) {
char showtab = 0; /* prints tab characters in caret notation */ char showtab = 0; /* prints tab characters in caret notation */
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) { if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL)) {
perror(argv[0] == NULL ? program_name : argv[0]); perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR; return EX_OSERR;
} }

View File

@ -46,7 +46,7 @@ use std::{
collections::VecDeque, collections::VecDeque,
env::args, env::args,
fmt::{ self, Display, Formatter }, fmt::{ self, Display, Formatter },
io::stdin, io::{ Error, Write, stdin, stdout },
process::ExitCode, process::ExitCode,
}; };
@ -54,13 +54,13 @@ use CalcType::*;
extern crate sysexits; extern crate sysexits;
use sysexits::EX_DATAERR; use sysexits::{ EX_DATAERR, EX_IOERR };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate strerror; #[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] extern crate openbsd; #[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use strerror::StrError; #[cfg(target_os="openbsd")] use strerror::StrError;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; #[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
#[derive(Clone, PartialEq, PartialOrd, Debug)] #[derive(Clone, PartialEq, PartialOrd, Debug)]
/* enum CalcType is a type containing operations used in the calculator */ /* enum CalcType is a type containing operations used in the calculator */
@ -120,12 +120,46 @@ impl Display for CalcType {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct EvaluationError { struct EvaluationError {
message: String, message: String,
code: i32, code: u8,
} }
/* Im no math nerd but I want the highest possible approximation of 0.9 impl StrError for EvaluationError {
* repeating and it seems this can give it to me */ fn strerror(&self) -> String {
const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0; self.message.clone()
}
}
fn err<T: StrError>(argv0: &String, e: &T, code: Option<u8>) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(code.unwrap_or(1 /* unknown error */))
}
fn operate(
mut stack: VecDeque<f64>,
op: CalcType,
) -> Result<VecDeque<f64>, EvaluationError> {
let vals = (stack.pop_back(), stack.pop_back());
if let (Some(x), Some(y)) = vals {
match op {
Add => stack.push_back(y + x),
Subtract => stack.push_back(y - x),
Multiply => stack.push_back(y * x),
Divide => stack.push_back(y / x),
Power => stack.push_back(y.powf(x)),
Floor => stack.push_back((y / x).floor()),
Modulo => stack.push_back(y % x),
_ => {},
};
} else {
return Err(EvaluationError {
message: format!("{}: unexpected operation", op),
code: EX_DATAERR,
})
}
Ok(stack)
}
fn eval( fn eval(
input: &str, input: &str,
@ -145,6 +179,7 @@ fn eval(
.rev() .rev()
.map(|t| CalcType::from(t)) .map(|t| CalcType::from(t))
.collect(); .collect();
let mut ops: VecDeque<CalcType> = VecDeque::new(); let mut ops: VecDeque<CalcType> = VecDeque::new();
while let Some(n) = toks.pop_back() { while let Some(n) = toks.pop_back() {
@ -152,36 +187,15 @@ fn eval(
Val(v) => stack.push_back(v), Val(v) => stack.push_back(v),
Invalid(i) => { Invalid(i) => {
return Err(EvaluationError { return Err(EvaluationError {
message: format!("{}: Invalid token", i), message: format!("{}: invalid token", i),
code: EX_DATAERR, code: EX_DATAERR,
}) })
}, },
op => { op => {
ops.push_back(op.clone()); ops.push_back(op.clone());
oper = true;
let vals = ( oper = true; /* this is an operation */
stack.pop_back(), return operate(stack, op).map(|s| (s, oper));
stack.pop_back(),
);
if let (Some(x), Some(y)) = vals {
match op {
Add => stack.push_back(y + x),
Subtract => stack.push_back(y - x),
Multiply => stack.push_back(y * x),
Divide => stack.push_back(y / x),
Power => stack.push_back(y.powf(x)),
Floor => stack.push_back((y / x).floor()),
Modulo => stack.push_back(y % x),
_ => {},
};
} else {
return Err(EvaluationError {
message: format!("{}: Unexpected operation", op),
code: EX_DATAERR,
})
}
}, },
}; };
} }
@ -190,51 +204,46 @@ fn eval(
} }
/* Round a float to the given precision level */ /* Round a float to the given precision level */
fn round_precise(value: &f64, precision: usize) -> f64 { fn round_precise(value: &f64) -> f64 {
let multiplier = 10_f64.powi(precision as i32); /* Set floating-point precision for correcting rounding errors based on
* machine epsilon */
let precision = (-f64::EPSILON.log10()).floor() as i32;
let multiplier = 10_f64.powi(precision);
(value * multiplier).round() / multiplier (value * multiplier).round() / multiplier
} }
/* print the stack and let the caller know if evaluation should continue */
fn unstack(stack: VecDeque<f64>, op: bool) -> Result<bool, Error> {
if let Some(val) = stack.iter().last() {
if !op { return Ok(true); }
let out = round_precise(val).to_string() + &'\n'.to_string();
return stdout().write_all(out.as_bytes()).map(|_| true);
} else {
return Ok(false);
}
}
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio"); let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
eprintln!("{}: {}", argv[0], e.strerror()); return err(&argv[0], &e, Some(EX_OSERR));
return ExitCode::from(EX_OSERR as u8); }
if let Err(e) = unveil(None, None) {
return err(&argv[0], &e, Some(EX_OSERR));
} }
} }
let mut stack = VecDeque::new(); let mut stack = VecDeque::new();
let mut buf = String::new(); let mut buf = String::new();
/* Set floating-point precision for correcting rounding errors based on
* machine epsilon */
let precision = (-f64::EPSILON.log10() * PRECISION_MOD).ceil() as usize;
if argv.get(1).is_none() { /* read from stdin */ if argv.get(1).is_some() { /* read expressions from argv */
while let Ok(_) = stdin().read_line(&mut buf) {
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone();
let val = match stack.iter().last() {
Some(v) => v,
None => break,
};
if s.1 == false { continue; }
println!("{}", round_precise(val, precision).to_string());
},
Err(err) => {
eprintln!("{}: {}", argv[0], err.message);
return ExitCode::from(err.code as u8);
},
};
}
} else { /* read from argv */
/* join argv into an owned String joined by spaces minus argv[0] */ /* join argv into an owned String joined by spaces minus argv[0] */
let input = argv let input = argv
.iter() .iter()
@ -245,20 +254,44 @@ fn main() -> ExitCode {
match eval(&input, stack) { match eval(&input, stack) {
Ok(s) => { Ok(s) => {
stack = s.0.clone(); /* we can ignore the return value of unstack() because we are
* not continually evaluating from stdin */
if let Err(e) = unstack(s.0.clone(), s.1.clone()) {
return err(&argv[0], &e, Some(EX_IOERR));
}
let val = match stack.iter().last() { return ExitCode::SUCCESS;
Some(v) => v,
None => return ExitCode::SUCCESS,
};
println!("{}", round_precise(val, precision).to_string())
}, },
Err(err) => { Err(e) => {
eprintln!("{}: {}", argv[0], err.message); return err(&argv[0], &e, Some(e.code));
return ExitCode::from(err.code as u8);
}, },
}; };
} }
/* else, read from stdin */
loop { /* take input until EOF */
if let Err(e) = stdin().read_line(&mut buf) {
return err(&argv[0], &e, Some(EX_IOERR));
}
match eval(&buf.trim(), stack) {
Ok(s) => {
buf.clear();
stack = s.0.clone(); /* out with the old, in with the new */
match unstack(s.0, s.1) {
Ok(b) if b => continue,
Ok(_) => break,
Err(e) => {
return err(&argv[0], &e, Some(EX_IOERR))
},
};
},
Err(e) => {
return err(&argv[0], &e, Some(e.code));
},
};
}
ExitCode::SUCCESS ExitCode::SUCCESS
} }

View File

@ -46,7 +46,7 @@ int main(int argc, char *argv[]) {
program_name = argv[0] == NULL ? program_name : argv[0]; program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge("rpath stdio unveil", NULL) == -1) { if (pledge("rpath stdio unveil", "") == -1) {
perror(program_name); perror(program_name);
return EX_OSERR; return EX_OSERR;
} }
@ -82,7 +82,7 @@ int main(int argc, char *argv[]) {
struct stat buf; struct stat buf;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (unveil(*argv, "r") == -1) { if (unveil(*argv, "rw") == -1) {
perror(program_name); perror(program_name);
return EX_OSERR; return EX_OSERR;
} }

View File

@ -25,7 +25,7 @@
#include <sysexits.h> /* EX_OSERR, EX_USAGE */ #include <sysexits.h> /* EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__ #ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */ # include <unistd.h> /* pledge(2), unveil(2) */
#endif #endif
char *program_name = "str"; char *program_name = "str";
@ -62,7 +62,7 @@ int main(int argc, char *argv[]) {
program_name = argv[0] == NULL ? program_name : argv[0]; program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) { if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) {
perror(program_name); perror(program_name);
return EX_OSERR; return EX_OSERR;
} }

View File

@ -20,7 +20,7 @@
#include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */ #include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__ #ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */ # include <unistd.h> /* pledge(2), unveil(2) */
#endif #endif
char *program_name = "strcmp"; char *program_name = "strcmp";
@ -29,7 +29,7 @@ int main(int argc, char *argv[]) {
unsigned int i; unsigned int i;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) { if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) {
perror(argv[0] == NULL ? program_name : argv[0]); perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR; return EX_OSERR;

View File

@ -33,31 +33,29 @@ use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
use strerror::StrError; use strerror::StrError;
#[cfg(target_os="openbsd")] extern crate openbsd; #[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; #[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil };
fn err(argv0: &String, e: Error, code: u8) -> ExitCode {
fn oserr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror()); eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_OSERR as u8) ExitCode::from(code)
} }
fn ioerr(argv0: &str, e: Error) -> ExitCode { fn usage(s: &String) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_IOERR as u8)
}
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-w word_size]", s); eprintln!("Usage: {} [-w word_size]", s);
ExitCode::from(EX_USAGE as u8) ExitCode::from(EX_USAGE)
} }
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
#[cfg(target_os="openbsd")] { #[cfg(target_os="openbsd")] {
let promises = Promises::new("stdio"); let promises = Promises::new("stdio unveil");
if let Err(e) = pledge(Some(promises), None) { if let Err(e) = pledge(Some(promises), Some(Promises::default())) {
return oserr(&argv[0], e); return err(&argv[0], e, EX_OSERR);
}
if let Err(e) = unveil(None, None) {
return err(&argv[0], e, EX_OSERR);
} }
} }
@ -88,22 +86,22 @@ fn main() -> ExitCode {
loop { loop {
match input.read(&mut buf) { match input.read(&mut buf) {
Ok(0) => break ExitCode::from(EX_OK as u8), // read nothing; bye Ok(0) => break ExitCode::from(EX_OK), // read nothing; bye
Ok(v) if v == wordsize => { // read full block; swab Ok(v) if v == wordsize => { // read full block; swab
let (left, right) = buf.split_at(v/2); let (left, right) = buf.split_at(v/2);
if let Err(e) = output.write(&right) if let Err(e) = output.write(&right)
.and_then(|_| output.write(&left)) { .and_then(|_| output.write(&left)) {
break ioerr(&argv[0], e); break err(&argv[0], e, EX_IOERR);
} }
}, },
Ok(v) => { // partial read; partially write Ok(v) => { // partial read; partially write
if let Err(e) = output.write(&buf[..v]) { if let Err(e) = output.write(&buf[..v]) {
break ioerr(&argv[0], e); break err(&argv[0], e, EX_IOERR);
} }
}, },
Err(e) => break oserr(&argv[0], e) Err(e) => break err(&argv[0], e, EX_OSERR)
} }
} }
} }

View File

@ -13,6 +13,6 @@
int main(void) { int main(void) {
#ifdef __OpenBSD__ #ifdef __OpenBSD__
pledge(NULL, NULL); (void)pledge("stdio", "");
#endif #endif
} }

View File

@ -24,6 +24,7 @@ fop_fail: $(BIN)/fop
! printf 'test\n' | $(BIN)/fop 1 cat ! printf 'test\n' | $(BIN)/fop 1 cat
! printf 'test\n' | $(BIN)/fop 'test' cat ! printf 'test\n' | $(BIN)/fop 'test' cat
! printf 'test\n' | $(BIN)/fop -d'test' cat ! printf 'test\n' | $(BIN)/fop -d'test' cat
! $(BIN)/fop
.PHONY: fop_functionality .PHONY: fop_functionality
fop_functionality: $(BIN)/fop fop_functionality: $(BIN)/fop

View File

@ -6,7 +6,7 @@
# notice are preserved. This file is offered as-is, without any warranty. # notice are preserved. This file is offered as-is, without any warranty.
.PHONY: mm_tests .PHONY: mm_tests
mm_tests: mm_args mm_help mm_stderr mm_remaining mm_tests: mm_args mm_help mm_stderr mm_remaining mm_remaining_options
.PHONY: mm_none .PHONY: mm_none
mm_none: $(BIN)/mm mm_none: $(BIN)/mm
@ -32,3 +32,9 @@ mm_remaining: $(BIN)/mm
test "$$($(BIN)/mm -i README COPYING)" = "$$(cat README COPYING)" test "$$($(BIN)/mm -i README COPYING)" = "$$(cat README COPYING)"
$(BIN)/mm -i README -o /tmp/mm_test0 /tmp/mm_test1 $(BIN)/mm -i README -o /tmp/mm_test0 /tmp/mm_test1
diff /tmp/mm_test0 /tmp/mm_test1 diff /tmp/mm_test0 /tmp/mm_test1
.PHONY: mm_remaining_options
# check to make sure mm -i with trailing arguments interprets -o as one
mm_remaining_options:
! $(BIN)/mm -i README COPYING -o - 2>&1 | cut -d: -f2 \
| xargs test " -o" =

View File

@ -6,7 +6,7 @@
# notice are preserved. This file is offered as-is, without any warranty. # notice are preserved. This file is offered as-is, without any warranty.
.PHONY: rpn_tests .PHONY: rpn_tests
rpn_tests: rpn_help rpn_add rpn_sub rpn_mul rpn_div rpn_mod rpn_flr rpn_tests: rpn_help rpn_add rpn_sub rpn_mul rpn_div rpn_mod rpn_flr rpn_stdin
.PHONY: rpn_help .PHONY: rpn_help
rpn_help: $(BIN)/rpn rpn_help: $(BIN)/rpn
@ -41,3 +41,8 @@ rpn_mod: $(BIN)/rpn
rpn_flr: $(BIN)/rpn rpn_flr: $(BIN)/rpn
test "$$($(BIN)/rpn 12 5 //)" -eq 2 test "$$($(BIN)/rpn 12 5 //)" -eq 2
test "$$($(BIN)/rpn 9 4 //)" -eq 2 test "$$($(BIN)/rpn 9 4 //)" -eq 2
# done last because all operations have been tested
.PHONY: rpn_stdin
rpn_stdin: $(BIN)/rpn
test "$$(printf '1\n2\n+\n3\n-\n' | $(BIN)/rpn | tail -n1)" -eq 0