21 Commits

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
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
12 changed files with 273 additions and 211 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

@@ -108,8 +108,10 @@ Io_write(struct Io *io) {
} }
static int static int
oserr(char *e, int n) { oserr(char *e, int n) { /* program_name: [failing component:] error */
(void)fprintf(stderr, "%s: %s: %s\n", program_name, e, strerror(n)); (void)fprintf(stderr, "%s: ", program_name);
if (e != NULL) { (void)fprintf(stderr, "%s: ", e); }
(void)fprintf(stderr, "%s\n", strerror(n));
return EX_OSERR; return EX_OSERR;
} }
@@ -168,12 +170,6 @@ usage(char *argv0) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
#ifdef __OpenBSD__
if (pledge("cpath rpath stdio unveil wpath", NULL) == -1) {
return oserr("pledge", errno);
}
#endif
int align; /* low 8b used, negative if no alignment is being done */ int align; /* low 8b used, negative if no alignment is being done */
int count; /* -1 if dj(1) runs until no more reads are possible */ int count; /* -1 if dj(1) runs until no more reads are possible */
char *fmt; /* set to fmt_asv (default) or fmt_human (-H) */ char *fmt; /* set to fmt_asv (default) or fmt_human (-H) */
@@ -181,6 +177,12 @@ int main(int argc, char *argv[]) {
bool retry; /* false if exits on partial reads or writes */ bool retry; /* false if exits on partial reads or writes */
struct Io io[2 /* { in, out } */]; struct Io io[2 /* { in, out } */];
#ifdef __OpenBSD__
if (pledge("cpath rpath stdio unveil wpath", NULL) == -1) {
return oserr(NULL, errno);
}
#endif
/* Set defaults. */ /* Set defaults. */
align = -1; align = -1;
count = -1; count = -1;
@@ -215,14 +217,10 @@ int main(int argc, char *argv[]) {
break; break;
} else { } else {
int fd; int fd;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
char *perms = "wc"; if (unveil(optarg, i == 0 ? "r" : "wc") == -1) {
return oserr(NULL, errno);
/* modify perms in-place to read-only */
if (i == 0) { perms = "r"; }
if (unveil(optarg, perms) == -1) {
return oserr("unveil", errno);
} }
#endif #endif
@@ -265,8 +263,9 @@ int main(int argc, char *argv[]) {
} }
} }
} }
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (unveil(NULL, NULL) == -1) { return oserr("unveil", errno); } if (unveil(NULL, NULL) == -1) { return oserr(NULL, errno); }
#endif #endif
assert(io->fd != STDIN_FILENO || io->fl == read_flags); assert(io->fd != STDIN_FILENO || io->fl == read_flags);

View File

