91 Commits
pg ... stris

Author SHA1 Message Date
ee9d5be015 stris.1: makes consistent with other docs 2025-02-25 00:02:01 -07:00
DTB
28f6d2508f stris(1): fix silly mistakes 2024-08-28 21:36:32 -06:00
DTB
cbf5032a22 tests: bonsai/stris.mk: broaden stris(1) test coverage 2024-08-28 21:36:02 -06:00
DTB
7d5443dee6 stris(1): fix typo 2024-08-28 18:54:12 -06:00
DTB
f9c84179fe stris(1): use String::default instead of String::new 2024-08-28 18:52:23 -06:00
DTB
72c3ac5df0 stris(1): modernize code 2024-08-28 18:50:21 -06:00
DTB
1796e772fb Merge branch 'main' into stris 2024-08-27 21:29:40 -06:00
e0c985f7ff libopenbsd.rs(3): uses c_char instead of i8 for portability 2024-08-14 00:32:28 -06:00
DTB
4c81516742 strcmp(1): further error-proofing 2024-08-10 22:30:08 -06:00
DTB
98c4d94f6d scrut(1): further error-proofing 2024-08-10 22:24:32 -06:00
DTB
da190f713c npc(1): tweak OpenBSD functions 2024-08-10 22:18:15 -06:00
DTB
10b7f7706b dj(1): tweak OpenBSD functions 2024-08-10 22:16:06 -06:00
DTB
b1a4a1a2b9 dj(1): use entirely-stdio error messages for OpenBSD functions 2024-08-10 22:03:59 -06:00
a693ced9d9 strcmp(1): fixes typo 2024-08-10 19:18:52 -06:00
1003c82d23 swab(1): uses pledge(2) 2024-08-10 19:16:26 -06:00
baa75a2619 strcmp(1): implements use of pledge(2) 2024-08-10 19:09:50 -06:00
d6d9c2088e npc(1): uses perror(3) 2024-08-10 19:09:26 -06:00
ea2efdf5b9 str(1): perror(2) -> perror(3) 2024-08-10 19:08:15 -06:00
6c558654f3 str(1): adds reference to perror(3) in include 2024-08-10 19:01:44 -06:00
8699d04ccc str(1): updates to use pledge(2) 2024-08-10 19:00:32 -06:00
18dfd20937 scrut(1): fixes buffer overflow on i386 systems 2024-08-10 18:57:36 -06:00
42010596de scrut(1): adds support for pledge(2) and unveil(2) 2024-08-10 17:17:42 -06:00
0ddfa6e474 tests: bonsai/rpn.mk: fixes rpn_test target deps 2024-08-10 15:34:41 -06:00
326c8f77d1 rpn(1): adds support for pledge(2) 2024-08-10 15:34:14 -06:00
72ef8d00bc npc(1): adds pledge(2) support 2024-08-10 15:22:07 -06:00
851f729ebd tests: bonsai/mm.mk, bonsai/intcmp.mk: updates for regression testing 2024-08-10 14:24:33 -06:00
e253cdf79c intcmp(1): fixes intcmp without options falling through to integer parsing 2024-08-10 14:23:53 -06:00
d89707a47c mm(1): fixes mm without arguments not working 2024-08-10 14:20:38 -06:00
2a75b8f820 mm(1): removes leftover debug print 2024-08-10 14:09:33 -06:00
bda7c074b0 hru(1): adds support for pledge(2) 2024-08-10 13:34:44 -06:00
2f805cc942 mm(1): adds support for pledge(2) and unveil(2) 2024-08-10 13:29:27 -06:00
70bec49127 libopenbsd.rs(3): fixes API for unveil(2) 2024-08-10 13:25:56 -06:00
eae0b0352b dj(1): fixes small rebase mistake 2024-08-10 13:07:46 -06:00
25eb08eb84 Makefile, include: makes conditional compilation more robust 2024-08-10 13:04:41 -06:00
6c882f54cb fop(1): adds pledge support 2024-08-10 12:51:25 -06:00
cf96a13419 true(1), false(1): adds pledge(2) and unveil(2) support 2024-08-10 12:50:51 -06:00
1f59a9806e dj(1): adds pledge(2) and unveil(2) support 2024-08-10 12:50:15 -06:00
c7c71c725b libopenbsd(3): adds pledge(2) and unveil(2) support for Rust; Makefile, include: adds conditional compilation 2024-08-10 12:49:35 -06:00
b76ff8fd90 tests, Makefile: cleaning up 2024-08-09 23:50:31 -06:00
66f809162b Merge branch 'testing' 2024-08-09 23:28:21 -06:00
7278a8fc41 Makefile: fixes testing import 2024-08-09 18:00:25 -06:00
8e5090d13d tests: README: updates README 2024-08-07 22:03:32 -06:00
eb821715f7 tests: bonsai/hru.mk: fixes hru.mk more 2024-08-07 20:45:26 -06:00
0bc0ffa0a5 tests: bonsai/hru.mk: updates to not use old variables 2024-08-07 20:38:03 -06:00
DTB
a7f16b5a7e swab.1: Add note to keep example in tests 2024-08-07 08:35:24 -06:00
DTB
a94884cc2a tests: bonsai/swab.mk: add example from tha man page 2024-08-07 08:34:29 -06:00
DTB
9412f95cb1 tests: bonsai/str.mk: add str_isalpha test 2024-08-05 21:16:18 -06:00
DTB
74d6ec16ac Merge branch 'testing' of git.tebibyte.media:bonsai/harakit into testing 2024-08-04 13:45:10 -06:00
3880abaa4f tests: bonsai/rpn.mk: added rpn(1) test 2024-08-05 13:47:12 -06:00
DTB
e93d218b87 tests/bonsai: str.mk: -h test 2024-08-04 09:14:19 -06:00
DTB
cd5983b10b tests/bonsai: intcmp.mk: add a comment 2024-08-04 09:05:20 -06:00
DTB
acdbecf178 tests: bonsai/scrut.mk: add tests 2024-08-03 21:36:53 -06:00
DTB
0f2d357476 tests/bonsai: dj.mk: tee diagnostics to stderr instead of tty 2024-08-03 07:33:46 -06:00
DTB
588680406a tests: bonsai/npc.mk: full ASCII test coverage 2024-08-03 07:29:04 -06:00
ee7b7e89b2 tests: bonsai/fop.mk: fixes record separators 2024-08-02 19:37:36 -06:00
DTB
91de98cea3 tests: bonsai/intcmp.mk: make tests more relevant to failure cases 2024-08-02 18:28:24 -06:00
DTB
bd09d16949 tests: bonsai/dj.mk: comment more 2024-08-02 18:22:45 -06:00
ce8a0a5be3 tests: converted to Makefiles 2024-08-02 17:29:30 -06:00
0b0bd9bd76 Makefile: fixes test and removes references to the RUSTLIBS variable 2024-08-01 13:12:54 -06:00
18aac06113 Makefile: changes test usage 2024-08-01 00:06:39 -06:00
4aeba9d13f Makefile: uses dirname(1) to get normalized prefix, adds libraries to RUSTFLAGS, fixes typo 2024-07-29 23:04:08 -06:00
DTB
94873a2ddc tests: bonsai/dj.sh: iron out some existing tests 2024-07-08 22:25:18 -06:00
DTB
aa819cabc2 tests: bonsai/npc.sh: initial arg parsing tests 2024-07-08 22:24:18 -06:00
DTB
7939985c98 Merge branch 'main' into testing 2024-07-08 14:53:47 -06:00
45329ccb8c tests/README: initial commit; tests/posix_env, tests/test.sh: updated to match README 2024-07-07 19:19:38 -06:00
014485d3c5 Merge branch 'main' into testing 2024-06-29 08:05:13 -06:00
5cfccf75af tests: bonsai/test_env: set -e; tests: strcmp: -h causes error 2024-06-07 23:30:13 -06:00
c88c41b213 tests: adds toki pona locale 2024-06-02 20:14:06 -06:00
406feb3dc7 tests: test.sh: fixes locales when run from root of project 2024-05-27 22:37:15 -06:00
787f0dc6e2 tests: test.sh: actually includes localization variables 2024-05-27 22:23:55 -06:00
94ada03ce4 tests: adds easy localization 2024-05-27 22:17:30 -06:00
9bfc587623 tests: posix: add environment script for sourcing in tests 2024-05-27 22:12:54 -06:00
8508479a5b tests: test.sh: remove superfluous printing of test category 2024-05-27 22:07:17 -06:00
DTB
7c9f640ee1 stris(1): fix bug only checking the first rune of strings 2024-05-03 21:36:23 -06:00
DTB
3910c341bd remove str(1) 2024-05-03 21:04:10 -06:00
DTB
cfef7aec1d stris(1) 2024-05-03 21:03:30 -06:00
e028844825 tests: test.sh: utilize $BIN 2024-04-27 15:18:09 -06:00
f022121436 tests: posix: cat(1p): added POSIX-compliant cat 2024-04-27 15:16:52 -06:00
e278307daf tests: bonsai: removed redundant code 2024-04-26 20:55:16 -06:00
4ad9e0da92 tests: bonsai: rename aliases sourced script to test_env 2024-04-25 18:57:05 -06:00
0113cf793d tests: bonsai: hru.sh: adds test 2024-04-25 17:20:55 -06:00
1aa2b596d0 tests: aliases: added set -x 2024-04-24 19:28:33 -06:00
c6f30c4195 tests: bonsai: fop.sh: added test 2024-04-24 19:25:55 -06:00
3398fc372c tests: bonsai: mm.sh: fixed copyright 2024-04-24 15:33:17 -06:00
28f2d44e2f CONTRIBUTING: updated to include docs and tests requirement 2024-04-24 15:24:35 -06:00
aefa87d9e5 tests: fixed aliasing and created mm.sh 2024-04-24 15:22:07 -06:00
057f5571d6 moved tests into test directory 2024-04-24 14:58:35 -06:00
DTB
e7021e127c Testfile: make non-executable 2024-03-13 18:20:30 -06:00
DTB
417d7ca405 Testfile: fix syntax 2024-03-11 21:03:53 -06:00
DTB
f7a74dc430 Testfile 2024-03-11 21:01:29 -06:00
DTB
cabe08bca4 TESTING: start testing document/script 2024-02-29 20:33:09 -07:00
45 changed files with 1154 additions and 561 deletions

