27 Commits

Author SHA1 Message Date
62ce288524 rpn(1): makes rounding more efficient 2024-09-04 22:12:44 -06:00
731e62ee7e rpn(1): made evaluation function more readable 2024-08-28 18:24:49 -06:00
f50bfeea92 fop(1): fixes panic on no arguments 2024-08-28 18:05:56 -06:00
8d00fa0afd fop(1): minor formatting change 2024-08-28 00:46:32 -06:00
9f520df82b strcmp(1): adds null unveil 2024-08-28 00:31:02 -06:00
41982c2f73 str(1): adds null unveil 2024-08-28 00:30:45 -06:00
6166a3ca36 npc(1): adds null unveil 2024-08-28 00:26:26 -06:00
efedcd3ae4 true(1), false(1): adds null unveils 2024-08-28 00:24:35 -06:00
8fd5bdf5a6 swab(1): adds null unveil 2024-08-28 00:15:15 -06:00
b946469da5 rpn(1): adds null unveil 2024-08-28 00:13:21 -06:00
06384c72fb intcmp(1): adds null unveil 2024-08-28 00:09:40 -06:00
9aeadd8f8f hru(1): adds null unveil 2024-08-27 23:57:12 -06:00
5b666d6858 fop(1): adds null unveil 2024-08-27 23:54:39 -06:00
b875aa1058 rpn(1): more improvements 2024-08-24 19:00:01 -06:00
cc53dab035 rpn(1): handles errors when writing to stdout 2024-08-24 18:18:27 -06:00
232629f4d3 hru(1): makes more efficient 2024-08-24 17:55:00 -06:00
ff4ff825bd swab(1): changes disparate error handling functions to one function 2024-08-24 17:26:26 -06:00
5eb71e90a3 tests/bonsai: rpn.mk: tests the standard input 2024-08-24 17:22:57 -06:00
a0138be79e rpn(1): refactor to make code more efficient, readable, and maintainable 2024-08-24 17:22:02 -06:00
150fa22f35 fop(1): changes casts to calls to .into() 2024-08-24 15:57:55 -06:00
f104598164 mm(1): updates error-handling functions and uncovers issue with error reporting 2024-08-24 15:53:58 -06:00
722679247a hru(1): uses new sysexits 2024-08-23 14:31:02 -06:00
4db4160019 swab(1): uses new sysexits 2024-08-23 14:23:11 -06:00
928dce0234 fop(1): uses new sysexits 2024-08-23 14:22:11 -06:00
1fffd3df52 Makefile: sets bindgen to output exit codes as u8 2024-08-23 14:21:43 -06:00
a14b8fb9d7 fop(1): adds functions for error handling 2024-08-23 13:35:30 -06:00
dd39aeff02 mm(1): adds ioerr(), usage(), and oser() functions 2024-08-22 00:22:05 -06:00
18 changed files with 271 additions and 492 deletions

View File

@@ -42,7 +42,7 @@ BIN = build/bin
default: all test default: all test
.PHONY: all .PHONY: all
all: dj false fop hru intcmp mm npc peek rpn scrut str strcmp swab true all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
# keep build/include until bindgen(1) has stdin support # keep build/include until bindgen(1) has stdin support
# https://github.com/rust-lang/rust-bindgen/issues/2703 # https://github.com/rust-lang/rust-bindgen/issues/2703
@@ -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
@@ -137,11 +137,6 @@ npc: build/bin/npc
build/bin/npc: src/npc.c build build/bin/npc: src/npc.c build
$(CC) $(CFLAGS) -o $@ src/npc.c $(CC) $(CFLAGS) -o $@ src/npc.c
.PHONY: peek
peek: build/bin/peek
build/bin/peek: src/peek.c build
$(CC) $(CFLAGS) -o $@ src/peek.c
.PHONY: rpn .PHONY: rpn
rpn: build/bin/rpn rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build rustlibs build/bin/rpn: src/rpn.rs build rustlibs

View File

