Compare commits

..
29 Commits
Author SHA1 Message Date
trinity ba55c4c8e7 sleep.1: add copyright header 2024-05-03 21:32:10 -06:00
trinity 003f5aeb1f Makefile: add sleep to the all recipe 2024-05-03 21:30:39 -06:00
trinity 327c71c8ae sleep.1: import from trinity/src 2024-05-03 21:29:49 -06:00
trinity b06c1d5488 sleep(1): reduce dependence on sysexits 2024-05-03 21:22:38 -06:00
trinity 82f6c46b64 Makefile: update sleep recipe 2024-05-03 21:22:07 -06:00
trinity 81c657ec3e Merge branch 'main' into sleep 2024-05-03 21:19:19 -06:00
trinity 6132c9bf47 Merge branch 'main' into scrut-sysexits 2024-04-26 19:37:34 -06:00
trinity 919b171400 scrut(1): conditionally include non-POSIX sysexits.h 2024-04-26 19:35:33 -06:00
trinity 8e38db92c7 scrut(1): update copyright header 2024-04-26 19:33:45 -06:00
trinity adc9dbded5 scrut(1): changed return status 1 to EXIT_FAILURE 2024-04-26 19:30:33 -06:00
emma 488351da39 scrut(1): changed return status 0 to EXIT_SUCCESS 2024-04-25 16:46:05 -06:00
trinity 61382c34d9 mm(1): error out when given positional arguments 2024-03-31 22:54:03 -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
trinity 82a941eee3 sleep(1): initial implementation 2024-03-21 20:41:05 -06:00
trinity e674027b3e Makefile: add sleep(1) 2024-03-21 20:40:19 -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
11 changed files with 347 additions and 118 deletions
+47 -32
View File
@@ -9,16 +9,26 @@
# 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
all: dj false fop hru intcmp mm npc rpn scrut sleep str strcmp swab true
build:
# keep build/include until bindgen(1) has stdin support
@@ -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
@@ -100,16 +110,21 @@ 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
build/bin/scrut: src/scrut.c build
$(CC) $(CFLAGS) -o $@ src/scrut.c
.PHONY: sleep
sleep: build/bin/sleep
build/bin/sleep: src/sleep.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/sleep.rs
.PHONY: str
str: build/bin/str
build/bin/str: src/str.c build
+53
View File
@@ -0,0 +1,53 @@
.\" Copyright (c) 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 SLEEP 1
.SH NAME
sleep \(en wait a moment
.SH SYNOPSIS
sleep
.RB [ seconds ]
.SH DESCRIPTION
Sleep waits the given amount of seconds before exiting.
.SH DIAGNOSTICS
Sleep exits successfully when the time specified has elapsed, or unsuccessfully
if the time fails to elapse or if invoked incorrectly. In the latter scenario
sleep will also print a usage synopsis.
.SH BUGS
User may still be tired after invoking sleep.
.SH EXAMPLES
The following invocation sleeps for five seconds.
.RS
.R sleep 5
.RE
The following sh(1p) command line sleeps for five minutes, or for sixty
seconds, five times, using rpn(1) and xargs(1p).
.RS
.R $ rpn 60 5 '*' | xargs sleep
.RE
.SH COPYRIGHT
Copyright (c) 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.SH SEE ALSO
sleep(3)
+111 -57
View File
@@ -46,11 +46,31 @@ struct Io{
/* Additionally, the following global variables are used to store user options.
*/
/* (-a) */ static int align; /* Only the lower 8b are used but align is
* negative if no alignment is being done. */
/* (-c) */ static int count; /* 0 if dj(1) runs until no more reads are
* possible. */
/* ASCII field separator delimited statistics */
static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034";
/* (-H) Human-readable statistics */
/* human-readable statistics */
static char *fmt_human = "%d+%d > %d+%d; %d > %d\n";
/* pointer to chosen formatting */
/* (-H) */ static char *fmt_output; /* fmt_asv (default) or fmt_human (-H) */
/* (-dq) */ static char debug; /*
* -d increments dj -qq | 0 - no diagnostic output whatsoever
* -q decrements dj -q | 1 - typical output without
* | notifications on partial reads or
* | writes
* dj | 2 - typical output (default)
* dj -d | 3 - verbose status messages */
/* (-n) */ static char noerror; /* 0 - exits on partial reads or writes
* (default)
* 1 - retries on partial reads/writes
* (-n) */
/* Non-configurable defaults. */
#define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */
@@ -60,8 +80,19 @@ static char *stdout_name = "<stdout>";
static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */
static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */
/* Macro to set defaults for user-configurable options. */
#define setdefaults do{ \
align = -1; \
count = 0; \
debug = 2; \
fmt_output = fmt_asv; \
noerror = 0; \
ep[0].fl = read_flags; \
Io_setdefaults(&ep[0]); \
ep[1].fl = write_flags; \
Io_setdefaults(&ep[1]); }while(0)
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/* Macro to check if fd is a std* file, e.g. stdin. */
#define fdisstd(fd) \
@@ -73,11 +104,38 @@ static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */
* particular io[2] used in main. Error conditions are not checked because this
* is only used when the program is about to terminate (hence its name). */
#define terminate(io) do{ \
free((io)[0].buf); \
free((io)[1].buf); \
Io_buffree(&(io)[0]); \
Io_buffree(&(io)[1]); \
Io_fdclose(&(io)[0]); \
Io_fdclose(&(io)[1]); }while(0)
/* Allocates *io's buffer. Returns NULL if unsuccessful. */
static void *
Io_bufalloc(struct Io *io){
return (io->buf = malloc(io->bs * (sizeof *io->buf)));
}
/* Frees *io's buffer. Returns io. */
static struct Io *
Io_buffree(struct Io *io){
free(io->buf);
return io;
}
/* Fills the unused portion of io's buffer with padding, updating io->bufuse.
* Returns io. */
static struct Io *
Io_bufrpad(struct Io *io, int padding){
memset(io->buf + io->bufuse, padding, io->bs - io->bufuse);
io->bufuse = io->bs;
return io;
}
/* Copies from the buffer in src as much as possible to the free space in the
* dest buffer, removing the copied units from src and permuting the remaining
* units in the src buffer to the start of the buffer, modifying both the src
@@ -109,10 +167,13 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){
}
/* Closes io->fn and returns -1 on error, otherwise io->fd. */
#define Io_fdclose(io) \
(fdisstd((io)->fd) \
? 0 \
: close((io)->fd))
static int
Io_fdclose(struct Io *io){
return fdisstd(io->fd)
? 0
: close(io->fd);
}
/* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd,
* which will be -1 if an error occured. */
@@ -138,6 +199,7 @@ Io_fdopen(struct Io *io, char *fn){
* be set to zero to indicate the seek occurred. */
static int
Io_fdseek(struct Io *io){
int (*op)(int, void *, size_t);
if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)
return -1;
@@ -221,6 +283,19 @@ oserr(char *s){
return EX_OSERR;
}
/* Prints statistics regarding the use of dj, particularly partially and
* completely read and written records, accessing debug, ep, and fmt_output. */
static void
output(void){
if(debug >= 1)
fprintf(stderr, fmt_output,
ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec,
ep[0].bytes, ep[1].bytes);
return;
}
/* Parses the string s to an integer, returning either the integer or in the
* case of an error a negative integer. This is used for argument parsing
* (e.g. -B [int]) in dj and no negative integer would be valid anyway. */
@@ -246,30 +321,11 @@ usage(void){
return EX_USAGE;
}
/* For use in main only.
* Prints statistics regarding the use of dj, particularly partially and
* completely read and written records, accessing ep, and fmt_output. */
#define output fprintf(stderr, fmt_output, \
ep[0].rec, ep[0].prec, ep[1].rec, ep[1].prec, ep[0].bytes, ep[1].bytes \
)
int main(int argc, char *argv[]){
int align; /* Only the lower 8b are used, negative if no alignment. */
int count;
int c, i;
enum { QUIETER = 0, TYPICAL = 1, VERBOSE = 2 } debug;
enum { GIVEUP = 0, RETRY = 1 } error;
char *fmt_output = fmt_asv;
int c;
int i;
ep[0].fl = read_flags;
Io_setdefaults(&ep[0]);
ep[1].fl = write_flags;
Io_setdefaults(&ep[1]);
align = -1;
count = 0;
debug = TYPICAL;
error = GIVEUP;
setdefaults;
if(argc > 0){
program_name = argv[0];
@@ -286,10 +342,10 @@ int main(int argc, char *argv[]){
terminate(ep);
return oserr(optarg);
case 'A': align = '\0'; break;
case 'd': debug = VERBOSE; break;
case 'n': error = RETRY; break;
case 'd': ++debug; break;
case 'n': noerror = 1; break;
case 'H': fmt_output = fmt_human; break;
case 'q': debug = QUIETER; break;
case 'q': --debug; break;
case 'a':
if(optarg[0] != '\0' && optarg[1] == '\0'){
align = optarg[0];
@@ -311,15 +367,14 @@ int main(int argc, char *argv[]){
}
}
if(debug >= VERBOSE)
if(debug >= 3)
fprintf(stderr,
"argv0=%s\n"
"in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n"
"out=%s\tobs=%d\tseek=%ld\tdebug=%2d\terror=%s\n",
"out=%s\tobs=%d\tseek=%ld\tdebug=%2d\tnoerror=%d\n",
program_name,
ep[0].fn, ep[0].bs, ep[0].seek, align, count,
ep[1].fn, ep[1].bs, ep[1].seek, debug,
error == GIVEUP ? "GIVEUP" : "RETRY");
ep[1].fn, ep[1].bs, ep[1].seek, debug, noerror);
if(argc > optind){
terminate(ep);
@@ -327,37 +382,36 @@ int main(int argc, char *argv[]){
}
for(i = 0; i <= 1; ++i){
if((ep[i].buf = malloc(ep[i].bs * (sizeof ep[i].buf))) == NULL){
if(Io_bufalloc(&ep[i]) == NULL){
fprintf(stderr, "%s: Failed to allocate %d bytes\n",
program_name, ep[i].bs * (sizeof ep[i].buf));
program_name, ep[i].bs);
terminate(ep);
return EX_OSERR;
}else if(ep[i].seek > 0 && (c = Io_fdseek(&ep[i])) != -1){
output;
}else if(ep[i].seek > 0)
switch(Io_fdseek(&ep[i])){
case EX_OK:
output();
terminate(ep);
return c;
return EX_OK;
}
}
do{ /* read */
Io_read(&ep[0]);
if(error == RETRY && ep[0].bufuse == 0)
if(!noerror && ep[0].bufuse == 0)
Io_read(&ep[0]); /* second chance */
if(ep[0].bufuse == 0) /* that's all she wrote */
break;
else if(ep[0].bufuse < ep[0].bs){
++ep[0].prec;
if(debug >= TYPICAL){
if(debug >= 2){
fprintf(stderr, "%s: Partial read:\n\t", program_name);
output;
output();
}
if(error == GIVEUP)
if(!noerror)
count = 1;
if(align >= 0){
memset(ep[0].buf + ep[0].bufuse, align,
ep[0].bs - ep[0].bufuse);
ep[0].bufuse = ep[0].bs;
}
if(align >= 0)
Io_bufrpad(&ep[0], align);
}else
++ep[0].rec;
@@ -371,18 +425,18 @@ int main(int argc, char *argv[]){
c = ep[1].bufuse;
Io_write(&ep[1]);
if(error == GIVEUP && ep[1].bufuse == c)
if(!noerror && ep[1].bufuse == c)
Io_write(&ep[1]); /* second chance */
if(c == ep[1].bufuse){ /* no more love */
count = 1;
break;
}else if(c > ep[1].bufuse && ep[1].bufuse > 0){
ep[1].prec += 1;
if(debug >= TYPICAL){
if(debug >= 2){
fprintf(stderr, "%s: Partial write:\n\t", program_name);
output;
output();
}
if(error == GIVEUP)
if(!noerror)
count = 1;
}else if(ep[1].bufuse == 0 && c < ep[1].bs)
++ep[1].prec;
@@ -391,7 +445,7 @@ int main(int argc, char *argv[]){
}while(ep[0].bufuse > 0);
}while(count == 0 || --count > 0);
output;
output();
terminate(ep);
return EX_OK;
+9 -7
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);
});
@@ -112,7 +114,7 @@ fn main() {
stdout().write_all(
fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e| {
eprintln!("{}: {}.", argv[0], 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);
});
}
+15 -3
View File
@@ -106,6 +106,15 @@ oserr(char *s, char *r){
} \
return retval
/* Prints a usage text, in which s is the program being run (i.e. argv[0]), and
* returns an exit status appropriate for a usage error. */
int usage(char *s){
fprintf(stderr, "Usage: %s (-aenu) (-i [input])... (-o [output])...\n", s);
return EX_USAGE;
}
int main(int argc, char *argv[]){
int c;
struct Files files[2]; /* {read, write} */
@@ -178,9 +187,12 @@ int main(int argc, char *argv[]){
k = 1;
break;
default:
fprintf(stderr, "Usage: %s (-aenu) (-i [input])..."
" (-o [output])...\n", argv[0]);
retval = EX_USAGE;
retval = usage(argv[0]);
terminate;
}
if(optind != argc){
retval = usage(argv[0]);
terminate;
}
+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,
})
}
+9 -6
View File
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 20232024 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify it under
@@ -17,13 +18,15 @@
*/
#include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */
#ifndef EX_USAGE
# include <sysexits.h>
#endif
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
#include <sys/stat.h> /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR,
* S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
* S_ISUID, S_ISVTX */
#include <sysexits.h>
static char args[] = "bcdefghkprsuwxLS";
static char ops[(sizeof args) / (sizeof *args)];
@@ -57,7 +60,7 @@ int main(int argc, char *argv[]){
argv += optind;
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
return 1; /* doesn't exist or isn't stattable */
return EXIT_FAILURE; /* doesn't exist or isn't stattable */
for(i = 0; ops[i] != '\0'; ++i)
if(ops[i] == 'e')
@@ -97,8 +100,8 @@ usage: fprintf(stderr, "Usage: %s (-%s) [file...]\n",
&& !S_ISLNK(buf.st_mode))
|| (ops[i] == 'S'
&& !S_ISSOCK(buf.st_mode)))
return 1;
return EXIT_FAILURE;
}while(*++argv != NULL);
return 0;
return EXIT_SUCCESS;
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright (c) 2024 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/.
*/
use std::{
env::args,
process::ExitCode,
thread::sleep,
time::Duration
};
extern crate sysexits;
use sysexits::EX_USAGE;
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [seconds]", s);
ExitCode::from(EX_USAGE as u8)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if argv.len() == 2 {
if let Ok(s) = argv[1].parse::<u64>() {
sleep(Duration::from_secs(s));
ExitCode::SUCCESS
} else {
usage(&argv[0])
}
} else {
usage(&argv[0])
}
}
+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);
});
}