28 Commits

Author SHA1 Message Date
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
DTB
6132c9bf47 Merge branch 'main' into scrut-sysexits 2024-04-26 19:37:34 -06:00
DTB
919b171400 scrut(1): conditionally include non-POSIX sysexits.h 2024-04-26 19:35:33 -06:00
DTB
8e38db92c7 scrut(1): update copyright header 2024-04-26 19:33:45 -06:00
DTB
adc9dbded5 scrut(1): changed return status 1 to EXIT_FAILURE 2024-04-26 19:30:33 -06:00
488351da39 scrut(1): changed return status 0 to EXIT_SUCCESS 2024-04-25 16:46:05 -06:00
DTB
61382c34d9 mm(1): error out when given positional arguments 2024-03-31 22:54:03 -06:00
8d9ac33566 Merge branch 'strerror' 2024-03-29 19:23:27 -06:00
b503d97625 Makefile: directory specification changes 2024-03-29 19:22:55 -06:00
b562898a74 copyright 2024-03-27 09:21:19 +05:45
d05d2fae05 Makefile: add a missing build target for npc(1) 2024-03-26 17:19:21 +05:45
d87b5d0958 hru(1), fop(1): changed to reflect strerror changes 2024-03-18 21:31:25 -06:00
1891c3e1aa strerror(3): created strerror() method 2024-03-18 21:30:43 -06:00
127192185f Merge branch 'dj-fix' (closes #66) 2024-03-18 21:01:48 -06:00
b356ac522f strerror(3): changed panic to 0 2024-03-18 20:45:53 -06:00
b2d56bbc9a Makefile: even more changes 2024-03-09 22:02:19 -07:00
2ad7140e1e Makefile: DESTDIR 2024-03-09 11:53:03 -07:00
8193e471f0 changes redundant .unwrap_or_else(panic!()) to .unwrap() 2024-03-03 17:26:40 -07:00
c392dbc680 Makefile: better deps; fop(1), hru(1), strerror(3): changed strerror wrapper function name 2024-03-02 10:16:32 -07:00
898044cd43 Makefile: better macro assignment 2024-03-01 23:51:36 -07:00
ca01ca4074 rpn(1): remove erroneous strerror(3) dependency 2024-03-01 23:14:58 -07:00
3e6dc5cc46 rpn(1): make error messages consistent with other tools 2024-03-01 23:14:18 -07:00
cf1d16f860 hru(1): changed to use strerror(3) 2024-03-01 23:12:44 -07:00
8f956d775c fop(1): changed to use strerror(3) 2024-03-01 23:10:28 -07:00
e81703c6e1 sterror(3): added library for C-like error messages; Makefile: some efficiency changes 2024-03-01 23:04:53 -07:00
DTB
58245c9484 dj(1): remove function pointer hijinks (fixes #66) 2024-02-22 19:27:14 -07:00
13 changed files with 370 additions and 213 deletions

View File

@@ -1,6 +1,7 @@
# Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media>
# Copyright (c) 20232024 DTB <trinity@trinity.moe>
# Copyright (c) 2023 Sasha Koshka <sashakoshka@tebibyte.media>
# Copyright (c) 2024 Aaditya Aryal <aryalaadi123@gmail.com>
# SPDX-License-Identifier: FSFAP
#
# Copying and distribution of this file, with or without modification, are
@@ -8,16 +9,26 @@
# notice are preserved. This file is offered as-is, without any warranty.
.POSIX:
# if using BSD make(1), remove these pragmas because they break it
.PRAGMA: posix_202x # future POSIX standard support à la pdpmake(1)
.PRAGMA: command_comment # breaks without this?
PREFIX=/usr/local
DESTDIR ?= dist
PREFIX ?= /usr/local
CC=cc
RUSTC=rustc
SYSEXITS != printf '\043include <sysexits.h>\n' | cpp -M - | sed 's/ /\n/g' \
| sed -n 's/sysexits\.h//p' || printf 'include\n'
CC ?= cc
RUSTC ?= rustc
RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
--extern strerror=build/o/libstrerror.rlib
CFLAGS += -I$(SYSEXITS)
.PHONY: all
all: dj false fop hru intcmp mm rpn scrut str strcmp swab true
all: dj false fop hru intcmp mm npc rpn scrut strcmp stris swab true
build:
# keep build/include until bindgen(1) has stdin support
@@ -26,36 +37,40 @@ build:
.PHONY: clean
clean:
rm -rf build/ dist/
rm -rf build dist
dist: all
mkdir -p dist/bin dist/share/man/man1
cp build/bin/* dist/bin/
cp docs/*.1 dist/share/man/man1/
mkdir -p $(DESTDIR)/$(PREFIX)/bin $(DESTDIR)/$(PREFIX)/share/man/man1
cp build/bin/* $(DESTDIR)/$(PREFIX)/bin
cp docs/*.1 $(DESTDIR)/$(PREFIX)/share/man/man1
.PHONY: install
install: dist
mkdir -p $(PREFIX)
cp -r dist/* $(PREFIX)/
cp -r $(DESTDIR)/* /
.PHONY: test
test: build
tests/posix-compat.sh
$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
build/o/libsysexits.rlib: build
.PHONY: rustlibs
rustlibs: build/o/libsysexits.rlib build/o/libgetopt.rlib \
build/o/libstrerror.rlib
build/o/libgetopt.rlib: build src/getopt-rs/lib.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
-o $@ src/getopt-rs/lib.rs
build/o/libstrerror.rlib: build src/strerror.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib -o $@ \
src/strerror.rs
build/o/libsysexits.rlib: build $(SYSEXITS)sysexits.h
# bandage solution until bindgen(1) gets stdin support
printf '#define EXIT_FAILURE 1\n' | cat - include/sysexits.h \
printf '#define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h \
> build/include/sysexits.h
bindgen --default-macro-constant-type signed --use-core --formatter=none \
"$$(printf '#include <sysexits.h>\n' \
| cpp -M -idirafter "build/include" - \
| sed 's/ /\n/g' | grep sysexits.h)" \
| $(RUSTC) $(RUSTFLAGS) --crate-type lib -o build/o/libsysexits.rlib -
build/o/libgetopt.rlib: src/getopt-rs/lib.rs
$(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
-o build/o/libgetopt.rlib src/getopt-rs/lib.rs
build/include/sysexits.h | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o $@ -
.PHONY: dj
dj: build/bin/dj
@@ -69,17 +84,13 @@ build/bin/false: src/false.c build
.PHONY: fop
fop: build/bin/fop
build/bin/fop: src/fop.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/fop.rs
build/bin/fop: src/fop.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fop.rs
.PHONY: hru
hru: build/bin/hru
build/bin/hru: src/hru.rs build build/o/libgetopt.rlib build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/hru.rs
build/bin/hru: src/hru.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/hru.rs
.PHONY: intcmp
intcmp: build/bin/intcmp
@@ -91,22 +102,29 @@ mm: build/bin/mm
build/bin/mm: src/mm.c build
$(CC) $(CFLAGS) -o $@ src/mm.c
.PHONY: npc
npc: build/bin/npc
build/bin/npc: src/npc.c build
$(CC) $(CFLAGAS) -o $@ src/npc.c
.PHONY: rpn
rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/rpn.rs
build/bin/rpn: src/rpn.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -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 build/o/libgetopt.rlib \
build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/stris.rs
.PHONY: strcmp
strcmp: build/bin/strcmp
@@ -115,7 +133,8 @@ build/bin/strcmp: src/strcmp.c build
.PHONY: swab
swab: build/bin/swab
build/bin/swab: src/swab.rs build build/o/libsysexits.rlib
build/bin/swab: src/swab.rs build build/o/libgetopt.rlib \
build/o/libsysexits.rlib
$(RUSTC) $(RUSTFLAGS) --extern getopt=build/o/libgetopt.rlib \
--extern sysexits=build/o/libsysexits.rlib \
-o $@ src/swab.rs

View File

@@ -1,58 +0,0 @@
.\" Copyright (c) 20232024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2023 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
.SH NAME
str \(en test the character types of string arguments
.SH SYNOPSIS
str
.RB [ type ]
.RB [ string... ]
.SH DESCRIPTION
Str tests each character in an arbitrary quantity of string arguments against
the function of the same name within ctype(3).
.SH DIAGNOSTICS
Str exits successfully if all tests pass and unsuccessfully if a test failed.
.PP
Str will exit unsuccessfully if a string is empty, as none of its contents
passed the test.
.PP
Str will print a message to standard error and exit unsuccessfully if used
improperly.
.SH DEPRECATED FEATURES
Str used to have an "isvalue" type as an extension to ctype(3). This was
removed in favor of using strcmp(1) to compare strings against the empty string
('').
.SH BUGS
There's no way of knowing which argument failed the test without re-testing
arguments individually.
.PP
If a character in a string isn't valid ASCII str will exit unsuccessfully.
.SH AUTHOR
Written by DTB <trinity@trinity.moe>.
.SH COPYRIGHT
Copyright © 2023 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.SH SEE ALSO
ctype(3p), strcmp(1), ascii(7)

118
docs/stris.1 Normal file
View File

@@ -0,0 +1,118 @@
.\" Copyright (c) 20232024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2023 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
.SH NAME
stris \(en test the character types of string arguments
.SH SYNOPSIS
stris
.RB ( -7bcdlu )
.RB ( -i [ inclusions ])
.RB [ strings... ]
.SH DESCRIPTION
Stris tests each rune in an arbitrary quantity of string arguments, ensuring
each meets any of the parameters specified in the program options.
.SH OPTIONS
.B -7
.RS
Tests to see if runes are fit within seven bits; that is, that they are encoded
with ASCII.
.RE
.B -b
.RS
Tests to see if runes are blank or "whitespace"; characters that do not print
but fill a predictable amount of space.
.RE
.B -c
.RS
Tests to see if runes are control characters; characters that are not printing
or graphical.
.RE
.B -d
.RS
Tests to see if runes are numeric. This test does not only allow the ASCII
digits but any numeric symbol.
.RE
.B -i
.RS
Permits, in addition to the given specified parameters, all of the runes
supplied in its option argument.
.RE
.B -l
.RS
Tests to see if runes are in their lower case.
.RE
.B -u
.RS
Tests to see if runes are in their upper case, or capitalized.
.RE
.SH DIAGNOSTICS
Stris exits successfully if all runes in all given strings meet any of the
specified parameters, or if no parameters were specified but all given strings
were legibly encoded. It exits unsuccessfully if the previous is untrue, and if
invalid options were given, or if no strings were given, a usage synopsis will
be printed to the standard error.
.SH BUGS
There's no way of knowing which argument failed the test without re-testing
arguments individually.
Some runes that can losslessly be encoded into ASCII from UTF-8 but in an
"overlong encoding", where the rune was encoded with unnecessary leading
zeroes causing it to span multiple bytes, won't be detected as ASCII.
.SH EXAMPLES
This is an sh(1p) snippet that checks to see if an environment variable is an
ASCII digit.
.RS
.R stris -7 "$v" && stris -d "$v" && echo ASCII digit.
.RE
This is an sh(1p) snippet that checks to see if an environment variable is a
hexadecimal number.
.RS
.R stris -7 "$v" && stris -di ABCDEFabcdef "$v" && echo Hexadecimal number.
.RE
.SH AUTHOR
Written by DTB <trinity@trinity.moe>.
.SH HISTORY
Stris replaces the former str(1) which took the name of a function from
ctype(3) as its first argument and checked the following strings against it;
str(1) exited unsuccessfully when it encountered any non-ASCII runes and could
only have one parameter specified.
.SH COPYRIGHT
Copyright © 20232024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/gpl.html>.
.SH SEE ALSO
ascii(7), ctype(3p), strcmp(1)

View File

@@ -203,28 +203,28 @@ Io_fdseek(struct Io *io){
if(!fdisstd(io->fd) && lseek(io->fd, io->seek, SEEK_SET) != -1)
return -1;
else if(io->fl == write_flags){
memset(io->buf, '\0', io->bs);
/* This is a dirty trick; rather than testing conditions and operating
* likewise, because the parameters to read or write are going to be
* the same either way, just use a function pointer to keep track of
* the intended operation. */
op = (int (*)(int, void *, size_t))&write;
/* Function pointer casts are risky; this works because the difference
* is in the second parameter and only that write(2) makes the buffer
* const whereas read(2) does not. To avoid even the slightest
* undefined behavior comment out the cast, just be ready for a
* -Wincompatible-function-pointer-types if your compiler notices it.
*/
}else
op = &read;
/* We're going to cheat and use bufuse as the retval for write(2), which is
* fine because it'll be zeroed as this function returns anyway. */
do{ if( (io->bufuse = (*op)(io->fd, io->buf, MIN(io->bs, io->seek))) == 0)
/* second chance */
io->bufuse = (*op)(io->fd, io->buf, MIN(io->bs, io->seek));
}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0);
/* repeated code to get the condition out of the loop */
if(io->fl == write_flags){
memset(io->buf, '\0', io->bs);
/* We're going to cheat and use bufuse as the retval for write(2),
* which is fine because it'll be zeroed as this function returns
* anyway. */
do{
if((io->bufuse = write(io->fd, io->buf, MIN(io->bs, io->seek)))
== 0)
/* second chance */
io->bufuse = write(io->fd, io->buf, MIN(io->bs, io->seek));
}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0);
}else if(io->fl == read_flags){
do{
if((io->bufuse = read(io->fd, io->buf, MIN(io->bs, io->seek)))
== 0)
/* second chance */
io->bufuse = read(io->fd, io->buf, MIN(io->bs, io->seek));
}while((io->seek -= io->bufuse) > 0 && io->bufuse != 0);
}else
return EX_SOFTWARE;
io->bufuse = 0;

