Compare commits

..

1 Commits
main ... dj

Author SHA1 Message Date
DTB
fe1216868b dj(1): refactor source 2024-03-28 21:22:36 -06:00
11 changed files with 159 additions and 313 deletions

View File

@@ -9,28 +9,16 @@
# notice are preserved. This file is offered as-is, without any warranty. # notice are preserved. This file is offered as-is, without any warranty.
.POSIX: .POSIX:
# if using BSD make(1), remove these pragmas because they break it
.PRAGMA: posix_202x # future POSIX standard support à la pdpmake(1) .PRAGMA: posix_202x # future POSIX standard support à la pdpmake(1)
.PRAGMA: command_comment # breaks without this? .PRAGMA: command_comment # breaks without this?
DESTDIR ?= dist PREFIX=/usr/local
PREFIX ?= /usr/local
MANDIR != [ $(PREFIX) = / ] && printf '/usr/share/man\n' \ CC=cc
|| printf '/share/man\n' 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 .PHONY: all
all: dj false fop hru intcmp mm npc rpn scrut sleep str strcmp swab true all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true
build: build:
# keep build/include until bindgen(1) has stdin support # keep build/include until bindgen(1) has stdin support
@@ -39,40 +27,36 @@ build:
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf build dist rm -rf build/ dist/
dist: all dist: all
mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1 mkdir -p dist/bin dist/share/man/man1
cp build/bin/* $(DESTDIR)/$(PREFIX)/bin cp build/bin/* dist/bin/
cp docs/*.1 $(DESTDIR)/$(PREFIX)/$(MANDIR)/man1 cp docs/*.1 dist/share/man/man1/
.PHONY: install .PHONY: install
install: dist install: dist
cp -r $(DESTDIR)/* / mkdir -p $(PREFIX)
cp -r dist/* $(PREFIX)/
.PHONY: test .PHONY: test
test: build test: build
tests/posix-compat.sh tests/posix-compat.sh
$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt $(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
.PHONY: rustlibs build/o/libsysexits.rlib: build
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 # bandage solution until bindgen(1) gets stdin support
printf '#define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h \ printf '#define EXIT_FAILURE 1\n' | cat - include/sysexits.h \
> build/include/sysexits.h > build/include/sysexits.h
bindgen --default-macro-constant-type signed --use-core --formatter=none \ bindgen --default-macro-constant-type signed --use-core --formatter=none \
build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ - "$$(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
.PHONY: dj .PHONY: dj
dj: build/bin/dj dj: build/bin/dj
@@ -86,13 +70,17 @@ build/bin/false: src/false.c build
.PHONY: fop .PHONY: fop
fop: build/bin/fop fop: build/bin/fop
build/bin/fop: src/fop.rs build rustlibs build/bin/fop: src/fop.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fop.rs $(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/fop.rs
.PHONY: hru .PHONY: hru
hru: build/bin/hru hru: build/bin/hru
build/bin/hru: src/hru.rs build rustlibs build/bin/hru: src/hru.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/hru.rs $(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/hru.rs
.PHONY: intcmp .PHONY: intcmp
intcmp: build/bin/intcmp intcmp: build/bin/intcmp
@@ -104,6 +92,7 @@ mm: build/bin/mm
build/bin/mm: src/mm.c build build/bin/mm: src/mm.c build
$(CC) $(CFLAGS) -o $@ src/mm.c $(CC) $(CFLAGS) -o $@ src/mm.c
.PHONY: npc .PHONY: npc
npc: build/bin/npc npc: build/bin/npc
build/bin/npc: src/npc.c build build/bin/npc: src/npc.c build
@@ -111,21 +100,16 @@ build/bin/npc: src/npc.c build
.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 build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/rpn.rs $(RUSTC) $(RUSTFLAGS) \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/rpn.rs
.PHONY: scrut .PHONY: scrut
scrut: build/bin/scrut scrut: build/bin/scrut
build/bin/scrut: src/scrut.c build build/bin/scrut: src/scrut.c build
$(CC) $(CFLAGS) -o $@ src/scrut.c $(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 .PHONY: str
str: build/bin/str str: build/bin/str
build/bin/str: src/str.c build build/bin/str: src/str.c build

57
README
View File

@@ -1,27 +1,27 @@
For the Bonsai coreutils, a better Unix toolset, please visit “Seek not to walk the path of the masters; seek what they sought.”
<https://git.tebibyte.media/bonsai/coreutils>. Matsuo Basho
wwwww / The Bonsai core utilities are the result of the careful examination of the
wWWWWWw /_ _____ _ * "It's not a very good tree, but current state of POSIX and Unix utilies. The Unix Philosophy, “do one thing and
w|||w / \/ \ / | /\ / /|/ occasionally it bears fruit!" do it well” is its core but these tools do not cling to the names of the past.
_,|||._ _/\__/|__// /_/_|_/_//|__
bonsix : "BOHN zix" : "Bonsai" but "POSIX"
The excellent Bonsai core utilities are the result of the careful examination The era of the original Unix tools has been long and fruitful, but they have
of the current state of POSIX and Unix utilities, ironing out wrinkles in the their flaws. The new, non-POSIX era of this project started with frustration
implementations that ended up being worn by the POSIX standard. Bonsai's with the way certain tools work and how other projects that extend POSIX dont
guiding quote is this one from Matsuo Basho: make anything better.
"Seek not to walk the path of the masters; seek what they sought." This project will not follow in the footsteps of GNU; extensions of POSIX will
not be found here. GNU extensions are a gateway to the misuse of the shell. The
Bonsai core utilities will intentionally discourage use of the shell for
purposes beyond its scope.
Unlike Bonsai, Bonsix intends to walk the path of the masters, implementing a See docs/ for more on the specific utilities currently implemented.
POSIX userland under an AGPLv3 license and taking inspiration from the
practices used by Bonsai.
Building Building
Bonsix requires an existing POSIX-compliant environment to compile, including a The coreutils require a POSIX-compliant environment to compile, including a C
C compiler and preprocessor (cc(1) and cpp(1) by default) and a POSIX-compliant compiler and preprocessor (cc(1) and cpp(1) by default) with the -idirafter
flag, a Rust compiler (rustc(1) by default), bindgen(1), and a POSIX-compliant
make(1) utility. make(1) utility.
To build and install: To build and install:
@@ -32,6 +32,29 @@ $ make PREFIX="/your/preferred/location" install
To build with a different compiler than the default: To build with a different compiler than the default:
$ make CC=clang $ make CC=clang
$ make RUSTC=gccrs
To test the utilities:
$ make test
To remove all untracked files:
$ make clean
Read More
An Introduction to the Unix Shell
<https://porkmail.org/era/unix/shell>
Master Foo and the Ten Thousand Lines
<http://www.catb.org/~esr/writings/unix-koans/ten-thousand.html>
Master Foo Discourses on the Unix-Nature
<http://www.catb.org/~esr/writings/unix-koans/unix-nature.html>
Shell Programming!
<https://tldp.org/LDP/abs/html/why-shell.html>
-- --
Copyright © 20232024 Emma Tebibyte <emma@tebibyte.media> Copyright © 20232024 Emma Tebibyte <emma@tebibyte.media>

182
src/dj.c
View File

@@ -46,31 +46,11 @@ struct Io{
/* Additionally, the following global variables are used to store user options. /* 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 */ /* ASCII field separator delimited statistics */
static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034"; static char *fmt_asv = "%d\037%d\036%d\037%d\035%d\036%d\034";
/* 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; /* /* (-H) Human-readable statistics */
* -d increments dj -qq | 0 - no diagnostic output whatsoever static char *fmt_human = "%d+%d > %d+%d; %d > %d\n";
* -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. */ /* Non-configurable defaults. */
#define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */ #define bs_default 1024 /* GNU dd(1) default; twice POSIX but a neat 2^10 */
@@ -80,19 +60,8 @@ static char *stdout_name = "<stdout>";
static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */ static int read_flags = O_RDONLY; /* These flags are consistent with Busybox */
static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */ 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 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. */ /* Macro to check if fd is a std* file, e.g. stdin. */
#define fdisstd(fd) \ #define fdisstd(fd) \
@@ -104,43 +73,16 @@ static int write_flags = O_WRONLY | O_CREAT; /* dd(1). */
* particular io[2] used in main. Error conditions are not checked because this * 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). */ * is only used when the program is about to terminate (hence its name). */
#define terminate(io) do{ \ #define terminate(io) do{ \
Io_buffree(&(io)[0]); \ free((io)[0].buf); \
Io_buffree(&(io)[1]); \ free((io)[1].buf); \
Io_fdclose(&(io)[0]); \ Io_fdclose(&(io)[0]); \
Io_fdclose(&(io)[1]); }while(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 /* 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 * 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 * units in the src buffer to the start of the buffer, modifying both the src
* and dest bufuse and returning dest. */ * and dest bufuse and returning dest. */
static struct Io* static struct Io *
Io_bufxapp(struct Io *dest, struct Io *src){ Io_bufxapp(struct Io *dest, struct Io *src){
int n; int n;
@@ -157,7 +99,7 @@ Io_bufxapp(struct Io *dest, struct Io *src){
* removing the copied units from src and permuting the remaining units in the * 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 and dest * src buffer to the start of the buffer, modifying both the src and dest
* bufuse and returning dest. */ * bufuse and returning dest. */
static struct Io* static struct Io *
Io_bufxfer(struct Io *dest, struct Io *src, int n){ Io_bufxfer(struct Io *dest, struct Io *src, int n){
memcpy(dest->buf, src->buf, (dest->bufuse = n)); memcpy(dest->buf, src->buf, (dest->bufuse = n));
@@ -167,13 +109,10 @@ Io_bufxfer(struct Io *dest, struct Io *src, int n){
} }
/* Closes io->fn and returns -1 on error, otherwise io->fd. */ /* Closes io->fn and returns -1 on error, otherwise io->fd. */
static int #define Io_fdclose(io) \
Io_fdclose(struct Io *io){ (fdisstd((io)->fd) \
? 0 \
return fdisstd(io->fd) : close((io)->fd))
? 0
: close(io->fd);
}
/* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd, /* Opens io->fn and saves the file descriptor into io->fd. Returns io->fd,
* which will be -1 if an error occured. */ * which will be -1 if an error occured. */
@@ -199,8 +138,7 @@ Io_fdopen(struct Io *io, char *fn){
* be set to zero to indicate the seek occurred. */ * be set to zero to indicate the seek occurred. */
static int static int
Io_fdseek(struct Io *io){ Io_fdseek(struct Io *io){
int (*op)(int, void *, size_t);
if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1) if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)
return -1; return -1;
@@ -283,19 +221,6 @@ oserr(char *s){
return EX_OSERR; 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 /* 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 * 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. */ * (e.g. -B [int]) in dj and no negative integer would be valid anyway. */
@@ -321,11 +246,30 @@ usage(void){
return EX_USAGE; return EX_USAGE;
} }
int main(int argc, char *argv[]){ /* For use in main only.
int c; * Prints statistics regarding the use of dj, particularly partially and
int i; * 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 \
)
setdefaults; 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;
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;
if(argc > 0){ if(argc > 0){
program_name = argv[0]; program_name = argv[0];
@@ -342,10 +286,10 @@ int main(int argc, char *argv[]){
terminate(ep); terminate(ep);
return oserr(optarg); return oserr(optarg);
case 'A': align = '\0'; break; case 'A': align = '\0'; break;
case 'd': ++debug; break; case 'd': debug = VERBOSE; break;
case 'n': noerror = 1; break; case 'n': error = RETRY; break;
case 'H': fmt_output = fmt_human; break; case 'H': fmt_output = fmt_human; break;
case 'q': --debug; break; case 'q': debug = QUIETER; break;
case 'a': case 'a':
if(optarg[0] != '\0' && optarg[1] == '\0'){ if(optarg[0] != '\0' && optarg[1] == '\0'){
align = optarg[0]; align = optarg[0];
@@ -367,14 +311,15 @@ int main(int argc, char *argv[]){
} }
} }
if(debug >= 3) if(debug >= VERBOSE)
fprintf(stderr, fprintf(stderr,
"argv0=%s\n" "argv0=%s\n"
"in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n" "in=%s\tibs=%d\tskip=%ld\talign=%hhx\tcount=%d\n"
"out=%s\tobs=%d\tseek=%ld\tdebug=%2d\tnoerror=%d\n", "out=%s\tobs=%d\tseek=%ld\tdebug=%2d\terror=%s\n",
program_name, program_name,
ep[0].fn, ep[0].bs, ep[0].seek, align, count, ep[0].fn, ep[0].bs, ep[0].seek, align, count,
ep[1].fn, ep[1].bs, ep[1].seek, debug, noerror); ep[1].fn, ep[1].bs, ep[1].seek, debug,
error == GIVEUP ? "GIVEUP" : "RETRY");
if(argc > optind){ if(argc > optind){
terminate(ep); terminate(ep);
@@ -382,36 +327,37 @@ int main(int argc, char *argv[]){
} }
for(i = 0; i <= 1; ++i){ for(i = 0; i <= 1; ++i){
if(Io_bufalloc(&ep[i]) == NULL){ if((ep[i].buf = malloc(ep[i].bs * (sizeof ep[i].buf))) == NULL){
fprintf(stderr, "%s: Failed to allocate %d bytes\n", fprintf(stderr, "%s: Failed to allocate %d bytes\n",
program_name, ep[i].bs); program_name, ep[i].bs * (sizeof ep[i].buf));
terminate(ep); terminate(ep);
return EX_OSERR; return EX_OSERR;
}else if(ep[i].seek > 0) }else if(ep[i].seek > 0 && (c = Io_fdseek(&ep[i])) != -1){
switch(Io_fdseek(&ep[i])){ output;
case EX_OK: terminate(ep);
output(); return c;
terminate(ep); }
return EX_OK;
}
} }
do{ /* read */ do{ /* read */
Io_read(&ep[0]); Io_read(&ep[0]);
if(!noerror && ep[0].bufuse == 0) if(error == RETRY && ep[0].bufuse == 0)
Io_read(&ep[0]); /* second chance */ Io_read(&ep[0]); /* second chance */
if(ep[0].bufuse == 0) /* that's all she wrote */ if(ep[0].bufuse == 0) /* that's all she wrote */
break; break;
else if(ep[0].bufuse < ep[0].bs){ else if(ep[0].bufuse < ep[0].bs){
++ep[0].prec; ++ep[0].prec;
if(debug >= 2){ if(debug >= TYPICAL){
fprintf(stderr, "%s: Partial read:\n\t", program_name); fprintf(stderr, "%s: Partial read:\n\t", program_name);
output(); output;
} }
if(!noerror) if(error == GIVEUP)
count = 1; count = 1;
if(align >= 0) if(align >= 0){
Io_bufrpad(&ep[0], align); memset(ep[0].buf + ep[0].bufuse, align,
ep[0].bs - ep[0].bufuse);
ep[0].bufuse = ep[0].bs;
}
}else }else
++ep[0].rec; ++ep[0].rec;
@@ -425,18 +371,18 @@ int main(int argc, char *argv[]){
c = ep[1].bufuse; c = ep[1].bufuse;
Io_write(&ep[1]); Io_write(&ep[1]);
if(!noerror && ep[1].bufuse == c) if(error == GIVEUP && ep[1].bufuse == c)
Io_write(&ep[1]); /* second chance */ Io_write(&ep[1]); /* second chance */
if(c == ep[1].bufuse){ /* no more love */ if(c == ep[1].bufuse){ /* no more love */
count = 1; count = 1;
break; break;
}else if(c > ep[1].bufuse && ep[1].bufuse > 0){ }else if(c > ep[1].bufuse && ep[1].bufuse > 0){
ep[1].prec += 1; ep[1].prec += 1;
if(debug >= 2){ if(debug >= TYPICAL){
fprintf(stderr, "%s: Partial write:\n\t", program_name); fprintf(stderr, "%s: Partial write:\n\t", program_name);
output(); output;
} }
if(!noerror) if(error == GIVEUP)
count = 1; count = 1;
}else if(ep[1].bufuse == 0 && c < ep[1].bs) }else if(ep[1].bufuse == 0 && c < ep[1].bs)
++ep[1].prec; ++ep[1].prec;
@@ -445,7 +391,7 @@ int main(int argc, char *argv[]){
}while(ep[0].bufuse > 0); }while(ep[0].bufuse > 0);
}while(count == 0 || --count > 0); }while(count == 0 || --count > 0);
output(); output;
terminate(ep); terminate(ep);
return EX_OK; return EX_OK;

View File

@@ -22,12 +22,10 @@ use std::{
process::{ Command, exit, Stdio }, process::{ Command, exit, Stdio },
}; };
extern crate getopt;
extern crate strerror;
extern crate sysexits; extern crate sysexits;
extern crate getopt;
use getopt::{ Opt, Parser }; use getopt::{ Opt, Parser };
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE }; use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
fn main() { fn main() {
@@ -57,7 +55,7 @@ fn main() {
}); });
let index = argv[index_arg].parse::<usize>().unwrap_or_else(|e| { 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); exit(EX_DATAERR);
}); });
@@ -77,13 +75,13 @@ fn main() {
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.unwrap_or_else( |e| { .unwrap_or_else( |e| {
eprintln!("{}: {}: {}", argv[0], argv[command_arg], e.strerror()); eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
exit(EX_UNAVAILABLE); exit(EX_UNAVAILABLE);
}); });
let field = fields.get(index).unwrap_or_else(|| { let field = fields.get(index).unwrap_or_else(|| {
eprintln!( eprintln!(
"{}: {}: No such index in input", "{}: {}: No such index in input.",
argv[0], argv[0],
index.to_string(), index.to_string(),
); );
@@ -96,7 +94,7 @@ 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()); eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
exit(EX_IOERR); exit(EX_IOERR);
}); });
@@ -105,7 +103,7 @@ fn main() {
if replace.pop() != Some(b'\n') { replace = output.stdout; } if replace.pop() != Some(b'\n') { replace = output.stdout; }
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], argv[command_arg], e);
exit(EX_IOERR); exit(EX_IOERR);
}); });
@@ -113,8 +111,8 @@ 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()); eprintln!("{}: {}.", argv[0], e);
exit(EX_IOERR); exit(EX_IOERR);
}); });
} }

