Compare commits

..

15 Commits

17 changed files with 235 additions and 176 deletions

View File

@@ -97,9 +97,10 @@ their editor or terminal.
For usage text and help messages, do not implement a -h option. Instead, print
usage information when any erroneous option is specified. Follow the NetBSD
style guide for the usage texts output format [1].
style guide for the usage texts output format [0].
[1] <http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/share/misc/style>
If committing a new utility, please include tests and documentation (see
tests/ and docs/) for the new tool.
If committing a new source file, format the commit message following these
guidelines:
@@ -128,6 +129,7 @@ $ git commit -m 'tool(1): fix #42 & add feature x'
Commit messages should be written in the present tense.
[0] <http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/share/misc/style>
--
This work © 20232024 by Emma Tebibyte is licensed under CC BY-SA 4.0. To view a
copy of this license, visit <http://creativecommons.org/licenses/by-sa/4.0/>

View File

@@ -14,10 +14,11 @@
PREFIX=/usr/local
CC=cc
MAKE=make
RUSTC=rustc
.PHONY: all
all: dj false fop hru intcmp rpn scrut str strcmp swab true
all: dj false fop hru intcmp rpn scrut str strcmp true
build:
# keep build/include until bindgen(1) has stdin support
@@ -39,8 +40,8 @@ install: dist
cp -r dist/* $(PREFIX)/
.PHONY: test
test: build
tests/posix-compat.sh
test: all
tests/test.sh
$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
build/o/libsysexits.rlib: build
@@ -108,13 +109,6 @@ strcmp: build/bin/strcmp
build/bin/strcmp: src/strcmp.c build
$(CC) $(CFLAGS) -o $@ src/strcmp.c
.PHONY: swab
swab: build/bin/swab
build/bin/swab: src/swab.rs build build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/swab.rs
.PHONY: true
true: build/bin/true
build/bin/true: src/true.c build

View File

@@ -1,71 +0,0 @@
.\" 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 swab 1
.SH NAME
swab \(en swap bytes
.SH SYNOPSIS
swab
.RB ( -f )
.RB ( -w
.R [
.B word size
.R ])
.SH USAGE
Swab swaps the latter and former halves of a block of bytes.
.SH EXAMPLES
The following sh(1p) line:
.R printf 'hello world!\n' | swab
Produces the following output:
.R ehll oowlr!d
.SH OPTIONS
The
.B -f
option ignores system call interruptions.
.PP
The
.B -w
option configures the word size; that is, the size in bytes of the block size
on which to operate. By default the word size is 2. The word size must be
cleanly divisible by 2, otherwise the block of bytes being processed can't be
halved.
.SH DIAGNOSTICS
If an error is encountered in input, output, or invocation, a diagnostic
message will be written to standard error and swab will exit with the
appropriate status from sysexits.h(3).
.SH RATIONALE
Swab was modeled after the
.R conv=swab
functionality specified in the POSIX dd utility but additionally allows the
word size to be configured.
.PP
Swab is useful for fixing the endianness of binary files produced on other
machines.
.SH COPYRIGHT
Copyright (c) 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.SH SEE ALSO
dd(1p)

View File

@@ -1,90 +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,
io::{ stdin, stdout, Error, ErrorKind, Read, Write },
process::ExitCode,
vec::Vec
};
extern crate getopt;
use getopt::{ Opt, Parser };
extern crate sysexits;
use sysexits::{ EX_OK, EX_OSERR, EX_USAGE };
fn oserr(s: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", s, e);
ExitCode::from(EX_OSERR as u8)
}
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} (-f) (-w [wordsize])", s);
ExitCode::from(EX_USAGE as u8)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
let mut buf: Vec<u8> = Vec::new();
let mut input = stdin();
let mut output = stdout().lock();
let mut opts = Parser::new(&argv, "fw:");
let mut force = false;
let mut wordsize: usize = 2;
loop {
match opts.next() {
None => break,
Some(opt) =>
match opt {
Ok(Opt('f', None)) => force = true,
Ok(Opt('w', Some(arg))) => {
match arg.parse::<usize>() {
Ok(w) if w % 2 == 0 => { wordsize = w; () },
_ => { return usage(&argv[0]); },
}
},
_ => { return usage(&argv[0]); }
}
}
}
buf.resize(wordsize, 0);
loop {
match input.read(&mut buf) {
Ok(0) => break ExitCode::from(EX_OK as u8),
Ok(v) if v == wordsize => {
let (left, right) = buf.split_at(v/2);
if let Err(e) = output.write(&right)
.and_then(|_| output.write(&left)) {
break oserr(&argv[0], e)
}
},
Ok(v) => {
if let Err(e) = output.write(&buf[..v]) {
break oserr(&argv[0], e)
}
},
Err(e) if e.kind() == ErrorKind::Interrupted && force => continue,
Err(e) => break oserr(&argv[0], e)
}
}
}

27
tests/bonsai/dj.sh Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
. tests/bonsai/test_env
! dj -h
# This test is theoretically Linux-dependent; write(2) should return -1 on
# error.
# Right now dj(1) interprets the return value of write(2) as the amount of
# bytes written. This can decrement the stored quantity of bytes written,
# which is an int, so doesn't underflow but goes negative. dj(1) tries to
# again to write(2) if an error occurs in which no bytes are written, so in
# total two write(2)s are attempted and so the written byte quantity is -2.
# This is a bug and will change, but for now is at least documented.
dj -Hi /dev/zero -o /dev/full \
| xargs -I out "$BIN/strcmp" '1+0 > 0+0; 1024 > -2' out
# Read nothing from /dev/null, write nothing to /dev/null.
dj -Hi /dev/null -o /dev/null \
| xargs -I out "$BIN/strcmp" '0+0 > 0+0; 0 > 0' out

13
tests/bonsai/false.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
. tests/bonsai/test_env
! false
! false -h

21
tests/bonsai/fop.sh Executable file
View File

@@ -0,0 +1,21 @@
#!/bin/sh
# 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.
. tests/bonsai/test_env
! fop -h
"$BIN/strcmp" "$(printf 'test0␞test1␞test2\n' | fop 1 sed 's/1/4/g')" \
'test0␞test4␞test2'
"$BIN/strcmp" "$(printf 'test0 test1 test2\n' | fop -d' ' 2 sed 's/2/4/g')" \
'test0 test1 test4'
! printf 'test\n' | fop 1 cat
! printf 'test\n' | fop 'test' cat
! printf 'test\n' | fop -d'test' cat

26
tests/bonsai/hru.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
# 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.
. tests/bonsai/test_env
alias strcmp="$BIN/strcmp"
alias rpn="$BIN/rpn"
strcmp "$(printf '1234\n' | hru)" '1.2 kB'
strcmp "$(printf '0\n' | hru)" '0 B'
# doesnt currently work but would be useful for testing for regressions
#n=1
#while "$BIN/true"; do
# n="$(rpn "$n" 10 ×)"
#
# printf '%s\n' "$n" | hru || break
#done
#printf 'integer limit: ~%s\n' "$(rpn "$n" 10 ÷)"
! printf '%s\n' '-1' | hru

25
tests/bonsai/intcmp.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
. tests/bonsai/test_env
intcmp -e 3 3 3
intcmp -g 3 2 1
intcmp -l 1 2 3
intcmp -ge 3 3 1
intcmp -le 1 3 3
intcmp -gl 1 2 3
intcmp -egl 3 1 1 2
! intcmp -e 1 2 3
! intcmp -g 1 3 3
! intcmp -l 3 3 1
! intcmp -ge 1 2 3
! intcmp -le 3 2 1
! intcmp -gl 3 3 3
! intcmp -egl foo

19
tests/bonsai/mm.sh Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
# 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.
. tests/bonsai/test_env
exec 3>&1
! mm -h
# mm(1) will error if positional arguments are given without -i or -o
! mm argument
# check if stderr is empty upon specifying -e
! "$BIN/strcmp" "$(printf 'test\n' | mm -i - -e 2>&1 1>&3)" ''

16
tests/bonsai/strcmp.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
. tests/bonsai/test_env
strcmp equals equals
! strcmp inequals equals
strcmp - -
strcmp -h
! strcmp nocmp

5
tests/bonsai/test_env Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -x
alias "$UTIL=$BIN/$UTIL"

13
tests/bonsai/true.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
. tests/bonsai/test_env
true
true -h

22
tests/posix/bin/cat Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
# 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.
# Strictly POSIX-compliant cat(1) implementation. See cat(1p)
for arg in "$@"; do
case "$arg" in
-u) args="$(printf '%s %s\n' "$args" "$arg")" ;;
*) args="$(printf -- '%s -i %s\n' "$args" "$arg")" ;;
esac
done
# See IEEE Std 1003.1-2017 3.282
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_282
IFS=' '
mm $args

12
tests/posix/bin/false Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
# Strictly POSIX-compliant false(1) implementation. See false(1p)
false "$@"

11
tests/posix/bin/true Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
# Strictly POSIX-compliant true(1) implementation. See true(1p)
true "$@"

View File

@@ -9,16 +9,30 @@
set -e
export BIN=build/bin
if ! ls Makefile >/dev/null 2>&1
then
printf '%s: Run this script in the root of the project.\n' "$0" 1>&2
exit 1
fi
printf "Starting POSIX compatibility testing.\n"
printf "Starting Bonsai testing.\n\n"
for utility in tests/posix/*; do
printf '%s: %s: Testing utility.\n' "$0" "$utility"
for script in tests/bonsai/*.sh; do
export UTIL="$(printf '%s\n' "$script" \
| sed -e 's/\.sh//g' -e 's;tests\/bonsai\/;;g')"
printf '%s: %s: Testing utility.\n' "$0" "$UTIL"
"$script"
printf '\n'
done
printf "Starting POSIX compatibility testing.\n\n"
for test in tests/posix/*.sh; do
export PATH="$BIN:$PATH"
printf '%s: %s: Testing utility.\n' "$0" "$test"
"$utility"
printf '\n'
done