Compare commits

...
20 Commits
Author SHA1 Message Date
trinity 6d3f084d9c peek(1): remove unused variables, clean up some typos 2024-04-18 10:14:52 -06:00
trinity c295f531cb peek(1): correct grammar 2024-04-18 10:04:26 -06:00
trinity c7739612a7 peek(1): strip down to bare essentials, rewrite man page to match 2024-04-18 10:03:02 -06:00
trinity 46b607384a peek(1): remove -p 2024-04-18 09:08:08 -06:00
trinity 4e5fc680e9 Import peek(1) from trinity/src 2024-04-17 16:37:54 -06:00
emma 8d9ac33566 Merge branch 'strerror' 2024-03-29 19:23:27 -06:00
emma b503d97625 Makefile: directory specification changes 2024-03-29 19:22:55 -06:00
emma d87b5d0958 hru(1), fop(1): changed to reflect strerror changes 2024-03-18 21:31:25 -06:00
emma 1891c3e1aa strerror(3): created strerror() method 2024-03-18 21:30:43 -06:00
emma b356ac522f strerror(3): changed panic to 0 2024-03-18 20:45:53 -06:00
emma b2d56bbc9a Makefile: even more changes 2024-03-09 22:02:19 -07:00
emma 2ad7140e1e Makefile: DESTDIR 2024-03-09 11:53:03 -07:00
emma 8193e471f0 changes redundant .unwrap_or_else(panic!()) to .unwrap() 2024-03-03 17:26:40 -07:00
emma c392dbc680 Makefile: better deps; fop(1), hru(1), strerror(3): changed strerror wrapper function name 2024-03-02 10:16:32 -07:00
emma 898044cd43 Makefile: better macro assignment 2024-03-01 23:51:36 -07:00
emma ca01ca4074 rpn(1): remove erroneous strerror(3) dependency 2024-03-01 23:14:58 -07:00
emma 3e6dc5cc46 rpn(1): make error messages consistent with other tools 2024-03-01 23:14:18 -07:00
emma cf1d16f860 hru(1): changed to use strerror(3) 2024-03-01 23:12:44 -07:00
emma 8f956d775c fop(1): changed to use strerror(3) 2024-03-01 23:10:28 -07:00
emma e81703c6e1 sterror(3): added library for C-like error messages; Makefile: some efficiency changes 2024-03-01 23:04:53 -07:00
8 changed files with 311 additions and 46 deletions
+45 -33
View File
@@ -9,13 +9,23 @@
# notice are preserved. This file is offered as-is, without any warranty.
.POSIX:
# if using BSD make(1), remove these pragmas because they break it
.PRAGMA: posix_202x # future POSIX standard support à la pdpmake(1)
.PRAGMA: command_comment # breaks without this?
PREFIX=/usr/local
DESTDIR ?= dist
PREFIX ?= /usr/local
CC=cc
RUSTC=rustc
SYSEXITS != printf '\043include <sysexits.h>\n' | cpp -M - | sed 's/ /\n/g' \
| sed -n 's/sysexits\.h//p' || printf 'include\n'
CC ?= cc
RUSTC ?= rustc
RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
--extern strerror=build/o/libstrerror.rlib
CFLAGS += -I$(SYSEXITS)
.PHONY: all
all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
@@ -27,36 +37,40 @@ build:
.PHONY: clean
clean:
rm -rf build/ dist/
rm -rf build dist
dist: all
mkdir -p dist/bin dist/share/man/man1
cp build/bin/* dist/bin/
cp docs/*.1 dist/share/man/man1/
mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1
cp build/bin/* $(DESTDIR)/$(PREFIX)/bin
cp docs/*.1 $(DESTDIR)/$(PREFIX)/share/man/man1
.PHONY: install
install: dist
mkdir -p $(PREFIX)
cp -r dist/* $(PREFIX)/
cp -r $(DESTDIR)/* /
.PHONY: test
test: build
tests/posix-compat.sh
$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
build/o/libsysexits.rlib: build
.PHONY: rustlibs
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
build/o/libstrerror.rlib
build/o/libgetopt.rlib: build src/getopt-rs/lib.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
-o $@ src/getopt-rs/lib.rs
build/o/libstrerror.rlib: build src/strerror.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib -o $@ \
src/strerror.rs
build/o/libsysexits.rlib: build $(SYSEXITS)sysexits.h
# bandage solution until bindgen(1) gets stdin support
printf '#define EXIT_FAILURE 1\n' | cat - include/sysexits.h \
printf '#define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h \
> build/include/sysexits.h
bindgen --default-macro-constant-type signed --use-core --formatter=none \
"$$(printf '#include <sysexits.h>\n' \
| cpp -M -idirafter "build/include" - \
| sed 's/ /\n/g' | grep sysexits.h)" \
| $(RUSTC) $(RUSTFLAGS) --crate-type lib -o build/o/libsysexits.rlib -
build/o/libgetopt.rlib: src/getopt-rs/lib.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
-o build/o/libgetopt.rlib src/getopt-rs/lib.rs
build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
.PHONY: dj
dj: build/bin/dj
@@ -70,17 +84,13 @@ build/bin/false: src/false.c build
.PHONY: fop
fop: build/bin/fop
build/bin/fop: src/fop.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/fop.rs
build/bin/fop: src/fop.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fop.rs
.PHONY: hru
hru: build/bin/hru
build/bin/hru: src/hru.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/hru.rs
build/bin/hru: src/hru.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/hru.rs
.PHONY: intcmp
intcmp: build/bin/intcmp
@@ -92,18 +102,20 @@ mm: build/bin/mm
build/bin/mm: src/mm.c build
$(CC) $(CFLAGS) -o $@ src/mm.c
.PHONY: npc
npc: build/bin/npc
build/bin/npc: src/npc.c build
$(CC) $(CFLAGAS) -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
rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/rpn.rs
build/bin/rpn: src/rpn.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/rpn.rs
.PHONY: scrut
scrut: build/bin/scrut
+90
View File
@@ -0,0 +1,90 @@
.\" Copyright (c) 2023-2024 DTB <trinity@trinity.moe>
.\"
.\" 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
.SH NAME
peek \(en read from the standard input, furtively
.SH SYNOPSIS
peek
.RB ( -i )
.SH DESCRIPTION
Peek reads input from the standard input with terminal echo disabled.
.SH OPTIONS
.B -i
.RS
Allows input to come from sources other than terminals (i.e. a pipe).
.RE
.SH DIAGNOSTICS
Peek prints an error message and exits with the appropriate status from
sysexits.h(3) if it encounters an unrecoverable error.
Peek tries to handle the signal SIGINT (^C) to ensure the user's terminal is
still usable after premature termination; if the signal can't be handled, it
prints an error message and continues. If peek 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's pinentry(1).
Accepting input without showing what is being typed is useful when keying in
secrets in public settings or places with installed surveillance cameras.
.SH BUGS
This does nothing to prevent others seeing the keyboard being used to input
secrets or mask the sound of typing. Audio or video recordings of typing can be
used to determine what was input without needing to see the characters appear
on the screen.
Accepting secrets in shell scripts is probably not adviseable.
On systems that support it, the ioctl(2) command TIOCSTI can be used to insert
characters into the standard input going to peek. This doesn't allow snooping
but can be used for general mischief.
.SH EXAMPLES
This is an sh(1p) command line that hashes a given password. It uses head(1p)
to only accept one line of input, xargs(1p) and printf(1p) to strip the
trailing newline, htpasswd(1) from Apache's utilities to hash the input with
the bcrypt algorithm, and cut(1p) to print only the resulting hash:
.RS
.R $ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
.RE
This is an sh(1p) command line that lets a user blindly write into a text file,
only able to see written lines. Some writers have the habit of prematurely
revising their work and use tools like this to prevent it. It uses mm(1) to
pipe the output of peek to both the standard error and the regular file
writing.txt.
.RS
.R $ echo Input ^D to quit. && peek | mm -eo - >writing.txt
.RE
.SH AUTHOR
Written by DTB <trinity@trinity.moe>.
.SH COPYRIGHT
Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.SH SEE ALSO
ioctl(2), ioctl_tty(2), read(1), sh(1)
+10 -8
View File
@@ -22,10 +22,12 @@ use std::{
process::{ Command, exit, Stdio },
};
extern crate sysexits;
extern crate getopt;
extern crate strerror;
extern crate sysexits;
use getopt::{ Opt, Parser };
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
fn main() {
@@ -55,7 +57,7 @@ fn main() {
});
let index = argv[index_arg].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: {}.", argv[0], argv[1], e);
eprintln!("{}: {}: {}", argv[0], argv[1], e);
exit(EX_DATAERR);
});
@@ -75,13 +77,13 @@ fn main() {
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else( |e| {
eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_UNAVAILABLE);
});
let field = fields.get(index).unwrap_or_else(|| {
eprintln!(
"{}: {}: No such index in input.",
"{}: {}: No such index in input",
argv[0],
index.to_string(),
);
@@ -94,7 +96,7 @@ fn main() {
}
let output = spawned.wait_with_output().unwrap_or_else(|e| {
eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror());
exit(EX_IOERR);
});
@@ -103,7 +105,7 @@ fn main() {
if replace.pop() != Some(b'\n') { replace = output.stdout; }
let new_field = String::from_utf8(replace).unwrap_or_else(|e| {
eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e);
exit(EX_IOERR);
});
@@ -111,8 +113,8 @@ fn main() {
stdout().write_all(
fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e|{
eprintln!("{}: {}.", argv[0], e);
).unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
});
}
+6 -4
View File
@@ -23,8 +23,10 @@ use std::{
process::{ ExitCode, exit },
};
extern crate strerror;
extern crate sysexits;
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE };
const LIST: [(u32, &str); 10] = [
@@ -49,7 +51,7 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
let c = match 10_u128.checked_pow(n) {
Some(c) => c,
None => {
return Err(format!("10^{}: Integer overflow.", n.to_string()));
return Err(format!("10^{}: Integer overflow", n.to_string()));
},
};
@@ -79,7 +81,7 @@ fn main() -> ExitCode {
f
},
Err(err) => {
eprintln!("{}: {}.", argv[0], err);
eprintln!("{}: {}", argv[0], err);
return ExitCode::from(EX_DATAERR as u8);
},
};
@@ -87,7 +89,7 @@ fn main() -> ExitCode {
let (number, prefix) = match convert(n) {
Ok(x) => x,
Err(err) => {
eprintln!("{}: {}.", argv[0], err);
eprintln!("{}: {}", argv[0], err);
return ExitCode::from(EX_SOFTWARE as u8);
},
};
@@ -98,7 +100,7 @@ fn main() -> ExitCode {
stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes())
.unwrap_or_else(|e| {
eprintln!("{}: {}.", argv[0], e);
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_IOERR);
});
}
+118
View File
@@ -0,0 +1,118 @@
/*
* 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 <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
* stdout, EOF, NULL */
#include <stdlib.h> /* exit(3), EXIT_FAILURE */
#if !defined EX_IOERR || !defined EX_OK || !defined EX_USAGE
# include <sysexits.h>
#endif
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
#include <unistd.h> /* getopt(3), isatty(3), STDIN_FILENO */
static 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;
tcgetattr(STDIN_FILENO, &t);
t.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &t);
/* If, for whatever ungodly reason, exit(3) returns, the user will notice
* their typed characters on the screen. */
if(sig != 0)
exit(EXIT_FAILURE);
return;
}
static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; }
static int usage(char *s){
fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s);
return EX_USAGE;
}
int main(int argc, char *argv[]){
if(argc < 1)
return usage(program_name);
{ /* option parsing */
char allow_nonterminals;
int c;
allow_nonterminals = 0;
while((c = getopt(argc, argv, "i")) != -1)
switch(c){
case 'i': allow_nonterminals = 1; break;
default: return usage(argv[0]);
}
if(argc > optind)
return usage(argv[0]);
if(!allow_nonterminals && isatty(STDIN_FILENO) != 1){
fprintf(stderr, "%s: Must be run in a terminal"
" (option -i skips this check)\n", argv[0]);
return EX_USAGE;
}
}
{ /* 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 configurations can only use 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(argv[0]);
#else
if(signal(SIGINT, restore_echo) == SIG_ERR)
perror(argv[0]);
#endif
}
{ /* banish terminal echo */
struct termios t;
tcgetattr(STDIN_FILENO, &t);
t.c_lflag ^= ECHO;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &t);
}
{ /* actual input loop */
int c;
while((c = fgetc(stdin)) != EOF)
if(fputc(c, stdout) == EOF)
return ioerr(argv[0]);
}
restore_echo(0);
return EX_OK;
}
+1 -1
View File
@@ -172,7 +172,7 @@ fn eval(
};
} else {
return Err(EvaluationError {
message: format!("{}: Unexpected operation.", op),
message: format!("{}: Unexpected operation", op),
code: EX_DATAERR,
})
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* 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.
*/
use std::ffi::{ c_int, c_char, CStr };
pub trait StrError { fn strerror(&self) -> String; }
impl StrError for std::io::Error {
/* wrapper function for use in Rust */
fn strerror(&self) -> String {
/* Get the raw OS error. If its None, what the hell is going on‽ */
let errno = self.raw_os_error().unwrap_or(0) as c_int;
/* Get a CStr from the error message so that its referenced and then
* convert it to an owned value. If the string is not valid UTF-8,
* return that error instead. */
match unsafe { CStr::from_ptr(strerror(errno)) }.to_str() {
Ok(s) => s.to_owned(), // yay!! :D
Err(e) => e.to_string(), // awww :(
}
}
}
/* binding to strerror(3p) */
extern "C" { fn strerror(errnum: c_int) -> *mut c_char; }
+10
View File
@@ -0,0 +1,10 @@
extern crate strerror;
use strerror::raw_message;
fn main() {
stdout.write_all(b"meow\n").unwrap_or_else(|e| {
eprintln!("{}", raw_message(e));
std::process::exit(1);
});
}