View File

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

View File

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

View File

@@ -106,6 +106,15 @@ oserr(char *s, char *r){
} \
return retval
/* Prints a usage text, in which s is the program being run (i.e. argv[0]), and
* returns an exit status appropriate for a usage error. */
int usage(char *s){
fprintf(stderr, "Usage: %s (-aenu) (-i [input])... (-o [output])...\n", s);
return EX_USAGE;
}
int main(int argc, char *argv[]){
int c;
struct Files files[2]; /* {read, write} */
@@ -178,12 +187,15 @@ int main(int argc, char *argv[]){
k = 1;
break;
default:
fprintf(stderr, "Usage: %s (-aenu) (-i [input])..."
" (-o [output])...\n", argv[0]);
retval = EX_USAGE;
retval = usage(argv[0]);
terminate;
}
if(optind != argc){
retval = usage(argv[0]);
terminate;
}
files[0].s += files[0].s == 0;
files[1].s += files[1].s == 0;

View File

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

View File

@@ -1,5 +1,6 @@
/*
* Copyright (c) 2023 DTB <trinity@trinity.moe>
* 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
@@ -17,13 +18,15 @@
*/
#include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <string.h> /* memset(3), strchr(3) */
#ifndef EX_USAGE
# include <sysexits.h>
#endif
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */
#include <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 */
#include <sysexits.h>
static char args[] = "bcdefghkprsuwxLS";
static char ops[(sizeof args) / (sizeof *args)];
@@ -57,7 +60,7 @@ int main(int argc, char *argv[]){
argv += optind;
do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1)
return 1; /* doesn't exist or isn't stattable */
return EXIT_FAILURE; /* doesn't exist or isn't stattable */
for(i = 0; ops[i] != '\0'; ++i)
if(ops[i] == 'e')
@@ -97,8 +100,8 @@ usage: fprintf(stderr, "Usage: %s (-%s) [file...]\n",
&& !S_ISLNK(buf.st_mode))
|| (ops[i] == 'S'
&& !S_ISSOCK(buf.st_mode)))
return 1;
return EXIT_FAILURE;
}while(*++argv != NULL);
return 0;
return EXIT_SUCCESS;
}

