15 Commits

5 changed files with 129 additions and 33 deletions

View File

@@ -1,4 +1,4 @@
# Copyright (c) 20232024 Emma Tebibyte <emma@tebibyte.media> # Copyright (c) 20232025 Emma Tebibyte <emma@tebibyte.media>
# Copyright (c) 20232024 DTB <trinity@trinity.moe> # Copyright (c) 20232024 DTB <trinity@trinity.moe>
# Copyright (c) 2023 Sasha Koshka <sashakoshka@tebibyte.media> # Copyright (c) 2023 Sasha Koshka <sashakoshka@tebibyte.media>
# Copyright (c) 2024 Aaditya Aryal <aryalaadi123@gmail.com> # Copyright (c) 2024 Aaditya Aryal <aryalaadi123@gmail.com>
@@ -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 peek rpn scrut str strcmp swab true all: dj false fileis fop hru intcmp mm npc peek rpn str strcmp 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
@@ -112,6 +112,11 @@ false: build/bin/false
build/bin/false: src/false.c build build/bin/false: src/false.c build
$(CC) $(CFLAGS) -o $@ src/false.c $(CC) $(CFLAGS) -o $@ src/false.c
.PHONY: fileis
fileis: build/bin/fileis
build/bin/fileis: src/fileis.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fileis.rs
.PHONY: fop .PHONY: fop
fop: build/bin/fop fop: build/bin/fop
build/bin/fop: src/fop.rs build rustlibs build/bin/fop: src/fop.rs build rustlibs
@@ -147,11 +152,6 @@ rpn: build/bin/rpn
build/bin/rpn: src/rpn.rs build rustlibs build/bin/rpn: src/rpn.rs build rustlibs
$(RUSTC) $(RUSTFLAGS) -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 .PHONY: str
str: build/bin/str str: build/bin/str
build/bin/str: src/str.c build build/bin/str: src/str.c build

View File

