Replace str(1) with stris(1) #102

Open
trinity wants to merge 10 commits from stris into main
6 changed files with 210 additions and 169 deletions

View File

@ -42,7 +42,7 @@ BIN = build/bin
default: all test default: all test
.PHONY: all .PHONY: all
all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true all: dj false fop hru intcmp mm npc rpn scrut strcmp stris swab true
# keep build/include until bindgen(1) has stdin support # keep build/include until bindgen(1) has stdin support
# https://github.com/rust-lang/rust-bindgen/issues/2703 # https://github.com/rust-lang/rust-bindgen/issues/2703
@ -147,10 +147,10 @@ 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: str .PHONY: stris
str: build/bin/str stris: build/bin/stris
build/bin/str: src/str.c build build/bin/stris: src/stris.rs build rustlibs
$(CC) $(CFLAGS) -o $@ src/str.c $(RUSTC) $(RUSTFLAGS) -o $@ src/stris.rs
.PHONY: strcmp .PHONY: strcmp
strcmp: build/bin/strcmp strcmp: build/bin/strcmp

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

@ -1,96 +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), perror(3) */
#include <stdlib.h> /* size_t, EXIT_FAILURE */
#include <string.h> /* strcmp(3) */
#include <sysexits.h> /* EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
#endif
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
program_name = argv[0] == NULL ? program_name : argv[0];
#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1) {
perror(program_name);
return EX_OSERR;
}
#endif
if (argc < 3) { return usage(program_name); }
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;
}

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

@ -7,14 +7,19 @@
.PRAGMA: command_comment .PRAGMA: command_comment
.PHONY: str_tests .PHONY: stris_tests
str_tests: str_help str_isalpha stris_tests: stris_help stris_7 stris_b
.PHONY: str_help .PHONY: stris_help
str_help: $(BIN)/str stris_help: $(BIN)/stris
! $(BIN)/str -h ! $(BIN)/stris -h
.PHONY: str_isalpha .PHONY: stris_7
str_isalpha: $(BIN)/str stris_7: $(BIN)/stris
$(BIN)/str isalpha c $(BIN)/stris -7 !1Aa' '
! $(BIN)/str isalpha 3 ! $(BIN)/stris -7 今日は
.PHONY: stris_b
stris_b: $(BIN)/stris
$(BIN)/stris -b "$(printf ' \t\v\r\n')"
! $(BIN)/stris -b !1Aa