diff --git a/Makefile b/Makefile index 11f1954..6e92fd9 100644 --- a/Makefile +++ b/Makefile @@ -95,7 +95,7 @@ build/o/libstrerror.rlib: build src/libstrerror.rs src/libstrerror.rs 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 $@ - # bandage solution until bindgen(1) gets stdin support diff --git a/src/false.c b/src/false.c index 555a170..3cea001 100644 --- a/src/false.c +++ b/src/false.c @@ -13,7 +13,7 @@ int main(void) { #ifdef __OpenBSD__ - pledge(NULL, NULL); + (void)pledge("stdio", ""); #endif return 1; } diff --git a/src/fop.rs b/src/fop.rs index 7c8424e..261aba5 100644 --- a/src/fop.rs +++ b/src/fop.rs @@ -18,8 +18,8 @@ use std::{ env::args, - io::{ Read, stdin, stdout, Write }, - process::{ Command, exit, Stdio }, + io::{ Error, Read, Write, stdin, stdout }, + process::{ Command, ExitCode, Stdio, exit }, }; 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")] 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::>(); let mut d = '\u{1E}'.to_string(); /* ASCII record separator */ let mut optind = 1; #[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) { - eprintln!("{}: {}", argv[0], e.strerror()); - exit(EX_OSERR); + err(&argv[0], e); + return ExitCode::from(EX_OSERR); + } + + if let Err(e) = unveil(None, None) { + err(&argv[0], e); + return ExitCode::from(EX_OSERR); } } - let usage = format!( - "Usage: {} [-d delimiter] index command [args...]", - argv[0], - ); + if argv.len() == 1 { return ExitCode::from(usage(&argv[0])); } while let Some(opt) = argv.getopt("d:") { match opt.opt() { @@ -60,8 +71,7 @@ fn main() { optind = opt.ind(); }, _ => { - eprintln!("{}", usage); - exit(EX_USAGE); + return ExitCode::from(usage(&argv[0])); } }; } @@ -69,7 +79,7 @@ fn main() { /* parse the specified index as a number we can use */ let index = argv[optind].parse::().unwrap_or_else(|e| { eprintln!("{}: {}: {}", argv[0], argv[1], e); - exit(EX_DATAERR); + exit(EX_DATAERR.into()); }); /* index of the argv[0] for the operator command */ @@ -77,15 +87,14 @@ fn main() { /* argv[0] of the operator command */ let operator = argv.get(command_arg).unwrap_or_else(|| { - eprintln!("{}", usage); - exit(EX_USAGE); + exit(usage(&argv[0]).into()); }); /* read entire standard input into memory */ let mut buf = String::new(); if let Err(e) = stdin().read_to_string(&mut buf) { - eprintln!("{}: {}", argv[0], e.strerror()); - exit(EX_IOERR); + err(&argv[0], e); + exit(EX_IOERR.into()); }; /* 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 */ .spawn() .unwrap_or_else( |e| { - eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); - exit(EX_UNAVAILABLE); + err(&argv[0], e); + exit(EX_UNAVAILABLE.into()); }); /* get field we want to pipe into spawned program */ let field = fields.get(index).unwrap_or_else(|| { - eprintln!( - "{}: {}: No such index in input", - argv[0], - index.to_string(), - ); - exit(EX_DATAERR); + eprintln!("{}: {}: no such index in input", argv[0], index); + exit(EX_DATAERR.into()); }); /* 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| { - eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); - exit(EX_IOERR); + err(&argv[0], e); + exit(EX_IOERR.into()); }); /* 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 */ let new_field = String::from_utf8(replace).unwrap_or_else(|e| { - eprintln!("{}: {}: {}", argv[0], argv[command_arg], e); - exit(EX_IOERR); + eprintln!("{}: {}", argv[0], e); + exit(EX_IOERR.into()); }); /* store the new field in the old fields vector */ @@ -154,7 +159,9 @@ fn main() { stdout().write_all( fields.join(&d.to_string()).as_bytes() ).unwrap_or_else(|e| { - eprintln!("{}: {}", argv[0], e.strerror()); - exit(EX_IOERR); + err(&argv[0], e); + exit(EX_IOERR.into()); }); + + ExitCode::SUCCESS } diff --git a/src/hru.rs b/src/hru.rs index e07ec39..153032b 100644 --- a/src/hru.rs +++ b/src/hru.rs @@ -19,7 +19,7 @@ use std::{ cmp::Ordering, env::args, - io::{ stdin, stdout, Write }, + io::{ Write, stdin, stdout }, 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")] extern crate openbsd; -#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge }; +#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge, unveil }; /* list of SI prefixes */ const LIST: [(u32, &str); 10] = [ @@ -47,6 +47,16 @@ const LIST: [(u32, &str); 10] = [ (30, "Q"), /* quetta */ ]; +fn err(argv0: &String, message: String, code: Option) -> 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> { /* preserve decimal places in output by casting to a float */ 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 { let argv = args().collect::>(); - if let Some(_) = argv.get(1) { - eprintln!("Usage: {}", argv[0]); - return ExitCode::from(EX_USAGE as u8); - } + if let Some(_) = argv.get(1) { return usage(&argv[0]); } #[cfg(target_os="openbsd")] { - let promises = Promises::new("stdio"); - if let Err(e) = pledge(Some(promises), None) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + let promises = Promises::new("stdio unveil"); + if let Err(e) = pledge(Some(promises), Some(Promises::default())) { + return err(&argv[0], e.strerror(), Some(EX_OSERR)); + } + + if let Err(e) = unveil(None, None) { + return err(&argv[0], e.strerror(), Some(EX_OSERR)); } } let mut buf = String::new(); - while let Ok(_) = stdin().read_line(&mut buf) { - if buf.is_empty() { return ExitCode::SUCCESS; } + if let Err(e) = stdin().read_line(&mut buf) { + return err(&argv[0], e.strerror(), Some(EX_IOERR)); + } - let n: u128 = match buf.trim().parse() { - Ok(f) => { - buf.clear(); - f - }, - Err(err) => { - eprintln!("{}: {}", argv[0], err); - return ExitCode::from(EX_DATAERR as u8); - }, - }; + if buf.is_empty() { return ExitCode::SUCCESS; } - let (number, prefix) = match convert(n) { - Ok(x) => x, - Err(err) => { - eprintln!("{}: {}", argv[0], err); - return ExitCode::from(EX_SOFTWARE as u8); - }, - }; + let n: u128 = match buf.trim().parse() { + Ok(f) => { + buf.clear(); + f + }, + 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 out = ((number * 10.0).round() / 10.0).to_string(); + let si_prefix = prefix.1.to_owned() + "B"; - stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes()) - .unwrap_or_else(|e| { - eprintln!("{}: {}", argv[0], e.strerror()); - exit(EX_IOERR); - }); + /* round output number to one decimal place */ + let rounded = (number * 10.0).round() / 10.0; + let out = rounded.to_string() + " " + &si_prefix + &'\n'.to_string(); + + if let Err(e) = stdout().write_all(out.as_bytes()) { + return err(&argv[0], e.strerror(), Some(EX_IOERR)); } ExitCode::SUCCESS diff --git a/src/intcmp.rs b/src/intcmp.rs index 456f268..5f8f64e 100644 --- a/src/intcmp.rs +++ b/src/intcmp.rs @@ -26,27 +26,35 @@ extern crate getopt; extern crate sysexits; 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")] extern crate openbsd; #[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; -fn usage(s: &str) -> ExitCode { - eprintln!("Usage: {} [-egl] integer integer...", s); - ExitCode::from(EX_USAGE as u8) +fn err(argv0: &String, e: String, code: u8) -> ExitCode { + eprintln!("{}: {}", argv0, e); + ExitCode::from(code) +} + +fn usage(argv0: &String) -> ExitCode { + eprintln!("Usage: {} [-egl] integer integer...", argv0); + ExitCode::from(EX_USAGE) } fn main() -> ExitCode { let argv = args().collect::>(); #[cfg(target_os="openbsd")] { - let promises = Promises::new("stdio"); - if let Err(e) = pledge(Some(promises), None) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + let promises = Promises::new("stdio unveil"); + if let Err(e) = pledge(Some(promises), Some(Promises::default())) { + return err(&argv[0], e.strerror(), EX_OSERR); + } + + 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::() { /* parse current operand */ Ok(n) => currn = n, Err(e) => { - eprintln!("{}: {}: {}", &argv[0], arg, e); - return ExitCode::from(EX_USAGE as u8); + let error = arg.to_owned() + ": " + &e.to_string(); + return err(&argv[0], error, EX_DATAERR); } } diff --git a/src/libopenbsd.rs b/src/libopenbsd.rs index f3e60b9..2557dd8 100644 --- a/src/libopenbsd.rs +++ b/src/libopenbsd.rs @@ -43,6 +43,10 @@ impl Promises { } } +impl Default for Promises { + fn default() -> Self { Promises::new("") } +} + pub fn pledge( promises: Option, execpromises: Option ) -> Result<(), Error> { @@ -65,14 +69,12 @@ pub fn pledge( pub struct UnveilPerms(CString); impl UnveilPerms { - pub fn new(permissions: Vec) -> Self { - if permissions.is_empty() { - return UnveilPerms(CString::new("").unwrap()); - } - - UnveilPerms( - CString::new(permissions.iter().collect::()).unwrap() - ) + pub fn new>(permissions: T) -> Self { + let perms = CString::new( + permissions.into_iter().collect::() + ).unwrap(); + + UnveilPerms(perms) } } @@ -83,9 +85,9 @@ pub fn unveil( 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 arg2 = permissions - .map(|p| p.0.into_raw() as *const c_char) - .unwrap_or(null()); + let arg2 = permissions.map(|p| { + p.0.into_raw() as *const c_char + }).unwrap_or(null()); unsafe { match openbsd::unveil(arg1, arg2) { diff --git a/src/mm.rs b/src/mm.rs index 10c6ef2..700082e 100644 --- a/src/mm.rs +++ b/src/mm.rs @@ -20,9 +20,9 @@ use std::{ env::args, fs::File, - io::{ stdin, stdout, stderr, BufWriter, Read, Write }, + io::{ Error, BufWriter, Read, Write, stderr, stdin, stdout }, os::fd::{ AsRawFd, FromRawFd }, - process::{ exit, ExitCode }, + process::{ ExitCode, exit}, }; extern crate getopt; @@ -47,15 +47,23 @@ use ArgMode::*; enum ArgMode { In, Out } +fn err(argv0: &String, e: Error, code: Option) -> 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 { let argv = args().collect::>(); - let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]); #[cfg(target_os="openbsd")] { let promises = Promises::new("cpath rpath stdio unveil wpath"); - if let Err(e) = pledge(Some(promises), None) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + if let Err(e) = pledge(Some(promises), Some(Promises::default())) { + return err(&argv[0], e, Some(EX_OSERR)); } } @@ -85,8 +93,7 @@ fn main() -> ExitCode { mode = Some(Out); /* latest argument == -o */ }, Err(_) | Ok(_) => { - eprintln!("{}", usage); - return ExitCode::from(EX_USAGE as u8); + return usage(&argv[0]); }, }; @@ -108,32 +115,28 @@ fn main() -> ExitCode { #[cfg(target_os="openbsd")] { for input in &ins { - let perms = UnveilPerms::new(vec!['r']); + let perms = UnveilPerms::new(['r']); if let Err(e) = unveil(Some(&input), Some(perms)) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + return err(&argv[0], e, Some(EX_OSERR)); } } 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)) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + return err(&argv[0], e, Some(EX_OSERR)); } } if let Err(e) = unveil(None, None) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + return err(&argv[0], e, Some(EX_OSERR)); } } if ins.is_empty() && outs.is_empty() && argv.len() > optind { - eprintln!("Usage: {}", usage); - return ExitCode::from(EX_USAGE as u8); + return usage(&argv[0]); } /* use stdin if no inputs are specified */ @@ -153,8 +156,8 @@ fn main() -> ExitCode { match File::open(file) { Ok(f) => f, Err(e) => { - eprintln!("{}: {}: {}", argv[0], file, e.strerror()); - exit(EX_IOERR); + let _ = err(&(argv[0].clone() + ": " + file), e, None); + exit(EX_IOERR.into()); }, } }).collect::>(); @@ -180,8 +183,8 @@ fn main() -> ExitCode { match options { Ok(f) => return f, Err(e) => { - eprintln!("{}: {}: {}", argv[0], file, e.strerror()); - exit(EX_IOERR); + let _ = err(&(argv[0].clone() + ": " + file), e, None); + exit(EX_IOERR.into()); }, }; }).collect::>(); @@ -205,21 +208,19 @@ fn main() -> ExitCode { for file in inputs { for byte in file.bytes().map(|b| { b.unwrap_or_else(|e| { - eprintln!("{}: {}", argv[0], e.strerror()); - exit(EX_IOERR); + let _ = err(&argv[0], e, None); + exit(EX_IOERR.into()); }) }) { for out in &mut outputs { if let Err(e) = out.write(&[byte]) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_IOERR as u8); + return err(&argv[0], e, Some(EX_IOERR)); } if u { /* immediately flush the output for -u */ if let Err(e) = out.flush() { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_IOERR as u8); + return err(&argv[0], e, Some(EX_IOERR)); } } } diff --git a/src/npc.c b/src/npc.c index cd488e5..e00b36e 100644 --- a/src/npc.c +++ b/src/npc.c @@ -20,7 +20,7 @@ #include /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin, * stdout, EOF */ #include /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */ -#include /* pledge(2), getopt(3) */ +#include /* NULL, getopt(3), pledge(2), unveil(2) */ char *program_name = "npc"; @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) { char showtab = 0; /* prints tab characters in caret notation */ #ifdef __OpenBSD__ - if (pledge("stdio", NULL) == -1) { + if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL)) { perror(argv[0] == NULL ? program_name : argv[0]); return EX_OSERR; } diff --git a/src/rpn.rs b/src/rpn.rs index 7669efb..4fc15c3 100644 --- a/src/rpn.rs +++ b/src/rpn.rs @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Emma Tebibyte + * Copyright (c) 2024–2025 Emma Tebibyte * SPDX-License-Identifier: AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify it under @@ -46,21 +46,21 @@ use std::{ collections::VecDeque, env::args, fmt::{ self, Display, Formatter }, - io::stdin, + io::{ Error, Write, stdin, stdout }, process::ExitCode, }; use CalcType::*; +extern crate strerror; extern crate sysexits; -use sysexits::EX_DATAERR; +use strerror::StrError; +use sysexits::{ EX_DATAERR, EX_IOERR }; #[cfg(target_os="openbsd")] use sysexits::EX_OSERR; -#[cfg(target_os="openbsd")] extern crate strerror; #[cfg(target_os="openbsd")] extern crate openbsd; -#[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)] /* enum CalcType is a type containing operations used in the calculator */ @@ -120,12 +120,46 @@ impl Display for CalcType { #[derive(Debug, Clone)] struct EvaluationError { message: String, - code: i32, + code: u8, } -/* I’m no math nerd but I want the highest possible approximation of 0.9 - * repeating and it seems this can give it to me */ -const PRECISION_MOD: f64 = 0.9 + f64::EPSILON * 100.0; +impl StrError for EvaluationError { + fn strerror(&self) -> String { + self.message.clone() + } +} + +fn err(argv0: &String, e: &T, code: Option) -> ExitCode { + eprintln!("{}: {}", argv0, e.strerror()); + ExitCode::from(code.unwrap_or(1 /* unknown error */)) +} + +fn operate( + mut stack: VecDeque, + op: CalcType, +) -> Result, 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( input: &str, @@ -145,6 +179,7 @@ fn eval( .rev() .map(|t| CalcType::from(t)) .collect(); + let mut ops: VecDeque = VecDeque::new(); while let Some(n) = toks.pop_back() { @@ -152,36 +187,15 @@ fn eval( Val(v) => stack.push_back(v), Invalid(i) => { return Err(EvaluationError { - message: format!("{}: Invalid token", i), + message: format!("{}: invalid token", i), code: EX_DATAERR, }) }, op => { ops.push_back(op.clone()); - oper = true; - 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, - }) - } + oper = true; /* this is an operation */ + return operate(stack, op).map(|s| (s, oper)); }, }; } @@ -190,51 +204,46 @@ fn eval( } /* Round a float to the given precision level */ -fn round_precise(value: &f64, precision: usize) -> f64 { - let multiplier = 10_f64.powi(precision as i32); +fn round_precise(value: &f64) -> f64 { + /* 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 } +/* print the stack and let the caller know if evaluation should continue */ +fn unstack(stack: VecDeque, op: bool) -> Result { + 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 { let argv = args().collect::>(); #[cfg(target_os="openbsd")] { - let promises = Promises::new("stdio"); - if let Err(e) = pledge(Some(promises), None) { - eprintln!("{}: {}", argv[0], e.strerror()); - return ExitCode::from(EX_OSERR as u8); + let promises = Promises::new("stdio unveil"); + if let Err(e) = pledge(Some(promises), Some(Promises::default())) { + return err(&argv[0], &e, Some(EX_OSERR)); + } + + if let Err(e) = unveil(None, None) { + return err(&argv[0], &e, Some(EX_OSERR)); } } let mut stack = VecDeque::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 */ - 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 */ + if argv.get(1).is_some() { /* read expressions from argv */ /* join argv into an owned String joined by spaces minus argv[0] */ let input = argv .iter() @@ -245,20 +254,44 @@ fn main() -> ExitCode { match eval(&input, stack) { 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() { - Some(v) => v, - None => return ExitCode::SUCCESS, - }; - - println!("{}", round_precise(val, precision).to_string()) + return ExitCode::SUCCESS; }, - Err(err) => { - eprintln!("{}: {}", argv[0], err.message); - return ExitCode::from(err.code as u8); + Err(e) => { + return err(&argv[0], &e, Some(e.code)); }, }; } + + /* 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 } diff --git a/src/scrut.c b/src/scrut.c index cfdd923..2d3c2bf 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -46,7 +46,7 @@ int main(int argc, char *argv[]) { program_name = argv[0] == NULL ? program_name : argv[0]; #ifdef __OpenBSD__ - if (pledge("rpath stdio unveil", NULL) == -1) { + if (pledge("rpath stdio unveil", "") == -1) { perror(program_name); return EX_OSERR; } @@ -82,7 +82,7 @@ int main(int argc, char *argv[]) { struct stat buf; #ifdef __OpenBSD__ - if (unveil(*argv, "r") == -1) { + if (unveil(*argv, "rw") == -1) { perror(program_name); return EX_OSERR; } diff --git a/src/str.c b/src/str.c index 9ed27c2..6d80bff 100644 --- a/src/str.c +++ b/src/str.c @@ -25,7 +25,7 @@ #include /* EX_OSERR, EX_USAGE */ #ifdef __OpenBSD__ -# include /* pledge(2) */ +# include /* pledge(2), unveil(2) */ #endif char *program_name = "str"; @@ -62,7 +62,7 @@ int main(int argc, char *argv[]) { program_name = argv[0] == NULL ? program_name : argv[0]; #ifdef __OpenBSD__ - if (pledge("stdio", NULL) == -1) { + if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) { perror(program_name); return EX_OSERR; } diff --git a/src/strcmp.c b/src/strcmp.c index 6d930d5..ff2e1ae 100644 --- a/src/strcmp.c +++ b/src/strcmp.c @@ -20,7 +20,7 @@ #include /* EX_OK, EX_OSERR, EX_USAGE */ #ifdef __OpenBSD__ -# include /* pledge(2) */ +# include /* pledge(2), unveil(2) */ #endif char *program_name = "strcmp"; @@ -29,7 +29,7 @@ int main(int argc, char *argv[]) { unsigned int i; #ifdef __OpenBSD__ - if (pledge("stdio", NULL) == -1) { + if (pledge("stdio unveil", "") == -1 || unveil(NULL, NULL) == -1) { perror(argv[0] == NULL ? program_name : argv[0]); return EX_OSERR; diff --git a/src/swab.rs b/src/swab.rs index 4dc86de..e167230 100644 --- a/src/swab.rs +++ b/src/swab.rs @@ -33,31 +33,29 @@ use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE }; use strerror::StrError; #[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 oserr(argv0: &str, e: Error) -> ExitCode { +fn err(argv0: &String, e: Error, code: u8) -> ExitCode { eprintln!("{}: {}", argv0, e.strerror()); - ExitCode::from(EX_OSERR as u8) + ExitCode::from(code) } -fn ioerr(argv0: &str, e: Error) -> ExitCode { - eprintln!("{}: {}", argv0, e.strerror()); - ExitCode::from(EX_IOERR as u8) -} - -fn usage(s: &str) -> ExitCode { +fn usage(s: &String) -> ExitCode { eprintln!("Usage: {} [-w word_size]", s); - ExitCode::from(EX_USAGE as u8) + ExitCode::from(EX_USAGE) } fn main() -> ExitCode { let argv = args().collect::>(); #[cfg(target_os="openbsd")] { - let promises = Promises::new("stdio"); - if let Err(e) = pledge(Some(promises), None) { - return oserr(&argv[0], e); + let promises = Promises::new("stdio unveil"); + if let Err(e) = pledge(Some(promises), Some(Promises::default())) { + 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 { 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 let (left, right) = buf.split_at(v/2); if let Err(e) = output.write(&right) .and_then(|_| output.write(&left)) { - break ioerr(&argv[0], e); + break err(&argv[0], e, EX_IOERR); } }, Ok(v) => { // partial read; partially write 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) } } } diff --git a/src/true.c b/src/true.c index 0e2f91d..d436aee 100644 --- a/src/true.c +++ b/src/true.c @@ -13,6 +13,6 @@ int main(void) { #ifdef __OpenBSD__ - pledge(NULL, NULL); + (void)pledge("stdio", ""); #endif } diff --git a/tests/bonsai/fop.mk b/tests/bonsai/fop.mk index b538031..230e1c1 100755 --- a/tests/bonsai/fop.mk +++ b/tests/bonsai/fop.mk @@ -24,6 +24,7 @@ fop_fail: $(BIN)/fop ! printf 'test\n' | $(BIN)/fop 1 cat ! printf 'test\n' | $(BIN)/fop 'test' cat ! printf 'test\n' | $(BIN)/fop -d'test' cat + ! $(BIN)/fop .PHONY: fop_functionality fop_functionality: $(BIN)/fop diff --git a/tests/bonsai/mm.mk b/tests/bonsai/mm.mk index 124d284..891dae3 100755 --- a/tests/bonsai/mm.mk +++ b/tests/bonsai/mm.mk @@ -6,7 +6,7 @@ # notice are preserved. This file is offered as-is, without any warranty. .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 mm_none: $(BIN)/mm @@ -32,3 +32,9 @@ mm_remaining: $(BIN)/mm test "$$($(BIN)/mm -i README COPYING)" = "$$(cat README COPYING)" $(BIN)/mm -i README -o /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" = diff --git a/tests/bonsai/rpn.mk b/tests/bonsai/rpn.mk index 6fecab9..297295c 100755 --- a/tests/bonsai/rpn.mk +++ b/tests/bonsai/rpn.mk @@ -6,7 +6,7 @@ # notice are preserved. This file is offered as-is, without any warranty. .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 rpn_help: $(BIN)/rpn @@ -41,3 +41,8 @@ rpn_mod: $(BIN)/rpn rpn_flr: $(BIN)/rpn test "$$($(BIN)/rpn 12 5 //)" -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