View File

@@ -23,10 +23,8 @@ use std::{
process::{ ExitCode, exit }, process::{ ExitCode, exit },
}; };
extern crate strerror;
extern crate sysexits; extern crate sysexits;
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE }; use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE };
const LIST: [(u32, &str); 10] = [ const LIST: [(u32, &str); 10] = [
@@ -51,7 +49,7 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
let c = match 10_u128.checked_pow(n) { let c = match 10_u128.checked_pow(n) {
Some(c) => c, Some(c) => c,
None => { None => {
return Err(format!("10^{}: Integer overflow", n.to_string())); return Err(format!("10^{}: Integer overflow.", n.to_string()));
}, },
}; };
@@ -81,7 +79,7 @@ fn main() -> ExitCode {
f f
}, },
Err(err) => { Err(err) => {
eprintln!("{}: {}", argv[0], err); eprintln!("{}: {}.", argv[0], err);
return ExitCode::from(EX_DATAERR as u8); return ExitCode::from(EX_DATAERR as u8);
}, },
}; };
@@ -89,7 +87,7 @@ fn main() -> ExitCode {
let (number, prefix) = match convert(n) { let (number, prefix) = match convert(n) {
Ok(x) => x, Ok(x) => x,
Err(err) => { Err(err) => {
eprintln!("{}: {}", argv[0], err); eprintln!("{}: {}.", argv[0], err);
return ExitCode::from(EX_SOFTWARE as u8); return ExitCode::from(EX_SOFTWARE as u8);
}, },
}; };
@@ -100,7 +98,7 @@ fn main() -> ExitCode {
stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes()) stdout().write_all(format!("{} {}\n", out, si_prefix).as_bytes())
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
eprintln!("{}: {}", argv[0], e.strerror()); eprintln!("{}: {}.", argv[0], e);
exit(EX_IOERR); exit(EX_IOERR);
}); });
} }

