diff --git a/Makefile b/Makefile index da7c2ce..68efd20 100644 --- a/Makefile +++ b/Makefile @@ -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 \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 \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 @@ -100,10 +110,8 @@ build/bin/npc: src/npc.c build .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 diff --git a/src/fop.rs b/src/fop.rs index c9a767e..b829602 100644 --- a/src/fop.rs +++ b/src/fop.rs @@ -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::().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); }); } diff --git a/src/hru.rs b/src/hru.rs index 0e0b25d..b7937f7 100644 --- a/src/hru.rs +++ b/src/hru.rs @@ -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); }); } diff --git a/src/rpn.rs b/src/rpn.rs index 0cb23b8..2bfbbf5 100644 --- a/src/rpn.rs +++ b/src/rpn.rs @@ -172,7 +172,7 @@ fn eval( }; } else { return Err(EvaluationError { - message: format!("{}: Unexpected operation.", op), + message: format!("{}: Unexpected operation", op), code: EX_DATAERR, }) } diff --git a/src/strerror.rs b/src/strerror.rs new file mode 100644 index 0000000..e306e7a --- /dev/null +++ b/src/strerror.rs @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 Emma Tebibyte + * 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 it’s 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 it’s 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; } diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..4a602b1 --- /dev/null +++ b/src/test.rs @@ -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); + }); +}