Compare commits

...

20 Commits

Author SHA1 Message Date
b0636b416e Merge branch 'fop-trim' (closes #54, closes #58) 2024-02-23 22:05:56 -07:00
3cee3de900 fop(1): committing to Rust error messages 2024-02-23 21:46:50 -07:00
90de1bf9a4 Makefile: RUSTFLAGS not RUSTCFLAGS 2024-02-18 20:03:04 -07:00
1c2e7ea14b Merge branch 'scrut-manpage' (fixes #49) 2024-02-18 15:20:13 -07:00
DTB
e93745ef28 .editorconfig: remove stale configure config 2024-02-17 23:25:35 -07:00
DTB
abb8fdf935 Merge branch 'scrut-fix' 2024-02-17 23:15:07 -07:00
DTB
de9f8dc037 scrut.1: update man page based on review (see #62) 2024-02-17 23:12:39 -07:00
dac7567405 Merge branch 'readme-changes' (closes #51 & closes #24) 2024-02-16 21:02:41 -07:00
97382b79fd README: changed dependency section and update copyright 2024-02-16 21:02:00 -07:00
DTB
bee0165074 scrut(1): add man page 2024-02-16 01:40:38 -07:00
DTB
64a1a19bd8 scrut(1): fix buffer overflow in option parsing 2024-02-16 01:13:37 -07:00
cc93389232 README: added cpp(1) as build dep 2024-02-15 17:39:26 -07:00
448211bbe2 fop(1): better newline removal (preserves existing newlines) 2024-02-14 23:05:39 -07:00
4c663bf9dd fop(1): closure fix 2024-02-14 22:55:28 -07:00
f8e3013563 fop(1): fixed trimming and handled unwraps (closes #58) 2024-02-14 22:53:45 -07:00
5b364e104e rpn(1): fixed exponentiation 2024-02-13 16:57:29 -07:00
42c5c5642e README: bullets 2024-02-07 21:38:32 -07:00
c753eeea9c README: added build information and copyright 2024-02-07 21:27:43 -07:00
d3470233ea Makefile, .gitignore, tests/cc-compat.sh: brought up-to-date 2024-02-07 20:13:45 -07:00
3e39739e88 Makefile: added rpn to all 2024-02-07 19:42:11 -07:00
9 changed files with 174 additions and 65 deletions

View File

@@ -12,6 +12,3 @@ indent_size = 2
[*.sh] [*.sh]
indent_size = 2 indent_size = 2
[configure]
indent_size = 2

1
.gitignore vendored
View File

@@ -1,3 +1,2 @@
build/ build/
dist/ dist/
*.mk

View File

@@ -17,7 +17,7 @@ CC=cc
RUSTC=rustc RUSTC=rustc
.PHONY: all .PHONY: all
all: dj false fop intcmp scrut str strcmp true all: dj false fop intcmp rpn scrut str strcmp true
build: build:
# keep build/include until bindgen(1) has stdin support # keep build/include until bindgen(1) has stdin support
@@ -40,7 +40,6 @@ install: dist
.PHONY: test .PHONY: test
test: build test: build
tests/cc-compat.sh
tests/posix-compat.sh tests/posix-compat.sh
$(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt $(RUSTC) --test src/getopt-rs/lib.rs -o build/test/getopt
@@ -55,7 +54,7 @@ build/o/libsysexits.rlib: build
| $(RUSTC) $(RUSTFLAGS) --crate-type lib -o build/o/libsysexits.rlib - | $(RUSTC) $(RUSTFLAGS) --crate-type lib -o build/o/libsysexits.rlib -
build/o/libgetopt.rlib: src/getopt-rs/lib.rs build/o/libgetopt.rlib: src/getopt-rs/lib.rs
$(RUSTC) $(RUSTCFLAGS) --crate-type=lib --crate-name=getopt \ $(RUSTC) $(RUSTFLAGS) --crate-type=lib --crate-name=getopt \
-o build/o/libgetopt.rlib src/getopt-rs/lib.rs -o build/o/libgetopt.rlib src/getopt-rs/lib.rs
.PHONY: dj .PHONY: dj

32
README
View File

@@ -17,6 +17,31 @@ purposes beyond its scope.
See docs/ for more on the specific utilities currently implemented. See docs/ for more on the specific utilities currently implemented.
Building
The coreutils require a POSIX-compliant environment to compile, including a C
compiler and preprocessor (cc(1) and cpp(1) by default) with the -idirafter
flag, a Rust compiler (rustc(1) by default), bindgen(1), and a POSIX-compliant
make(1) utility.
To build and install:
$ make
$ make PREFIX="/your/preferred/location" install
To build with a different compiler than the default:
$ make CC=clang
$ make RUSTC=gccrs
To test the utilities:
$ make test
To remove all untracked files:
$ make clean
Read More Read More
An Introduction to the Unix Shell An Introduction to the Unix Shell
@@ -30,3 +55,10 @@ Master Foo Discourses on the Unix-Nature
Shell Programming! Shell Programming!
<https://tldp.org/LDP/abs/html/why-shell.html> <https://tldp.org/LDP/abs/html/why-shell.html>
--
Copyright © 20232024 Emma Tebibyte <emma@tebibyte.media>
Copyright © 2024 DTB <trinity@trinity.moe>
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/>.

93
docs/scrut.1 Normal file
View File

@@ -0,0 +1,93 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\"
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license,
.\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.TH scrut 1
.SH NAME
scrut \(en scrutinize file properties
.SH SYNOPSIS
scrut
.RB ( -bcdefgkprsuwxLS )
.RB [ file... ]
.SH DESCRIPTION
Scrut determines if given files comply with the opted requirements.
.SH OPTIONS
.B -b
requires the given files to exist and be block special files.
.PP
.B -c
requires the given files to exist and be character special files.
.PP
.B -d
requires the given files to exist and be directories.
.PP
.B -e
requires the given files to exist, and is redundant to any other option.
.PP
.B -e
requires the given files to exist and be regular files.
.PP
.B -g
requires the given files to exist and have their set group ID flags set.
.PP
.B -k
requires the given files to exist and have their sticky bit set.
.PP
.B -p
requires the given files to exist and be named pipes.
.PP
.B -r
requires the given files to exist and be readable.
.PP
.B -u
requires the given files to exist and have their set user ID flags set.
.PP
.B -w
requires the given files to exist and be writable.
.PP
.B -x
requires the given files to exist and be executable.
.PP
.B -L
requires the given files to exist and be symbolic links.
.PP
.B -S
requires the given files to exist and be sockets.
.SH EXIT STATUS
Scrut prints a debug message and exits unsuccessfully with the appropriate
sysexits.h(3) error code if invoked incorrectly. Scrut exits successfully if
the given files comply with their requirements and unsuccessfully otherwise.
.SH STANDARDS
Scrut is nearly compatible with POSIX's test utility though it is narrower in
scope. Notably, the
.B -h
option is now invalid and therefore shows usage information instead of being an
alias to the modern
.B -L
option.
.SH AUTHOR
Written by DTB <trinity@trinity.moe>.
.SH COPYRIGHT
Copyright © 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
<https://gnu.org/licenses/agpl.html>.
.SH SEE ALSO
access(3p), lstat(3p), test(1p)

View File

@@ -18,7 +18,7 @@
use std::{ use std::{
env::args, env::args,
io::{ Read, stdin, Write }, io::{ Read, stdin, stdout, Write },
process::{ Command, exit, Stdio }, process::{ Command, exit, Stdio },
}; };
@@ -26,7 +26,7 @@ extern crate sysexits;
extern crate getopt; extern crate getopt;
use getopt::{ Opt, Parser }; use getopt::{ Opt, Parser };
use sysexits::{ EX_DATAERR, EX_USAGE }; use sysexits::{ EX_DATAERR, EX_IOERR, EX_UNAVAILABLE, EX_USAGE };
fn main() { fn main() {
let argv = args().collect::<Vec<String>>(); let argv = args().collect::<Vec<String>>();
@@ -54,43 +54,65 @@ fn main() {
exit(EX_USAGE); exit(EX_USAGE);
}); });
let index = argv[index_arg].parse::<usize>().unwrap_or_else(|_| { let index = argv[index_arg].parse::<usize>().unwrap_or_else(|e| {
eprintln!("{}: {}: Not an integer.", argv[0], argv[1]); eprintln!("{}: {}: {}.", argv[0], argv[1], e);
exit(EX_DATAERR); exit(EX_DATAERR);
}); });
let mut buf = String::new(); let mut buf = String::new();
stdin().read_to_string(&mut buf).unwrap(); let _ = stdin().read_to_string(&mut buf);
let mut fields = buf.split(d).collect::<Vec<&str>>(); let mut fields = buf.split(d).collect::<Vec<&str>>();
let opts = argv.iter().clone().skip(command_arg + 1).collect::<Vec<&String>>(); let opts = argv
.iter()
.clone()
.skip(command_arg + 1)
.collect::<Vec<&String>>();
let mut spawned = Command::new(argv.get(command_arg).unwrap()) let mut spawned = Command::new(argv.get(command_arg).unwrap())
.args(opts) .args(opts)
.stdin(Stdio::piped()) .stdin(Stdio::piped())
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.spawn() .spawn()
.unwrap(); .unwrap_or_else( |e| {
eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
exit(EX_UNAVAILABLE);
});
let field = fields.get(index).unwrap_or_else(|| { let field = fields.get(index).unwrap_or_else(|| {
eprintln!( eprintln!(
"{}: {}: No such index in input.", "{}: {}: No such index in input.",
argv[0], argv[0],
index.to_string() index.to_string(),
); );
exit(EX_DATAERR); exit(EX_DATAERR);
}); });
if let Some(mut child_stdin) = spawned.stdin.take() { if let Some(mut child_stdin) = spawned.stdin.take() {
child_stdin.write_all(field.as_bytes()).unwrap(); let _ = child_stdin.write_all(field.as_bytes());
drop(child_stdin); drop(child_stdin);
} }
let output = spawned.wait_with_output().unwrap(); let output = spawned.wait_with_output().unwrap_or_else(|e| {
eprintln!("{}: {}: {}.", argv[0], argv[command_arg], e);
exit(EX_IOERR);
});
let new_field = String::from_utf8(output.stdout).unwrap(); let mut replace = output.stdout.clone();
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);
exit(EX_IOERR);
});
fields[index] = &new_field; fields[index] = &new_field;
print!("{}", fields.join(&d.to_string())); stdout().write_all(
fields.join(&d.to_string()).as_bytes()
).unwrap_or_else(|e|{
eprintln!("{}: {}.", argv[0], e);
exit(EX_IOERR);
});
} }

View File

@@ -165,7 +165,7 @@ fn eval(
Subtract => stack.push_back(y - x), Subtract => stack.push_back(y - x),
Multiply => stack.push_back(y * x), Multiply => stack.push_back(y * x),
Divide => stack.push_back(y / x), Divide => stack.push_back(y / x),
Power => stack.push_back(x.powf(y)), Power => stack.push_back(y.powf(x)),
Floor => stack.push_back((y / x).floor()), Floor => stack.push_back((y / x).floor()),
Modulo => stack.push_back(y % x), Modulo => stack.push_back(y % x),
_ => {}, _ => {},

View File

@@ -18,7 +18,7 @@
#include <stdio.h> /* fprintf(3), stderr, NULL */ #include <stdio.h> /* fprintf(3), stderr, NULL */
#include <stdlib.h> /* EXIT_FAILURE */ #include <stdlib.h> /* EXIT_FAILURE */
#include <string.h> /* strchr(3) */ #include <string.h> /* memset(3), strchr(3) */
#include <unistd.h> /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */ #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, #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_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK,
@@ -33,17 +33,24 @@ int main(int argc, char *argv[]){
struct stat buf; struct stat buf;
int c; int c;
size_t i; size_t i;
char *p;
if(argc < 2) if(argc < 2)
goto usage; goto usage;
i = 0; memset(ops, '\0', sizeof ops);
while((c = getopt(argc, argv, args)) != -1) while((c = getopt(argc, argv, args)) != -1)
if(strchr(args, c) == NULL) if((p = strchr(args, c)) == NULL)
goto usage; goto usage;
else else
ops[i++] = c; ops[p - args] = c;
ops[i] = '\0'; /* straighten out ops */
for(i = 0, p = ops; i < (sizeof ops) / (sizeof *ops); ++i)
if(ops[i] != '\0'){
*p = ops[i];
if(&ops[i] != p++)
ops[i] = '\0';
}
if(optind == argc) if(optind == argc)
goto usage; goto usage;

View File

@@ -1,40 +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 64 # sysexits.h(3) EX_USAGE
fi
make clean
./configure clean
./configure
for CC in cc \
clang \
gcc \
'zig cc'
do
export CC
command -v "$(printf '%s\n' "$CC" | cut -d ' ' -f1)" >/dev/null 2>&1 \
|| continue
printf '%s: %s: Testing build.\n' "$0" "$CC"
make CC="$CC" && printf '%s: Build successful.\n' "$0"
ls -lA build/bin
make clean
printf '\n'
done