View File

@@ -106,15 +106,6 @@ oserr(char *s, char *r){
} \ } \
return retval 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 main(int argc, char *argv[]){
int c; int c;
struct Files files[2]; /* {read, write} */ struct Files files[2]; /* {read, write} */
@@ -187,15 +178,12 @@ int main(int argc, char *argv[]){
k = 1; k = 1;
break; break;
default: default:
retval = usage(argv[0]); fprintf(stderr, "Usage: %s (-aenu) (-i [input])..."
" (-o [output])...\n", argv[0]);
retval = EX_USAGE;
terminate; terminate;
} }
if(optind != argc){
retval = usage(argv[0]);
terminate;
}
files[0].s += files[0].s == 0; files[0].s += files[0].s == 0;
files[1].s += files[1].s == 0; files[1].s += files[1].s == 0;

View File

@@ -172,7 +172,7 @@ fn eval(
}; };
} else { } else {
return Err(EvaluationError { return Err(EvaluationError {
message: format!("{}: Unexpected operation", op), message: format!("{}: Unexpected operation.", op),
code: EX_DATAERR, code: EX_DATAERR,
}) })
} }

View File

@@ -1,6 +1,5 @@
/* /*
* Copyright (c) 20232024 DTB <trinity@trinity.moe> * Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: AGPL-3.0-or-later * SPDX-License-Identifier: AGPL-3.0-or-later
* *
* This program is free software: you can redistribute it and/or modify it under * This program is free software: you can redistribute it and/or modify it under
@@ -18,15 +17,13 @@
*/ */
#include <stdio.h> /* fprintf(3), stderr, NULL */ #include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ #include <stdlib.h> /* EXIT_FAILURE */
#include <string.h> /* memset(3), strchr(3) */ #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 <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, #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_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
* S_ISUID, S_ISVTX */ * S_ISUID, S_ISVTX */
#include <sysexits.h>
static char args[] = "bcdefghkprsuwxLS"; static char args[] = "bcdefghkprsuwxLS";
static char ops[(sizeof args) / (sizeof *args)]; static char ops[(sizeof args) / (sizeof *args)];
@@ -60,7 +57,7 @@ int main(int argc, char *argv[]){
argv += optind; argv += optind;
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
return EXIT_FAILURE; /* doesn't exist or isn't stattable */ return 1; /* doesn't exist or isn't stattable */
for(i = 0; ops[i] != '\0'; ++i) for(i = 0; ops[i] != '\0'; ++i)
if(ops[i] == 'e') if(ops[i] == 'e')
@@ -100,8 +97,8 @@ usage: fprintf(stderr, "Usage: %s (-%s) [file...]\n",
&& !S_ISLNK(buf.st_mode)) && !S_ISLNK(buf.st_mode))
|| (ops[i] == 'S' || (ops[i] == 'S'
&& !S_ISSOCK(buf.st_mode))) && !S_ISSOCK(buf.st_mode)))
return EXIT_FAILURE; return 1;
}while(*++argv != NULL); }while(*++argv != NULL);
return EXIT_SUCCESS; return 0;
} }

View File

@@ -1,47 +0,0 @@
/*
* 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])
}
}

View File

@@ -1,31 +0,0 @@
/*
* 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; }

View File

@@ -1,10 +0,0 @@
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);
});
}