View File

@@ -99,9 +99,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:
@@ -130,6 +131,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

@@ -16,23 +16,33 @@
DESTDIR ?= dist
PREFIX ?= /usr/local
# for conditionally compiling OS features
OS != uname
OS_INCLUDE != test -e include/$(OS).mk && printf 'include/$(OS).mk\n' \
|| include/None.mk
# normalized prefix
PREFIX_N != (test -d $(PREFIX) && [ '-' != $(PREFIX) ] \
&& CDPATH= cd -P -- $(PREFIX) && pwd -P)
MANDIR != [ $(PREFIX_N) = / ] && printf '/usr/share/man\n' \
PREFIX_N != dirname $(PREFIX)/.
MANDIR != test $(PREFIX_N) = / && printf '/usr/share/man\n' \
|| printf '/share/man\n'
SYSEXITS != printf '\043include <sysexits.h>\n' | cpp -M - | tr ' ' '\n' \
| 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
RUSTFLAGS += --extern getopt=build/o/libgetopt.rlib \
--extern strerror=build/o/libstrerror.rlib \
--extern sysexits=build/o/libsysexits.rlib
CFLAGS += -I$(SYSEXITS)
# testing requires the absolute path to the bin directory set
BIN = build/bin
.PHONY: default
default: all test
.PHONY: all
all: dj false fop hru intcmp mm npc rpn scroll scrut str strcmp swab true
all: dj false fop hru intcmp mm npc rpn scrut strcmp stris swab true
# keep build/include until bindgen(1) has stdin support
# https://github.com/rust-lang/rust-bindgen/issues/2703
@@ -52,10 +62,12 @@ dist: all docs
install: dist
cp -r $(DESTDIR)/* /
include tests/tests.mk
.PHONY: test
test: build /tmp/getopt
test: all $(TESTS) /tmp/getopt
@echo $(TESTS)
/tmp/getopt
tests/posix-compat.sh
/tmp/getopt: src/libgetopt.rs
$(RUSTC) --test -o /tmp/getopt src/libgetopt.rs
@@ -67,9 +79,12 @@ docs: docs/ build
"s/X\.X\.X/$$(git describe --tags --long | cut -d'-' -f1)/g")"; \
sed "s/$$original/$$title/g" <"$$file" >"build/$$file"; done
# include OS feature libraries for compilation
include $(OS_INCLUDE)
.PHONY: rustlibs
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
build/o/libstrerror.rlib
rustlibs: build/o/libgetopt.rlib build/o/libstrerror.rlib \
build/o/libsysexits.rlib $(OSLIB)
build/o/libgetopt.rlib: build src/libgetopt.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
@@ -100,47 +115,42 @@ build/bin/false: src/false.c build
.PHONY: fop
fop: build/bin/fop
build/bin/fop: src/fop.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fop.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/fop.rs
.PHONY: hru
hru: build/bin/hru
build/bin/hru: src/hru.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/hru.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/hru.rs
.PHONY: intcmp
intcmp: build/bin/intcmp
build/bin/intcmp: src/intcmp.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/intcmp.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/intcmp.rs
.PHONY: mm
mm: build/bin/mm
build/bin/mm: src/mm.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/mm.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/mm.rs
.PHONY: npc
npc: build/bin/npc
build/bin/npc: src/npc.c build
$(CC) $(CFLAGS) -o $@ src/npc.c
.PHONY: scroll
scroll: build/bin/scroll
build/bin/scroll: src/scroll.c build
$(CC) $(CFLAGS) -o $@ src/scroll.c
.PHONY: rpn
rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/rpn.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/rpn.rs
.PHONY: scrut
scrut: build/bin/scrut
build/bin/scrut: src/scrut.c build
$(CC) $(CFLAGS) -o $@ src/scrut.c
.PHONY: str
str: build/bin/str
build/bin/str: src/str.c build
$(CC) $(CFLAGS) -o $@ src/str.c
.PHONY: stris
stris: build/bin/stris
build/bin/stris: src/stris.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) -o $@ src/stris.rs
.PHONY: strcmp
strcmp: build/bin/strcmp
@@ -150,7 +160,7 @@ build/bin/strcmp: src/strcmp.c build
.PHONY: swab
swab: build/bin/swab
build/bin/swab: src/swab.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/swab.rs
$(RUSTC) $(RUSTFLAGS) -o $@ src/swab.rs
.PHONY: true
true: build/bin/true

View File

@@ -1,52 +0,0 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
.\"
.\" 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 PG 1 2024-07-31 "Harakit X.X.X"
.SH NAME
scroll \(en present output
.\"
.SH SYNOPSIS
scroll
.RB ( -p
.RB [ prompt ])
.\"
.SH DESCRIPTION
Print standard input to standard output, accepting commands between pages.
.\"
.SH OPTIONS
.IP -p
Replace the default prompt (\(lq: \(rq) with the option argument.
.\"
.SH DIAGNOSTICS
In the event of an error, a debug message will be printed and the program will
exit with the appropriate
.BR sysexits.h (3)
error code.
.\"
.SH RATIONALE
Plan 9 from Bell Labs had
.BR p (1),
a similar \(lqcooked\(rq-mode paginator (as opposed to \(lqraw\(rq mode, which a
vast majority of paginators use).
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.\"
.SH SEE ALSO
.BR more (1p)

View File

@@ -1,59 +0,0 @@
.\" Copyright (c) 20232024 DTB <trinity@trinity.moe>
.\" Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
.\"
.\" 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 STR 1 2024-06-17 "Harakit X.X.X"
.SH NAME
str \(en test string arguments
.\"
.SH SYNOPSIS
str
.B type string...
.\"
.SH DESCRIPTION
Test the character types of string arguments.
The tests in this program are equivalent to the functions with the same names in
.BR ctype.h (0p)
and are the methods by which string arguments are tested.
.\"
.SH DIAGNOSTICS
If all tests pass, the program will exit successfully. If any of the tests fail,
the program will exit unsuccessfully with an error code of 1.
When invoked incorrectly, a debug message will be printed and the program will
exit with the appropriate
.BR sysexits.h (3)
error code.
.\"
.SH CAVEATS
None of an empty string\(cqs contents pass any of the tests, so the program will
exit unsuccessfully if one is specified.
There\(cqs no way of knowing which argument failed the test without re-testing
arguments individually.
If a character in a string isn\(cqt valid ASCII, the program will exit
unsuccessfully.
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH COPYRIGHT
Copyright \(co 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.\"
.SH SEE ALSO
.BR ctype (3p),
.BR strcmp(1),
.BR ascii(7)

104
docs/stris.1 Normal file
View File

@@ -0,0 +1,104 @@
.\" Copyright (c) 20232024 DTB <trinity@trinity.moe>
.\" Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
.\"
.\" 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 STRIS 1 2024-10-22 "Harakit X.X.X"
.SH NAME
stris \(en test the character types of string arguments
.\"
.SH SYNOPSIS
stris
.RB [ -7bcdlu ]
.RB [ -i\ inclusions ]
.RB strings...
.\"
.SH DESCRIPTION
Test each character in any number of string arguments, ensuring each meets any
of the parameters specified by program options.
.\"
.SH OPTIONS
.IP \fB-7\fP
Tests if the character encoding is ASCII.
.IP \fB-b\fP
Tests if characters are whitespace.
.IP \fB-c\fP
Tests if characters are control characters.
.IP \fB-d\fP
Tests if characters are numeric.
.IP \fB-i\fP \fIinclusions...\fP
In addition to specified options, also permits characters in
.IR inclusions .
.IP \fB-l\fP
Tests if characters are in lower case.
.IP \fB-u\fP
Tests if characters are in upper case.
.\"
.SH DIAGNOSTICS
If no test cases pass for a character in
.IR strings ,
the program will exit unsuccessfully.
.SH CAVEATS
If no options are specified, the program will exit successfully as long as the
input
.I strings
are legibly encoded.
Neither which test failed nor which of the
.I strings
failed a test cannot be known without further invocations of the program.
Characters that can be encoded losslessly into ASCII from UTF-8 but which
are in an \(lqoverlong encoding\(rq, where the character was encoded with
unnecessary leading zeroes causing it to span multiple bytes, won\(cqt be
detected as ASCII.
.\"
.SH EXAMPLES
This is an
.BR sh (1p)
snippet which tests if an environment variable is an ASCII digit.
.RS
stris -7 "$v" && stris -d "$v" && echo ASCII digit.
.RE
This is an
.BR sh (1p)
snippet that tests if an environment variable is a hexadecimal number.
.RS
stris -7 "$v" && stris -di ABCDEFabcdef "$v" && echo Hexadecimal number.
.RE
.\"
.SH AUTHOR
Written by DTB
.MT trinity@trinity.moe
.ME .
.\"
.SH HISTORY
This program replaces the former str(1) which took the name of a function from
.BR ctype (3p)
as its first argument and checked the following strings against it;
.BR str (1)
exited unsuccessfully when it encountered any non-ASCII characters and could
only have one parameter specified.
.\"
.SH COPYRIGHT
Copyright \(co 2023\(en2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.\"
.SH SEE ALSO
.BR ascii (7),
.BR ctype (3p),
.BR strcmp (1)

View File

@@ -33,6 +33,7 @@ line:
.RS
printf 'hello world!\(rsn' | swab
.RE
.\" If you change this, make sure to change it in tests/bonsai/swab.mk too.
Produces the following output:

6
include/FreeBSD.mk Normal file
View File

@@ -0,0 +1,6 @@
# 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.

0
include/None.mk Normal file
View File

13
include/OpenBSD.mk Normal file
View File

@@ -0,0 +1,13 @@
# 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.
OSLIB = build/o/libopenbsd.rlib
RUSTFLAGS += --extern openbsd=$(OSLIB)
$(OSLIB): src/libopenbsd.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=openbsd \
-o $@ src/libopenbsd.rs

View File

@@ -26,7 +26,8 @@
#include <string.h> /* memcpy(3), memmove(3), memset(3) */
#include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#include <unistd.h> /* close(2), getopt(3), lseek(2), read(2), write(2),
* optarg, optind, STDIN_FILENO, STDOUT_FILENO */
* pledge(2), unveil(2), optarg, optind, STDIN_FILENO,
* STDOUT_FILENO */
#include <sys/stat.h> /* S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR */
char *program_name = "dj";
@@ -107,8 +108,10 @@ Io_write(struct Io *io) {
}
static int
oserr(char *e, int n) {
(void)fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n));
oserr(char *e, int n) { /* program_name: [failing component:] error */
(void)fprintf(stderr, "%s: ", program_name);
if (e != NULL) { (void)fprintf(stderr, "%s: ", e); }
(void)fprintf(stderr, "%s\n", strerror(n));
return EX_OSERR;
}
@@ -174,6 +177,12 @@ int main(int argc, char *argv[]) {
bool retry; /* false if exits on partial reads or writes */
struct Io io[2 /* { in, out } */];
#ifdef __OpenBSD__
if (pledge("cpath rpath stdio unveil wpath", NULL) == -1) {
return oserr(NULL, errno);
}
#endif
/* Set defaults. */
align = -1;
count = -1;
@@ -209,6 +218,12 @@ int main(int argc, char *argv[]) {
} else {
int fd;
#ifdef __OpenBSD__
if (unveil(optarg, i == 0 ? "r" : "wc") == -1) {
return oserr(NULL, errno);
}
#endif
if (
(fd = open(optarg, io[i].fl, creat_mode)) != -1
&& (fdisstd(io[i].fd) || close(io[i].fd) == 0)
@@ -249,6 +264,10 @@ int main(int argc, char *argv[]) {
}
}
#ifdef __OpenBSD__
if (unveil(NULL, NULL) == -1) { return oserr(NULL, errno); }
#endif
assert(io->fd != STDIN_FILENO || io->fl == read_flags);
assert(io->fd != STDOUT_FILENO || io->fl == write_flags);

View File

@@ -1,9 +1,18 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: CC0
*
* This work is marked with CC0 1.0. To view a copy of this license, visit
* <http://creativecommons.org/publicdomain/zero/1.0>.
*/
int main() { return 1; }
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
#endif
int main() {
#ifdef __OpenBSD__
pledge(NULL, NULL);
#endif
return 1;
}

View File

@@ -30,11 +30,23 @@ use getopt::GetOpt;
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
fn main() {
let argv = args().collect::<Vec<String>>();
let mut d = '\u{1E}'.to_string(); /* ASCII record separator */
let mut optind = 1;
if cfg!(target_os="openbsd") {
let promises = Promises::new("stdio proc exec");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
exit(EX_OSERR);
}
}
let usage = format!(
"Usage: {} [-d delimiter] index command [args...]",
argv[0],

View File

@@ -27,7 +27,11 @@ extern crate strerror;
extern crate sysexits;
use strerror::StrError;
use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE };
use sysexits::{ EX_DATAERR, EX_IOERR, EX_SOFTWARE, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
/* list of SI prefixes */
const LIST: [(u32, &str); 10] = [
@@ -76,6 +80,20 @@ fn convert(input: u128) -> Result<(f64, (u32, &'static str)), String> {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if let Some(_) = argv.get(1) {
eprintln!("Usage: {}", argv[0]);
return ExitCode::from(EX_USAGE as u8);
}
if cfg!(target_os="openbsd") {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
let mut buf = String::new();
while let Ok(_) = stdin().read_line(&mut buf) {

View File

@@ -1,5 +1,6 @@
/*
* 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
@@ -22,11 +23,18 @@ use std::{
};
extern crate getopt;
use getopt::GetOpt;
extern crate sysexits;
use getopt::GetOpt;
use sysexits::EX_USAGE;
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[cfg(target_os="openbsd")] use strerror::StrError;
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-egl] integer integer...", s);
ExitCode::from(EX_USAGE as u8)
@@ -34,6 +42,15 @@ fn usage(s: &str) -> ExitCode {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if cfg!(target_os="openbsd") {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
let mut e = false; /* args can be == */
let mut g = false; /* args can be > */
let mut l = false; /* args can be < */
@@ -46,11 +63,13 @@ fn main() -> ExitCode {
Ok("e") => e = true,
Ok("g") => g = true,
Ok("l") => l = true,
_ => { return usage(&argv[0]); },
_ => return usage(&argv[0]),
}
optind = opt.ind();
}
if !e & !g & !l { return usage(&argv[0]); }
if argv.len() - optind < 2 /* see usage */ { return usage(&argv[0]); }
let mut prev: Option<usize> = None; /* no previous operand */
@@ -59,8 +78,8 @@ fn main() -> ExitCode {
for arg in argv.iter().skip(optind) { /* iterate operands */
match arg.parse::<usize>() { /* parse current operand */
Ok(n) => currn = n,
_ => {
eprintln!("{}: {}: Invalid integer", &argv[0], arg);
Err(e) => {
eprintln!("{}: {}: {}", &argv[0], arg, e);
return ExitCode::from(EX_USAGE as u8);
}
}

97
src/libopenbsd.rs Normal file
View File

@@ -0,0 +1,97 @@
/*
* 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
* 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::{
ffi::{ CString, c_char },
io::Error,
ptr::null,
};
mod openbsd {
use std::ffi::{ c_char, c_int };
extern "C" {
pub fn pledge(arg1: *const c_char, arg2: *const c_char) -> c_int;
pub fn unveil(arg1: *const c_char, arg2: *const c_char) -> c_int;
pub fn __errno() -> *mut c_int;
}
}
pub struct Promises(*const c_char);
impl Promises {
pub fn new(promises: &str) -> Self {
let p = CString::new(promises).unwrap();
Promises(p.into_raw() as *const c_char)
}
}
pub fn pledge(
promises: Option<Promises>, execpromises: Option<Promises>
) -> Result<(), Error> {
/* From pledge(2):
*
* Passing NULL to promises or execpromises specifies to not change
* the current value. */
let arg1 = promises.unwrap_or(Promises(null())).0;
let arg2 = execpromises.unwrap_or(Promises(null())).0;
unsafe {
match openbsd::pledge(arg1, arg2) {
-1 => Err(Error::from_raw_os_error(*openbsd::__errno())),
0 => Ok(()),
_ => panic!(), /* unreachable */
}
}
}
pub struct UnveilPerms(CString);
impl UnveilPerms {
pub fn new(permissions: Vec<char>) -> Self {
if permissions.is_empty() {
return UnveilPerms(CString::new("").unwrap());
}
UnveilPerms(
CString::new(permissions.iter().collect::<String>()).unwrap()
)
}
}
pub fn unveil(
path: Option<&str>,
permissions: Option<UnveilPerms>,
) -> Result<(), Error> {
let path_c = path.map(CString::new).map(Result::unwrap);
let arg1 = path_c.map(|p| p.into_raw() as *const c_char).unwrap_or(null());
let arg2 = permissions
.map(|p| p.0.into_raw() as *const c_char)
.unwrap_or(null());
unsafe {
match openbsd::unveil(arg1, arg2) {
-1 => Err(Error::from_raw_os_error(*openbsd::__errno())),
0 => Ok(()),
_ => panic!(), /* unreachable */
}
}
}

View File

@@ -33,6 +33,16 @@ use getopt::GetOpt;
use strerror::StrError;
use sysexits::{ EX_IOERR, EX_USAGE };
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")]
use openbsd::{
Promises,
UnveilPerms,
pledge,
unveil,
};
use ArgMode::*;
enum ArgMode { In, Out }
@@ -41,6 +51,14 @@ fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let usage = format!("Usage: {} [-aetu] [-i input] [-o output]", argv[0]);
if cfg!(target_os="openbsd") {
let promises = Promises::new("rpath stdio unveil");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
let mut a = false; /* append to the file */
let mut e = false; /* use stderr as an output */
let mut t = true; /* do not truncate the file before writing */
@@ -58,11 +76,29 @@ fn main() -> ExitCode {
Ok("t") => t = false,
Ok("i") => { /* add inputs */
let input = opt.arg().unwrap();
if cfg!(target_os="openbsd") {
let perms = UnveilPerms::new(vec!['r']);
if let Err(e) = unveil(Some(&input), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
ins.push(input);
mode = Some(In); /* latest argument == -i */
},
Ok("o") => { /* add output */
let output = opt.arg().unwrap();
if cfg!(target_os="openbsd") {
let perms = UnveilPerms::new(vec!['w', 'c']);
if let Err(e) = unveil(Some(&output), Some(perms)) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
outs.push(output);
mode = Some(Out); /* latest argument == -o */
},
@@ -86,8 +122,17 @@ fn main() -> ExitCode {
Out => outs.push(arg.to_string()),
};
}
} else {
eprintln!("{}", usage);
}
if cfg!(target_os="openbsd") {
if let Err(e) = unveil(None, None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
if ins.is_empty() && outs.is_empty() && argv.len() > optind {
eprintln!("Usage: {}", usage);
return ExitCode::from(EX_USAGE as u8);
}

View File

@@ -19,8 +19,8 @@
#include <stdio.h> /* fprintf(3), fputs(3), getc(3), perror(3), putc(3), stdin,
* stdout, EOF */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_USAGE */
#include <unistd.h> /* getopt(3) */
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
#include <unistd.h> /* pledge(2), getopt(3) */
char *program_name = "npc";
@@ -43,6 +43,13 @@ int main(int argc, char *argv[]) {
char showend = 0; /* print a dollar sign before each newline */
char showtab = 0; /* prints tab characters in caret notation */
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR;
}
#endif
if (argc > 0) {
program_name = argv[0];

View File

@@ -56,6 +56,12 @@ extern crate sysexits;
use sysexits::EX_DATAERR;
#[cfg(target_os="openbsd")] use sysexits::EX_OSERR;
#[cfg(target_os="openbsd")] extern crate strerror;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use strerror::StrError;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
#[derive(Clone, PartialEq, PartialOrd, Debug)]
/* enum CalcType is a type containing operations used in the calculator */
enum CalcType {
@@ -191,6 +197,15 @@ fn round_precise(value: &f64, precision: usize) -> f64 {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if cfg!(target_os="openbsd") {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
eprintln!("{}: {}", argv[0], e.strerror());
return ExitCode::from(EX_OSERR as u8);
}
}
let mut stack = VecDeque::new();
let mut buf = String::new();
/* Set floating-point precision for correcting rounding errors based on

View File

@@ -1,288 +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/.
*/
#include <assert.h> /* assert(3) */
#include <errno.h> /* errno */
#include <stdbool.h> /* bool */
#include <stdio.h> /* fclose(3), feof(3), fgetc(3), fgets(3), fopen(3),
* fprintf(3), fputc(3), perror(3), stderr, stdin, stdout,
* EOF, FILE, NULL */
#include <stdlib.h> /* size_t */
#include <string.h> /* strchr(3), strcmp(3) */
#include <unistd.h> /* getopt(3) */
/* Commands start with cmd_. They take an argc and NULL-terminated argv, like
* main, and return a status from <sysexits.h>. Return values other than EX_OK
* and EX_USAGE cause pg(1) to exit with that value, except EX_UNAVAILABLE,
* which causes pg(1) to exit with the status EX_OK. */
#include <sysexits.h>
#define CMDLINE_MAX 99+1 /* Maximum length of command line. */
static char *whitespace = " \n\r\t\v";
//static
struct Tube {
char *name; // command line
FILE *in; // process stdin
FILE *out; // process stdoout
size_t index; // in pipeline
};
static struct {
size_t quantity;
enum { LINES = 0, BYTES = 1 } type;
} default_page_unit = { 22, LINES } /* Plan 9 default */;
static char *prompt = ": ";
static char *program_name = "pg";
static char *
permute_out(char *s, size_t i) {
for (
;
s[i] != '\0';
s[i] = s[i + 1], ++i
);
return s;
}
/* strtok(3p), but supports double-quotes and escapes (but only for escaping
* quotes). Unmatched quotes in str are considered literal. The behavior of
* strtok_quoted when '\'', '"', or '\\' are in sep is undefined. Use of UTF-8
* separators with strtok_quoted is undefined. */
static char *
strtok_quoted(char *str, char *sep) {
static char *s;
if (str != NULL) { s = str; }
while (strchr(sep, *s) != NULL) { // skip beginning whitespace
if(*++s == '\0') { return NULL; } // no remaining except seps
}
{
bool in_escape = 0; // previous char was '\\'
char quote = '\0'; // quotation mark used, or '\0' if none
for (int i = 0; s[i] != '\0'; ++i)
switch (s[i]) {
case '\\':
// if literal "\\", permute out a backslash
if (in_escape) { (void)permute_out(s, i--); }
in_escape = !in_escape;
break;
case '\'': case '"':
if (in_escape) { // \"
s[i] = s[i - 1];
(void)permute_out(s, i--); // permute out backslash
} else if (s[i] == quote) {
quote = '\0';
(void)permute_out(s, i--); // second quote
} else {
quote = s[i];
if (strchr(&s[i + 1], quote) != NULL) { // has a match
(void)permute_out(s, i--); // permute out lquote
}
}
break;
case '\0': return s;
default:
if (!in_escape
&& quote == '\0'
&& (strchr(sep, s[i]) != NULL || s[i] == '\0')) {
char *t; // start of current token
t = s;
s = s[i] != '\0'
? &t[i + 1] // store start of next token,
: &t[i]; // or the address of the nul if found
s[i] = '\0'; // NUL terminate current token
return t;
}
}
}
return s;
}
/* Page at most l bytes from f without consideration of a buffer (print them to
* stdout). */
static int
pg_b_u(FILE *f, size_t l) {
int c;
while ((c = fgetc(f)) != EOF) {
if ((c = fputc(c, stdout)) == EOF || --l == 0) { break; }
}
return c;
}
/* Page at most l lines, which are lengths of characters terminated by nl, from
* f without consideration of a buffer (print them to stdout). */
static int
pg_l_u(FILE *f, size_t l, char nl) {
int c;
while ((c = fgetc(f)) != EOF) {
if ((c = fputc(c, stdout)) == EOF || (l -= (c == nl)) == 0) { break; }
}
return c;
}
static int
cmd_quit(int argc, char **argv, char **envp) { return EX_UNAVAILABLE; }
static int
cmd_default_page(int argc, char **argv, char **envp) {
if (argc > 1) { return EX_USAGE; } // should be impossible
else if (default_page_unit.type == BYTES) {
return pg_b_u(stdin, default_page_unit.quantity) == EOF
? EX_UNAVAILABLE
: EX_OK;
} else if (default_page_unit.type == LINES) {
return pg_l_u(stdin, default_page_unit.quantity, '\n') == EOF
? EX_UNAVAILABLE
: EX_OK;
} else { return EX_SOFTWARE; }
}
static int
cmd_page_down_lines(int argc, char **argv, char **envp) {
switch (argc) {
case 1: return cmd_default_page(argc, argv, envp);
case 2: /* not implemented */
default:
(void)fprintf(stderr, "Usage: %s" /*" (lines)"*/ "\n", argv[0]);
return EX_USAGE;
}
}
/* A CmdMap must be NULL-terminated. */
static struct CmdMap{ char *name; int (*fn)(int, char **, char **); }
builtins[] = {
/* don't make the user feel trapped */
{ ":q", cmd_quit }, { ":q!", cmd_quit },
{ "exit", cmd_quit }, { "q", cmd_quit }, { "Q", cmd_quit },
{ "quit", cmd_quit }, { "ZZ", cmd_quit },
{ NULL, NULL }
};
#define ARGV_MAX 10
/* Find and execute the command in the command map, given a corresponding
* command line. */
static int
cmdline_exec(struct CmdMap *map, char *cmdline, char **envp) {
static int argc;
static char *argv[ARGV_MAX];
if ((argv[(argc = 0)] = strtok_quoted(cmdline, whitespace)) == NULL) {
while (cmdline[0] != '\0') { cmdline = &cmdline[1]; }
argv[argc] = cmdline;
argv[++argc] = NULL;
} else {
while (
(argv[++argc] = strtok_quoted(NULL, whitespace)) != NULL
&& argc < ARGV_MAX
);
}
for (; map->name != NULL; map = &map[1]) {
if(strcmp(map->name, argv[0]) == 0) {
return map->fn(argc, argv, envp);
}
}
(void)fprintf(stderr, "%s: %s: not found\n", program_name, argv[0]);
return EX_USAGE;
}
static int
ioerr(char *argv0) {
perror(argv0);
return EX_IOERR;
}
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s [-p prompt]\n", argv0);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
unsigned char cmd[CMDLINE_MAX];
FILE *t;
if (argc > 0) {
int c;
program_name = argv[0];
while ((c = getopt(argc, argv, "p:")) != -1) {
switch (c) {
case 'p': prompt = optarg; break;
default: return usage(program_name);
}
}
}
if (argc > optind) { return usage(program_name); }
if ((t = fopen("/dev/tty", "rb")) == NULL) {
perror(program_name);
return EX_OSERR;
}
for (;;) {
if (fputs(prompt, stderr) == EOF) { return ioerr(program_name); }
// if the line...
if (fgets((char *)cmd, (sizeof cmd) / (sizeof *cmd), t) != NULL) {
if (strchr((char *)cmd, '\n') == NULL) { // was taken incompletely
int c;
while ((c = fgetc(t)) != '\n') { // ...fast-forward stream
if (c == EOF) { break; }
}
}
} else { fputc('\n', stdout); } // EOF at start of line; finish prompt
if (feof(t)) { return EX_OK; }
{
int r;
switch ((r = cmdline_exec(builtins, (char *)cmd, NULL))){
case EX_OK: case EX_USAGE: break;
case EX_UNAVAILABLE: return EX_OK;
default: return r;
}
}
}
/* UNREACHABLE */ assert(0);
}

View File

@@ -17,18 +17,21 @@
* along with this program. If not, see https://www.gnu.org/licenses/.
*/
#include <assert.h> /* assert(3) */
#include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */
#include <sysexits.h> /* EX_USAGE */
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
#include <sysexits.h> /* EX_OSERR, EX_USAGE */
#include <unistd.h> /* access(3), getopt(3), pledge(2), unveil(2), 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 */
char *program_name = "scrut";
#define OPTS "bcdefgkprsuwxLS"
static char *opts = OPTS;
/* this is an array so main:sel's size can be known at compile time */
static char opts[] = OPTS;
static int
usage(char *argv0) {
@@ -40,7 +43,16 @@ usage(char *argv0) {
int main(int argc, char *argv[]) {
char sel[(sizeof opts) / (sizeof *opts)];
if (argc < 2) { return usage(argv[0] == NULL ? program_name : argv[0]); }
program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__
if (pledge("rpath stdio unveil", NULL) == -1) {
perror(program_name);
return EX_OSERR;
}
#endif
if (argc < 2) { return usage(program_name); }
{ /* option parsing */
char *p;
@@ -48,7 +60,10 @@ int main(int argc, char *argv[]) {
memset(sel, '\0', sizeof sel);
for (int c; (c = getopt(argc, argv, opts)) != -1;) {
if ((p = strchr(opts, c)) == NULL) { return usage(argv[0]); }
else { sel[p - opts] = c; }
else {
assert(p - opts < sizeof sel / sizeof *sel); /* bounds check */
sel[p - opts] = c;
}
}
/* straighten out selections; permute out nulls */
@@ -63,9 +78,16 @@ int main(int argc, char *argv[]) {
if (optind == argc) { return usage(argv[0]); }
for (argv += optind ; *argv != NULL; ++argv) {
for (argv += optind ; *argv != NULL; argv = &argv[1]) {
struct stat buf;
#ifdef __OpenBSD__
if (unveil(*argv, "r") == -1) {
perror(program_name);
return EX_OSERR;
}
#endif
if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) {
return EXIT_FAILURE; /* doesn't exist or isn't stattable */
}

View File

@@ -1,84 +0,0 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* Copyright (c) 2023 Marceline Cramer <mars@tebibyte.media>
* 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 <ctype.h>
#include <stddef.h> /* NULL */
#include <stdio.h> /* fprintf(3) */
#include <stdlib.h> /* size_t, EXIT_FAILURE */
#include <string.h> /* strcmp(3) */
#include <sysexits.h> /* EX_USAGE */
char *program_name = "str";
static struct {
char *name;
int (*f)(int);
} ctypes[] = {
{ "isalnum", isalnum },
{ "isalpha", isalpha },
{ "isblank", isblank },
{ "iscntrl", iscntrl },
{ "isdigit", isdigit },
{ "isxdigit", isxdigit },
{ "isgraph", isgraph },
{ "islower", islower },
{ "isprint", isprint },
{ "ispunct", ispunct },
{ "isspace", isspace },
{ "isupper", isupper },
{ NULL, NULL } /* marks end */
};
static int
usage(char *argv0) {
(void)fprintf(stderr, "Usage: %s type string...\n", argv0);
return EX_USAGE;
}
int main(int argc, char *argv[]) {
size_t ctype; // selected from ctypes.h; index of ctype
int retval; // initially fail but becomes success on the first valid char
if (argc < 3) { return usage(argv[0] == NULL ? program_name : argv[0]); }
for ( /* iterate ctypes */
ctype = 0;
ctypes[ctype].f != NULL /* break at the end of ctypes */
&& strcmp(argv[1], ctypes[ctype].name) != 0; /* break at match */
++ctype
);
if (ctypes[ctype].f == NULL) { return usage(argv[0]); }
/* iterate args */
for (argv += 2, retval = EXIT_FAILURE; *argv != NULL; ++argv) {
for (size_t i = 0; argv[0][i] != '\0'; ++i) { /* iterate arg bytes */
/* First checks if argv[0][i] is valid ASCII; ctypes(3) don't
* handle non-ASCII. This is bad. */
if(
(unsigned char)argv[0][i] < 0x80 // argv[0][i] is ASCII,
&& !ctypes[ctype].f(argv[0][i]) // so use ctypes(3)
) { return EXIT_FAILURE; }
else { retval = EXIT_SUCCESS; }
}
}
return retval;
}

View File

@@ -16,13 +16,25 @@
* 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 <stdio.h> /* fprintf(3), stderr */
#include <sysexits.h> /* EX_OK, EX_USAGE */
#include <stdio.h> /* fprintf(3), perror(3), stderr */
#include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
#endif
char *program_name = "strcmp";
int main(int argc, char *argv[]) {
int i;
unsigned int i;
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR;
}
#endif
if (argc < 3) {
(void)fprintf(

87
src/stris.rs Normal file
View File

@@ -0,0 +1,87 @@
/*
* Copyright (c) 20232024 DTB <trinity@trinity.moe>
* Copyright (c) 2023 Marceline Cramer <mars@tebibyte.media>
* 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,
};
extern crate getopt;
extern crate sysexits;
use getopt::GetOpt;
use sysexits::EX_USAGE;
struct Reqs {
ascii: bool, blank: bool, cntrl: bool, digit: bool, lower: bool,
upper: bool, inuse: bool, extra: String
}
fn usage(s: &str) -> ExitCode {
eprintln!("Usage: {} [-7bcdlu] [-i inclusions] [strings...]", s);
ExitCode::from(EX_USAGE as u8)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
let mut optind = 1;
let mut reqs = Reqs {
ascii: false, blank: false, cntrl: false, digit: false, lower: false,
upper: false, inuse: false, extra: String::default()
};
while let Some(opt) = argv.getopt("7bcdi:lu") {
match opt.opt() {
Ok("7") => reqs.ascii = true,
Ok("b") => reqs.blank = true,
Ok("c") => reqs.cntrl = true,
Ok("d") => reqs.digit = true,
Ok("i") => reqs.extra = opt.arg().unwrap(),
Ok("l") => reqs.lower = true,
Ok("u") => reqs.upper = true,
_ => { return usage(&argv[0]); }
}
optind = opt.ind();
reqs.inuse = true;
}
if argv.len() == optind { return usage(&argv[0]); }
drop(argv);
if reqs.inuse {
for arg in args().skip(optind) {
for c in arg.chars() {
if (reqs.ascii && c.is_ascii())
|| (reqs.blank && c.is_whitespace())
|| (reqs.cntrl && c.is_control())
|| (reqs.digit && c.is_numeric())
|| (reqs.lower && c.is_lowercase())
|| (reqs.upper && c.is_uppercase())
|| reqs.extra.contains(c) {
continue;
} else {
return ExitCode::FAILURE;
}
}
}
}
ExitCode::SUCCESS
}

View File

@@ -32,6 +32,10 @@ use getopt::GetOpt;
use sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
use strerror::StrError;
#[cfg(target_os="openbsd")] extern crate openbsd;
#[cfg(target_os="openbsd")] use openbsd::{ Promises, pledge };
fn oserr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_OSERR as u8)
@@ -49,6 +53,14 @@ fn usage(s: &str) -> ExitCode {
fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>();
if cfg!(target_os="openbsd") {
let promises = Promises::new("stdio");
if let Err(e) = pledge(Some(promises), None) {
return oserr(&argv[0], e);
}
}
let mut buf: Vec<u8> = Vec::new(); // holds the sequence getting swabbed
let mut input = stdin();
let mut output = stdout().lock();

View File

@@ -1,9 +1,17 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: CC0
*
* This work is marked with CC0 1.0. To view a copy of this license, visit
* <http://creativecommons.org/publicdomain/zero/1.0>.
*/
int main() {}
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
#endif
int main() {
#ifdef __OpenBSD__
pledge(NULL, NULL);
#endif
}

36
tests/README Normal file
View File

@@ -0,0 +1,36 @@
The testing suite contains two trees: the Bonsai tree and the POSIX tree:
.
├── README
├── bonsai/
│   ├── dj.mk
│   ├── false.mk
│   ├── fop.mk
│   └── ...
├── posix/
└── tests.mk
The Bonsai tree tests the functionality of Harakit utilities for regressions and
other issues relating to compliance to our standards of practice.
The POSIX tests are currently a work-in-progress. Their status in this
repository is uncertain.
Both sets of tests also inherit the environment set by the top-level Makefile,
which sets the BIN variable to the build/bin directory at the root of the
project; therefore, each binary is located at $(BIN)/tool for idiomatic access.
Each test contains a set of PHONY targets which are prefixed with the name of
the tool being tested and an underscore. The first target is tests, which
depends on all the other targets in the test file. These test files are each
included in the top Makefile, so they can be called from the root of the
repository. This also means that BIN can be set manually so that tests can be
run using make(1) inside of the tests directory:
$ make -f tests.mk BIN=../build/bin dj_tests
--
Copyright © 2024 Emma Tebibyte <emma@tebibyte.media>
This work is licensed under CC BY-SA 4.0. To view a copy of this license, visit
<http://creativecommons.org/licenses/by-sa/4.0/>.

48
tests/bonsai/dj.mk Executable file
View File

@@ -0,0 +1,48 @@
# 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.
.PRAGMA: command_comment
/dev/full:
/dev/null:
.PHONY: dj_tests
dj_tests: dj_help dj_full dj_null # dj_skip_stdin
.PHONY: dj_full
# Linux has a /dev/full pseudodevice useful for testing errors.
dj_full: $(BIN)/dj /dev/full
case "$$(uname)" in \
Linux) \
$(BIN)/dj -Hi /dev/zero -o /dev/full 2>&1 \
| tee /dev/stderr \
| xargs -I out test '1+0 > 0+0; 1024 > 0' = out \
;; \
esac
.PHONY: dj_help
dj_help: $(BIN)/dj
! $(BIN)/dj -h
.PHONY: dj_null
# Read nothing from /dev/null, write nothing to /dev/null.
dj_null: $(BIN)/dj /dev/null
$(BIN)/dj -Hi /dev/null -o /dev/null 2>&1 \
| tee /dev/stderr \
| xargs -I out test '0+0 > 0+0; 0 > 0' = out
# This test currently fails. This is probably due to dj(1) being stale relative
# to the main harakit branch. TODO: Reassess once the testing branch is merged.
# .PHONY: dj_skip_stdin
# # Test skipping stdin.
# dj_skip_stdin: $(BIN)/dj
# # Pipe 1024B of '\0' into dj(1); skip the first 24B; expect 1000B written.
# dd count=1 bs=1024 </dev/zero 2>/dev/null \
# | $(BIN)/dj -H -s 24 -o /dev/null 2>&1 \
# | tee /dev/stderr \
# | xargs -I out test '1+0 > 1+0; 1024 > 1000' = out

18
tests/bonsai/false.mk Executable file
View File

@@ -0,0 +1,18 @@
# 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.
.PHONY: false_tests
false_tests: false_test false_help
.PHONY: false
false_test: $(BIN)/false
! $(BIN)/false
.PHONY: false_help
false_help: $(BIN)/false
! $(BIN)/false -h

31
tests/bonsai/fop.mk Executable file
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.
.PHONY: fop_tests
fop_tests: fop_functionality fop_delimiter fop_help fop_fail
.PHONY: fop_help
fop_help: $(BIN)/fop
! $(BIN)/fop -h
.PHONY: fop_delimiter
fop_delimiter: $(BIN)/fop
test "$$(printf 'test1 test1 test1\n' | $(BIN)/fop -d' ' 2 sed 's/1/4/g')" \
= 'test1 test1 test4'
test "$$(printf 'meowsetwoofsetribbit\n' \
| $(BIN)/fop -d 'set' 1 sed 's/woof/meow/g')" = 'meowsetmeowsetribbit'
.PHONY: fop_fail
fop_fail: $(BIN)/fop
! printf 'test\n' | $(BIN)/fop 1 cat
! printf 'test\n' | $(BIN)/fop 'test' cat
! printf 'test\n' | $(BIN)/fop -d'test' cat
.PHONY: fop_functionality
fop_functionality: $(BIN)/fop
test "$$(printf 'test1\036test1\036test1\n' | $(BIN)/fop 1 sed 's/1/4/g')" \
= "$$(printf 'test1\036test4\036test1\n')"

32
tests/bonsai/hru.mk Executable file
View File

@@ -0,0 +1,32 @@
# 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.
.PHONY: hru_tests
hru_tests: hru_help hru_functionality hru_negative hru_regressions
.PHONY: hru_help
hru_help: $(BIN)/hru
! $(BIN)/hru -h
.PHONY: hru_functionality
hru_functionality: $(BIN)/hru
test "$$(printf '1234\n' | $(BIN)/hru)" = '1.2 kB'
test "$$(printf '0\n' | $(BIN)/hru)" = '0 B'
.PHONY: hru_negative
hru_negative: $(BIN)/hru
! printf '%s\n' '-1' | $(BIN)/hru
.PHONY: hru_regressions
hru_regressions: $(BIN)/hru
n=1; \
while true; \
do \
printf '%s\n' "$$n" | $(BIN)/hru || break; \
n="$$(($$n * 10))"; \
done; \
printf 'Max float: %s\n' "$$n"

67
tests/bonsai/intcmp.mk Executable file
View File

@@ -0,0 +1,67 @@
# 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.
.PHONY: intcmp_tests
intcmp_tests: intcmp_help intcmp_none intcmp_e intcmp_g intcmp_l intcmp_combined
.PHONY: intcmp_help
intcmp_help: $(BIN)/intcmp
! $(BIN)/intcmp -h
# These test that integer comparisons are working as they should. For the sake
# of readability (to facilitate faster skimming) these recipes follow a
# columned format:
# $binary -flags d d d d # op
# For flag meanings reference intcmp(1) (though they are somewhat self
# explanatory). d here refers to a decimal number; a mixture of 1s, 2s, and 3s
# (a particularly lovely number) arranged to demonstrate easily the operation
# under scrutiny. The commented op is the operation that is true for the given
# numbers. For example:
# $(BIN)/intcmp -e 3 3 3 3 # ==
# op here is ==; 3 == 3 == 3 == 3. The flag being used is -e, to test for
# equality, so this test should succeed.
# ! $(BIN)/intcmp -l 3 2 1 # >
# op here is >; 3 > 2 > 1. The flag being used is -l, to test for each integer
# being less than the next, so intcmp should fail - hence the ! at the start of
# the invocation. If this test failed, intcmp(1) would be confusing -l for -g,
# so that would be a good place to start looking for bugs.
.PHONY: intcmp_none
intcmp_none: $(BIN)/intcmp
! $(BIN)/intcmp 1 2
.PHONY: intcmp_e
intcmp_e: $(BIN)/intcmp
$(BIN)/intcmp -e 3 3 3 # ==
! $(BIN)/intcmp -e 1 2 3 # <
! $(BIN)/intcmp -e 3 2 1 # >
.PHONY: intcmp_g
intcmp_g: $(BIN)/intcmp
$(BIN)/intcmp -g 3 2 1 # >
! $(BIN)/intcmp -g 3 3 3 # ==
! $(BIN)/intcmp -g 1 2 3 # <
$(BIN)/intcmp -ge 3 3 1 # >=
! $(BIN)/intcmp -ge 1 2 3 # <
.PHONY: intcmp_l
intcmp_l: $(BIN)/intcmp
$(BIN)/intcmp -l 1 2 3 # <
! $(BIN)/intcmp -l 3 3 3 # ==
! $(BIN)/intcmp -l 3 2 1 # >
$(BIN)/intcmp -le 1 3 3 # <=
! $(BIN)/intcmp -le 3 2 1 # >
.PHONY: intcmp_combined
intcmp_combined: $(BIN)/intcmp
$(BIN)/intcmp -gl 1 2 3 # <
$(BIN)/intcmp -gl 3 2 1 # >
$(BIN)/intcmp -gl 1 3 1 # !=
! $(BIN)/intcmp -gl 3 3 3 # ==
$(BIN)/intcmp -egl 3 1 1 3 # >, ==, <
! $(BIN)/intcmp -egl foo # huh?

27
tests/bonsai/mm.mk Executable file
View File

@@ -0,0 +1,27 @@
# Copyright (c) 2024 E$(NAME)a Tebibyte <e$(NAME)a@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.
.PHONY: mm_tests
mm_tests: mm_args mm_help mm_stderr
.PHONY: mm_none
mm_none: $(BIN)/mm
test "$$(printf 'meow\n' | $(BIN)/mm)" = meow
.PHONY: mm_args
# mm(1) will error if positional arguments are given without -i or -o
mm_args: $(BIN)/mm
! $(BIN)/mm argument
.PHONY: mm_help
mm_help: $(BIN)/mm
! $(BIN)/mm -h
.PHONY: mm_stderr
# check if stderr is empty upon specifying -e
mm_stderr: $(BIN)/mm
test "$$(printf 'test\n' | $(BIN)/mm -e 2>&1 >/dev/null )" = "test"

72
tests/bonsai/npc.mk Executable file
View File

@@ -0,0 +1,72 @@
#!/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.
.PRAGMA: command_comment
.PHONY: npc_tests
npc_tests: npc_help npc_args npc_ascii
.PHONY: npc_help
npc_help: $(BIN)/npc
! $(BIN)/npc -h
.PHONY: npc_args
# arg parsing
npc_args:
$(BIN)/npc -e </dev/null
$(BIN)/npc -t </dev/null
$(BIN)/npc -et </dev/null
! $(BIN)/npc -et 5 </dev/null
.PHONY: npc_ascii
# Test 0x00 to 0x7f in input; in other words, the full 7b ASCII range.
npc_ascii: npc_ascii_controls npc_ascii_symbols npc_ascii_uppers # \
# npc_ascii_lowers
.PHONY: npc_ascii_controls
# (control characters)
npc_ascii_controls:
# ASCII 0x00 to 0x0a (before the newline, due to xargs(1p) issues)
awk 'BEGIN{ for (i = 0; i < 32; ++i) printf("%c", i); }' \
| $(BIN)/npc \
| head -n 1 \
| xargs -I out test "^@^A^B^C^D^E^F^G^H" = out
# ASCII 0x0a (otherwise the head|tail sequence won't work) to 0x1f
awk 'BEGIN{ for (i = 0; i < 32; ++i) printf("%c", i); print }' \
| $(BIN)/npc \
| head -n 2 \
| tail -n 1 \
| xargs -I out test "^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_"
.PHONY: npc_ascii_symbols
# ASCII 0x1f to 0x3f (^_ and symbols)
npc_ascii_symbols:
# shell quoting olympics
awk 'BEGIN{ for (i = 31; i < 64; ++i) printf("%c", i); print }' \
| $(BIN)/npc \
| sed -e s"/\'/\\\'/g" -e 's/"/\\"/g' \
| xargs -I out test "^_ !\"#$$%&'()*+,-./0123456789:;<=>?" = out
.PHONY: npc_ascii_uppers
# ASCII 0x40 to 0x5f (uppercases)
npc_ascii_uppers:
awk 'BEGIN{ for (i = 64; i < 96; ++i) printf("%c", i); print }' \
| $(BIN)/npc \
| sed 's/\\/\\\\/' \
| xargs -I out test @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_ = out
# This test is broken and will need closer inspection along with the npc(1)
# source.
# .PHONY: npc_ascii_lowers
# # ASCII 0x60 to 0x7f (lowercases)
# npc_ascii_lowers:
# awk 'BEGIN{ for (i = 96; i < 128; ++i) printf("%c", i); print }' \
# | $(BIN)/npc \
# | xargs -I out test "\`abcdefghijklmnopqrstuvwxyz{|}~^?" = out

43
tests/bonsai/rpn.mk Executable file
View File

@@ -0,0 +1,43 @@
# 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.
.PHONY: rpn_tests
rpn_tests: rpn_help rpn_add rpn_sub rpn_mul rpn_div rpn_mod rpn_flr
.PHONY: rpn_help
rpn_help: $(BIN)/rpn
! $(BIN)/rpn -h
.PHONY: rpn_add
rpn_add: $(BIN)/rpn
test "$$($(BIN)/rpn 1 2 +)" -eq 3
test "$$($(BIN)/rpn 0.2 0.1 +)" = 0.3
.PHONY: rpn_sub
rpn_sub: $(BIN)/rpn
test "$$($(BIN)/rpn 23 5 -)" -eq 18
test "$$($(BIN)/rpn 0.3 0.1 -)" = 0.2
.PHONY: rpn_mul
rpn_mul: $(BIN)/rpn
test "$$($(BIN)/rpn 1.2 3 '*')" = 3.6
test "$$($(BIN)/rpn 0 3 '*')" -eq 0
.PHONY: rpn_div
rpn_div: $(BIN)/rpn
test "$$($(BIN)/rpn 12 5 /)" = 2.4
test "$$($(BIN)/rpn 3 0 /)" -eq inf
.PHONY: rpn_mod
rpn_mod: $(BIN)/rpn
test "$$($(BIN)/rpn 12 5 %)" -eq 2
test "$$($(BIN)/rpn 9 4 %)" -eq 1
.PHONY: rpn_flr
rpn_flr: $(BIN)/rpn
test "$$($(BIN)/rpn 12 5 //)" -eq 2
test "$$($(BIN)/rpn 9 4 //)" -eq 2

46
tests/bonsai/scrut.mk Executable file
View File

@@ -0,0 +1,46 @@
#!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
.PRAGMA: command_comment
.PHONY: scrut_tests
scrut_tests: scrut_help scrut_options
.PHONY: scrut_help
scrut_help: $(BIN)/scrut
! $(BIN)/scrut -h
.PHONY: scrut_options
# scrut tests file attributes, but files of a certain attribute aren't
# guaranteed to be present on a system. This test checks all of the files in
# harakit and, if test(1p) says a file matches a certain attribute, then checks
# scrut.
# opts are space-delimited (for command splitting), sel is not
scrut_options: $(BIN)/scrut
set -e; \
opts="b c d e f g k p r s u w x L S"; \
sel=; \
find . -name .git -prune -o -print \
| while read -r f; do \
for opt in $$opts; \
do if ! printf "%s\n" $$sel | grep $$opt >/dev/null; then \
if test -$$opt "$$f"; then \
if ! $(BIN)/scrut -$$opt "$$f"; \
then printf "[!!] scrut -%s failed on %s.\n" \
$$opt "$$f"; \
fi; \
sel="$$sel$$opt"; \
printf "[OK] Tested scrut -%s using %s\n" \
$$opt "$$f"; \
fi; \
fi; \
done; \
if printf "%s\n" "$$opts" | sed 's/ //g' | xargs test "$$sel" =; \
then break; \
fi; \
done

31
tests/bonsai/strcmp.mk Executable file
View File

@@ -0,0 +1,31 @@
# 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.
NAME = strcmp
TARGET = $(NAME)_tests
BINARY = $(BIN)/$(NAME)
.PHONY: strcmp_tests
strcmp_tests: strcmp_equals strcmp_help strcmp_nocmp strcmp_unequals
.PHONY: strcmp_equals
strcmp_equals: $(BIN)/strcmp
$(BIN)/strcmp equals equals
$(BIN)/strcmp - -
.PHONY: strcmp_help
strcmp_help: $(BIN)/strcmp
! $(BIN)/strcmp -h
.PHONY: strcmp_nocmp
strcmp_nocmp: $(BIN)/strcmp
! $(BIN)/strcmp nocmp
.PHONY: strcmp_unequals
strcmp_unequals: $(BIN)/strcmp
! $(BIN)/strcmp unequals equals

25
tests/bonsai/stris.mk Executable file
View File

@@ -0,0 +1,25 @@
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
.PRAGMA: command_comment
.PHONY: stris_tests
stris_tests: stris_help stris_7 stris_b
.PHONY: stris_help
stris_help: $(BIN)/stris
! $(BIN)/stris -h
.PHONY: stris_7
stris_7: $(BIN)/stris
$(BIN)/stris -7 !1Aa' '
! $(BIN)/stris -7 今日は
.PHONY: stris_b
stris_b: $(BIN)/stris
$(BIN)/stris -b "$(printf ' \t\v\r\n')"
! $(BIN)/stris -b !1Aa

22
tests/bonsai/swab.mk Executable file
View File

@@ -0,0 +1,22 @@
# Copyright (c) 2024 DTB <trinity@trinity.moe>
# 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.
.PRAGMA: command_comment
.PHONY: swab_tests
swab_tests: swab_help swab_examples
.PHONY: swab_help
swab_help: $(BIN)/swab
! $(BIN)/swab -h
.PHONY: swab_examples
# These are the examples present in the man page.
swab_examples: $(BIN)/swab
printf 'hello world!\n' \
| $(BIN)/swab \
| xargs -I out test 'ehll oowlr!d' = out

19
tests/bonsai/true.mk Executable file
View File

@@ -0,0 +1,19 @@
#!/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.
.PHONY: true_tests
true_tests: true_test
.PHONY: true_help
true_help: $(BIN)/true
$(BIN)/true -h
.PHONY: true_test
true_test: $(BIN)/true
$(BIN)/true

View File

@@ -1,24 +0,0 @@
#!/bin/sh
# Copyright (c) 20232024 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.
set -e
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"
for utility in tests/posix/*; do
printf '%s: %s: Testing utility.\n' "$0" "$utility"
"$utility"
printf '\n'
done

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 "$@"

5
tests/posix/posix_env Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
set -ex
PATH="$PWD/bin:$PATH"

15
tests/tests.mk Normal file
View File

@@ -0,0 +1,15 @@
# 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.
#TESTFILES != for file in tests/bonsai/*.mk tests/posix/*.mk; do printf '%s ' "$$file"; done;
TESTFILES != for file in tests/bonsai/*.mk; do printf '%s ' "$$file"; done;
TESTS != printf '%s\n' "$(TESTFILES)" | xargs -n1 basename \
| sed 's/\.mk/_tests/g'
include $(TESTFILES)