@@ -1,15 +1,15 @@
.\" Copyright (c) 2024 DTB <trinity@trinity.moe> .\" Copyright (c) 2024 DTB <trinity@trinity.moe>
.\" Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media> .\" Copyright (c) 20242025 Emma Tebibyte <emma@tebibyte.media>
.\" .\"
.\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" 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/>. .\" visit <http://creativecommons.org/licenses/by-sa/4.0/>.
.\" .\"
.TH SCRUT 1 2024-06-06 "Harakit X.X.X" .TH FILEIS 1 2025-02-24 "Harakit X.X.X"
.SH NAME .SH NAME
scrut \(en scrutinize file properties fileis \(en scrutinize file properties
.SH SYNOPSIS .SH SYNOPSIS
scrut fileis
.RB [ -LSbcdefgkprsuwx ] .RB [ -LSbcdefgkprsuwx ]
.B file... .B file...
.\" .\"
@@ -82,5 +82,6 @@ Copyright \(co 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
.\" .\"
.SH SEE ALSO .SH SEE ALSO
.BR access (3p), .BR access (3p),
.BR chown (1p),
.BR lstat (3p), .BR lstat (3p),
.BR test (1p) .BR test (1p)

91
src/fileis.rs Normal file
View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2024 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
* 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,
fs::metadata,
os::unix::fs::{ FileTypeExt, MetadataExt },
process::ExitCode,
};
extern crate getopt;
extern crate strerror;
extern crate sysexits;
use getopt::GetOpt;
use strerror::StrError;
use sysexits::EX_USAGE;
const OPTS: &str = "bcdefgkprsuwxLS";
fn usage(argv0: &str) -> ExitCode {
eprintln!("Usage: {} [-{}] file...", argv0, OPTS);
ExitCode::from(EX_USAGE)
}
fn main() -> ExitCode {
let argv = args().collect::<Vec<_>>();
let mut sel = String::with_capacity(OPTS.len()); // selected options
let mut optind: usize = 1; // argv[0]
while let Some(opt) = argv.getopt(OPTS) {
if let Ok(optchr) = opt.opt() { sel.push_str(optchr); }
else { return usage(&argv[0]); }
optind = opt.ind();
}
if optind == argv.len() { return usage(&argv[0]); }
for arg in argv.iter().skip(optind) {
let fmeta = match metadata(arg) {
Ok(m) => m,
Err(e) => { // no perms or nonexistent
eprintln!("{}: {}: {}", argv[0], arg, e.strerror());
return ExitCode::FAILURE;
},
};
let fmode = fmeta.mode();
let ftype = fmeta.file_type();
for selection in sel.chars() { // run all selected tests
match selection {
'b' if ftype.is_block_device() => (),
'c' if ftype.is_char_device() => (),
'e' => (), // exists or metadata would have errored
'd' if fmeta.is_dir() => (),
'f' if fmeta.is_file() => (),
'g' if fmode & 0o2000 /* S_ISGID */ != 0 => (), // setgid
'k' if fmode & 0o1000 /* S_ISVTX */ != 0 => (), // setvtx
'p' if ftype.is_fifo() => (),
'r' if fmode & 0o0400 /* S_IRUSR */ != 0 => (), // read access
'u' if fmode & 0o4000 /* S_ISUID */ != 0 => (), // setuid
'w' if fmode & 0o0200 /* S_IWUSR */ != 0 => (), // write access
'x' if fmode & 0o0100 /* S_IXUSR */ != 0 => (), // exec access
'L' if fmeta.is_symlink() => (),
'S' if ftype.is_socket() => (),
_ => { return ExitCode::FAILURE; }
}
}
}
ExitCode::SUCCESS
}

View File

@@ -8,20 +8,20 @@
.PRAGMA: command_comment .PRAGMA: command_comment
.PHONY: scrut_tests .PHONY: fileis_tests
scrut_tests: scrut_help scrut_options fileis_tests: fileis_help fileis_options
.PHONY: scrut_help .PHONY: fileis_help
scrut_help: $(BIN)/scrut fileis_help: $(BIN)/fileis
! $(BIN)/scrut -h ! $(BIN)/fileis -h
.PHONY: scrut_options .PHONY: fileis_options
# scrut tests file attributes, but files of a certain attribute aren't # fileis 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 # 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 # harakit and, if test(1p) says a file matches a certain attribute, then checks
# scrut. # fileis.
# opts are space-delimited (for command splitting), sel is not # opts are space-delimited (for command splitting), sel is not
scrut_options: $(BIN)/scrut fileis_options: $(BIN)/fileis
set -e; \ set -e; \
opts="b c d e f g k p r s u w x L S"; \ opts="b c d e f g k p r s u w x L S"; \
sel=; \ sel=; \
@@ -30,12 +30,12 @@ scrut_options: $(BIN)/scrut
for opt in $$opts; \ for opt in $$opts; \
do if ! printf "%s\n" $$sel | grep $$opt >/dev/null; then \ do if ! printf "%s\n" $$sel | grep $$opt >/dev/null; then \
if test -$$opt "$$f"; then \ if test -$$opt "$$f"; then \
if ! $(BIN)/scrut -$$opt "$$f"; \ if ! $(BIN)/fileis -$$opt "$$f"; \
then printf "[!!] scrut -%s failed on %s.\n" \ then printf "[!!] fileis -%s failed on %s.\n" \
$$opt "$$f"; \ $$opt "$$f"; \
fi; \ fi; \
sel="$$sel$$opt"; \ sel="$$sel$$opt"; \
printf "[OK] Tested scrut -%s using %s\n" \ printf "[OK] Tested fileis -%s using %s\n" \
$$opt "$$f"; \ $$opt "$$f"; \
fi; \ fi; \
fi; \ fi; \

View File

@@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# Copyright (c) 2024 DTB <trinity@trinity.moe> # Copyright (c) 2024 DTB <trinity@trinity.moe>
# Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media> # Copyright (c) 20242025 Emma Tebibyte <emma@tebibyte.media>
# SPDX-License-Identifier: FSFAP # SPDX-License-Identifier: FSFAP
# #
# Copying and distribution of this file, with or without modification, are # Copying and distribution of this file, with or without modification, are
@@ -26,7 +26,7 @@ npc_args:
.PHONY: npc_ascii .PHONY: npc_ascii
# Test 0x00 to 0x7f in input; in other words, the full 7b ASCII range. # 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: npc_ascii_controls npc_ascii_uppers # npc_ascii_symbols \
# npc_ascii_lowers # npc_ascii_lowers
.PHONY: npc_ascii_controls .PHONY: npc_ascii_controls
@@ -63,14 +63,18 @@ npc_ascii_controls:
| tail -n 1 \ | tail -n 1 \
| xargs -I out test "^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_" | xargs -I out test "^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_"
.PHONY: npc_ascii_symbols # This test is broken on Linux and will need closer inspection along with GNU
# ASCII 0x1f to 0x3f (^_ and symbols) # xargs(1).
npc_ascii_symbols: # .PHONY: npc_ascii_symbols
# shell quoting olympics # # ASCII 0x1f to 0x3f (^_ and symbols)
awk 'BEGIN{ for (i = 31; i < 64; ++i) printf("%c", i); print }' \ # npc_ascii_symbols:
| $(BIN)/npc \ # # shell quoting olympics
| sed -e s"/\'/\\\'/g" -e 's/"/\\"/g' \ # c="$(awk 'BEGIN{ for (i = 31; i < 64; ++i) printf("%c", i); print }')"
| xargs -I out test "^_ !\"#$$%&'()*+,-./0123456789:;<=>?" = out #
# printf '%s\n' "$c" | $(BIN)/npc \
# | sed -e s"/\'/\\\'/g" -e 's/"/\\"/g' \
# | tr -d '\n' \
# | xargs -I out test "^_ !\"#$$%&\'()*+,-./0123456789:;<=>?" = out
.PHONY: npc_ascii_uppers .PHONY: npc_ascii_uppers
# ASCII 0x40 to 0x5f (uppercases) # ASCII 0x40 to 0x5f (uppercases)