View File

@@ -1,75 +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> /* EXIT_FAILURE */
#include <string.h> /* strcmp(3) */
#include <sysexits.h>
static 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 }
};
int main(int argc, char *argv[]){
int ctype;
int i;
int r;
if(argc >= 3){
for(ctype = 0; ctype < (sizeof ctypes) / (sizeof *ctypes);
++ctype)
if(strcmp(argv[1], ctypes[ctype].name) == 0)
goto pass;
}
fprintf(stderr, "Usage: %s [type] [string...]\n",
argv[0] == NULL ? program_name : argv[0]);
return EX_USAGE;
pass: for(argv += 2, r = 1; *argv != NULL; ++argv)
for(i = 0; argv[0][i] != '\0'; ++i)
/* 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 && !ctypes[ctype].f(argv[0][i]))
return 1;
else
r = 0;
return r;
}

31
src/strerror.rs Normal 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.
*/
use std::ffi::{ c_int, c_char, CStr };
pub trait StrError { fn strerror(&self) -> String; }
impl StrError for std::io::Error {
/* wrapper function for use in Rust */
fn strerror(&self) -> String {
/* Get the raw OS error. If its None, what the hell is going on‽ */
let errno = self.raw_os_error().unwrap_or(0) as c_int;
/* Get a CStr from the error message so that its referenced and then
* convert it to an owned value. If the string is not valid UTF-8,
* return that error instead. */
match unsafe { CStr::from_ptr(strerror(errno)) }.to_str() {
Ok(s) => s.to_owned(), // yay!! :D
Err(e) => e.to_string(), // awww :(
}
}
}
/* binding to strerror(3p) */
extern "C" { fn strerror(errnum: c_int) -> *mut c_char; }

93
src/stris.rs Normal file
View File

@@ -0,0 +1,93 @@
/*
* 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;
use getopt::{ Opt, Parser };
extern crate sysexits;
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 opts = Parser::new(&argv, "7bcdi:lu");
let mut reqs = Reqs {
ascii: false, blank: false, cntrl: false, digit: false, lower: false,
upper: false, inuse: false, extra: String::new()
};
loop {
match opts.next() {
None => break,
Some(opt) => {
match opt {
Ok(Opt('7', None)) => reqs.ascii = true,
Ok(Opt('b', None)) => reqs.blank = true,
Ok(Opt('c', None)) => reqs.cntrl = true,
Ok(Opt('d', None)) => reqs.digit = true,
Ok(Opt('i', Some(arg))) => reqs.extra = arg,
Ok(Opt('l', None)) => reqs.lower = true,
Ok(Opt('u', None)) => reqs.upper = true,
_ => { return usage(&argv[0]); }
}
reqs.inuse = true;
}
}
}
if argv.len() == opts.index() {
return usage(&argv[0]);
}
drop(argv);
if reqs.inuse {
for arg in args().skip(opts.index()) {
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
}

10
src/test.rs Normal file
View File

@@ -0,0 +1,10 @@
extern crate strerror;
use strerror::raw_message;
fn main() {
stdout.write_all(b"meow\n").unwrap_or_else(|e| {
eprintln!("{}", raw_message(e));
std::process::exit(1);
});
}