@@ -1,111 +0,0 @@
.\" Copyright (c) 2023-2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
.\"
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.\"
.TH PEEK 1 2024-08-14 "Harakit X.X.X"
.SH NAME
peek \(en read from the standard input, furtively
.\"
.SH SYNOPSIS
peek
.RB [ -i ]
.\"
.SH DESCRIPTION
Read input from the standard input with terminal echo disabled.
.\"
.SH OPTIONS
.IP \fB-i\fP
Allows input to come from sources other than terminals (pipes).
.\"
.SH DIAGNOSTICS
In the event of an error, a debug message will be printed and the program will
exit with the appropriate
.BR sysexits.h (3)
error code.
In order to ensure the user\(cqs terminal is still usable after premature
termination, the program attempts to handle the SIGINT signal; if it cannot,
an error message is printed and execution continues. If the program is
interrupted, it exits unsuccessfully without an error message.
.\"
.SH RATIONALE
This tool was originally written to accept passwords in shell scripts as an
extremely simple alternative to the GNU Privacy Guard project\(cqs
.BR pinentry (1)
utility.
Accepting input without showing what is being typed is useful when keying in
secrets in public settings or in places where surveillance cameras are
installed.
.\"
.SH CAVEATS
This program does nothing to prevent others from seeing the key presses input to
a keyboard. It also does not protect against the sound of typing being analyzed
to determine what was input without needing to see screen or keyboard.
Accepting secrets in shell scripts is probably not advisable.
On systems that support it, the
.BR ioctl (2)
command TIOCSTI can be used to insert characters into the standard input. This
doesn't allow snooping but can be used for general mischief.
.\"
.SH EXAMPLES
This is an
.BR sh (1p)
command line that hashes a given password. It uses
.BR head (1p)
to only accept one line of input,
.BR xargs (1p)
and
.BR printf (1p)
to strip the trailing newline,
.BR htpasswd (1)
from Apache\(cqs utilities to hash the input with the bcrypt algorithm, and
.BR cut (1p)
to print only the resulting hash:
.RS
$ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
.RE
This is an
.BR sh (1p)
command line that allows a user to write blindly into a text file but displaying
only written lines. Some writers have the habit of prematurely revising their
work and use tools with functionality similar to this to prevent it.
It uses
.BR mm (1)
to pipe the output of the program to both the standard error and the regular
file writing.txt:
.RS
$ echo Input ^D to quit. && peek | mm -eo - >writing.txt
.RE
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.\"
.SH SEE ALSO
.BR ioctl (2),
.BR ioctl_tty (2),
.BR read (1p),
.BR sh (1p),
.BR stty (1p)

View File

@@ -8,12 +8,13 @@
*/ */
#ifdef __OpenBSD__ #ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */ # include <unistd.h> /* NULL, pledge(2), unveil(2) */
#endif #endif
int main(void) { int main(void) {
#ifdef __OpenBSD__ #ifdef __OpenBSD__
pledge(NULL, NULL); (void)pledge(NULL, NULL);
(void)unveil(NULL, NULL);
#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), None) {
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: &str) -> 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), None) {
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

@@ -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), None) {
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);
}, },
}; };
@@ -111,8 +118,7 @@ fn main() -> ExitCode {
let perms = UnveilPerms::new(vec!['r']); let perms = UnveilPerms::new(vec!['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);
} }
} }
@@ -120,20 +126,17 @@ fn main() -> ExitCode {
let perms = UnveilPerms::new(vec!['c', 'w']); let perms = UnveilPerms::new(vec!['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", NULL) == -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

@@ -1,147 +0,0 @@
/*
* Copyright (c) 20232024 DTB <trinity@trinity.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
* stdout, EOF, NULL */
#include <stdlib.h> /* exit(3), EXIT_FAILURE */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
#include <unistd.h> /* getopt(3), isatty(3), pledge(2), unveil(2),
* STDIN_FILENO */
char *program_name = "peek";
/* Restores terminal echo; otherwise when a user ^Cs the terminal would
* continue to not display typed text. If sig isn't zero, this will terminate
* the program. */
static void
restore_echo(int sig) {
static struct termios t;
/* Failure isn't reported because this is the termination routine anyway;
* errors will be obvious. */
if (tcgetattr(STDIN_FILENO, &t) == 0) {
t.c_lflag |= ECHO;
(void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &t);
}
if (sig != 0) { exit(EXIT_FAILURE); } /* Terminated by signal. */
return;
}
static int
ioerr(char *argv0) {
perror(argv0);
restore_echo(0);
return EX_IOERR;
}
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s [-i]\n", argv0);
return EX_USAGE;
}
int main(int argc, char *argv[]){
bool is_term; /* Is stdin a terminal? */
bool must_be_term = 1; /* Must it be? */
#ifdef __OpenBSD__
if (pledge("stdio tty unveil", "") != 0 || unveil(NULL, NULL) != 0) {
/* This isn't fatal; these return values could be cast to void just as
* easily. */
(void)perror(argv[0] == NULL ? argv[0] : program_name);
}
#endif
is_term = isatty(STDIN_FILENO);
if (argc > 0) { /* option parsing */
int c;
program_name = argv[0];
while ((c = getopt(argc, argv, "i")) != -1) {
switch (c) {
case 'i': must_be_term = 0; break;
default: return usage(argv[0]);
}
}
if (argc > optind) { return usage(argv[0]); }
}
if (!is_term && must_be_term) {
(void)fprintf(
stderr,
"%s: Must be run in a terminal (specify -i to skip this check)\n",
argv[0]
);
return EX_USAGE;
}
if (is_term) {
{ /* Install signal handler */
/* There isn't a difference in functionality between the signal(2)
* and sigaction(2) methods. sigaction(2) is vastly preferred for
* portability but some older systems only have signal(2). */
/* Errors aren't terminating because the worst that happens is some
* terminal phooeyness if things go awry. */
#if defined _POSIX_C_SOURCE
struct sigaction act = { 0 };
act.sa_handler = restore_echo;
if (sigaction(SIGINT, &act, NULL) != 0) { perror(program_name); }
#else
if (signal(SIGINT, restore_echo) == SIG_ERR) {
perror(program_name);
}
#endif
}
{ /* Banish terminal echo */
/* This terminates when it fails because it's the whole point of
* the program. */
struct termios t;
if (tcgetattr(STDIN_FILENO, &t) != 0) {
return ioerr(program_name);
}
t.c_lflag ^= ECHO;
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &t) != 0) {
return ioerr(program_name);
}
}
}
{ /* Input loop */
int c;
while ((c = fgetc(stdin)) != EOF) {
if (fputc(c, stdout) == EOF) { return ioerr(program_name); }
}
}
if (is_term) { restore_echo(0); }
return EX_OK;
}

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), None) {
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

@@ -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", NULL) == -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", NULL) == -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: &str, 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 {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_IOERR as u8)
} }
fn usage(s: &str) -> ExitCode { 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), None) {
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

@@ -8,11 +8,12 @@
*/ */
#ifdef __OpenBSD__ #ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */ # include <unistd.h> /* NULL, pledge(2), unveil(2) */
#endif #endif
int main(void) { int main(void) {
#ifdef __OpenBSD__ #ifdef __OpenBSD__
pledge(NULL, NULL); (void)pledge(NULL, NULL);
(void)unveil(NULL, NULL);
#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

@@ -1,24 +0,0 @@
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice and this
# notice are preserved. This file is offered as-is, without any warranty.
# Testing peek is hard as it requires visual confirmation that text isn't being
# echoed. These tests don't go that far but are a start, and have already
# caught a bug in -i behavior.
.PHONY: peek_tests
peek_tests: peek_help peek_stdio
.PHONY: peek_help
peek_help: $(BIN)/peek
! $(BIN)/peek -h
.PHONY: peek_stdio
# Test peek -i
peek_stdio: $(BIN)/peek
printf 'Test.\n' \
| $(BIN)/peek -i \
| xargs test 'Test.' =

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