@@ -17,7 +17,7 @@
*/ */
use std::{ use std::{
ffi::CString, ffi::{ CString, c_char },
io::Error, io::Error,
ptr::null, ptr::null,
}; };
@@ -33,13 +33,13 @@ mod openbsd {
} }
} }
pub struct Promises(*const i8); pub struct Promises(*const c_char);
impl Promises { impl Promises {
pub fn new(promises: &str) -> Self { pub fn new(promises: &str) -> Self {
let p = CString::new(promises).unwrap(); let p = CString::new(promises).unwrap();
Promises(p.into_raw() as *const i8) Promises(p.into_raw() as *const c_char)
} }
} }
@@ -81,10 +81,10 @@ pub fn unveil(
permissions: Option<UnveilPerms>, permissions: Option<UnveilPerms>,
) -> Result<(), Error> { ) -> Result<(), Error> {
let path_c = path.map(CString::new).map(Result::unwrap); let path_c = path.map(CString::new).map(Result::unwrap);
let arg1 = path_c.map(|p| p.into_raw() as *const i8).unwrap_or(null()); let arg1 = path_c.map(|p| p.into_raw() as *const c_char).unwrap_or(null());
let arg2 = permissions let arg2 = permissions
.map(|p| p.0.into_raw() as *const i8) .map(|p| p.0.into_raw() as *const c_char)
.unwrap_or(null()); .unwrap_or(null());
unsafe { unsafe {

View File

@@ -22,11 +22,6 @@
#include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */ #include <sysexits.h> /* EX_IOERR, EX_OK, EX_OSERR, EX_USAGE */
#include <unistd.h> /* pledge(2), getopt(3) */ #include <unistd.h> /* pledge(2), getopt(3) */
#ifdef __OpenBSD__
# include <errno.h> /* errno */
# include <string.h> /* strerror(3) */
#endif
char *program_name = "npc"; char *program_name = "npc";
static int static int
@@ -44,22 +39,20 @@ usage(char *argv0) {
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
#ifdef __OpenBSD__
program_name = argv[0] == NULL ? program_name : argv[0];
if (pledge("stdio", NULL) == -1) {
(void)fprintf(stderr, "%s: %s\n", program_name, strerror(errno));
return EX_OSERR;
}
#endif
int c; int c;
char showend = 0; /* print a dollar sign before each newline */ char showend = 0; /* print a dollar sign before each newline */
char showtab = 0; /* prints tab characters in caret notation */ char showtab = 0; /* prints tab characters in caret notation */
if (argc > 0) { #ifdef __OpenBSD__
#ifndef __OpenBSD__ if (pledge("stdio", NULL) == -1) {
program_name = argv[0]; perror(argv[0] == NULL ? program_name : argv[0]);
return EX_OSERR;
}
#endif #endif
if (argc > 0) {
program_name = argv[0];
while ((c = getopt(argc, argv, "et")) != -1) { while ((c = getopt(argc, argv, "et")) != -1) {
switch (c){ switch (c){
case 'e': showend = 1; break; case 'e': showend = 1; break;

View File

@@ -17,6 +17,7 @@
* along with this program. If not, see https://www.gnu.org/licenses/. * 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 <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */ #include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */ #include <string.h> /* memset(3), strchr(3) */
@@ -29,6 +30,7 @@
char *program_name = "scrut"; char *program_name = "scrut";
#define OPTS "bcdefgkprsuwxLS" #define OPTS "bcdefgkprsuwxLS"
/* this is an array so main:sel's size can be known at compile time */
static char opts[] = OPTS; static char opts[] = OPTS;
static int static int
@@ -58,7 +60,10 @@ int main(int argc, char *argv[]) {
memset(sel, '\0', sizeof sel); memset(sel, '\0', sizeof sel);
for (int c; (c = getopt(argc, argv, opts)) != -1;) { for (int c; (c = getopt(argc, argv, opts)) != -1;) {
if ((p = strchr(opts, c)) == NULL) { return usage(argv[0]); } 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 */ /* straighten out selections; permute out nulls */
@@ -73,7 +78,7 @@ int main(int argc, char *argv[]) {
if (optind == argc) { return usage(argv[0]); } if (optind == argc) { return usage(argv[0]); }
for (argv += optind ; *argv != NULL; ++argv) { for (argv += optind ; *argv != NULL; argv = &argv[1]) {
struct stat buf; struct stat buf;
#ifdef __OpenBSD__ #ifdef __OpenBSD__

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(2) */
#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;
}

View File

@@ -16,13 +16,25 @@
* You should have received a copy of the GNU Affero General Public License * 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/. * along with this program. If not, see https://www.gnu.org/licenses/.
*/ */
#include <stdio.h> /* fprintf(3), stderr */ #include <stdio.h> /* fprintf(3), perror(3), stderr */
#include <sysexits.h> /* EX_OK, EX_USAGE */ #include <sysexits.h> /* EX_OK, EX_OSERR, EX_USAGE */
#ifdef __OpenBSD__
# include <unistd.h> /* pledge(2) */
#endif
char *program_name = "strcmp"; char *program_name = "strcmp";
int main(int argc, char *argv[]) { 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) { if (argc < 3) {
(void)fprintf( (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 sysexits::{ EX_IOERR, EX_OK, EX_OSERR, EX_USAGE };
use strerror::StrError; 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 { fn oserr(argv0: &str, e: Error) -> ExitCode {
eprintln!("{}: {}", argv0, e.strerror()); eprintln!("{}: {}", argv0, e.strerror());
ExitCode::from(EX_OSERR as u8) ExitCode::from(EX_OSERR as u8)
@@ -49,6 +53,14 @@ fn usage(s: &str) -> ExitCode {
fn main() -> ExitCode { fn main() -> ExitCode {
let argv = args().collect::<Vec<String>>(); 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 buf: Vec<u8> = Vec::new(); // holds the sequence getting swabbed
let mut input = stdin(); let mut input = stdin();
let mut output = stdout().lock(); let mut output = stdout().lock();

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