From 71655a8559fbe8623d0551871cd452bc55534e00 Mon Sep 17 00:00:00 2001 From: DTB Date: Mon, 15 Jul 2024 13:47:04 -0600 Subject: [PATCH 01/10] libfileis(3): a library for scrutinizing files --- src/libfileis.c | 27 +++++++++++++++++++++++++++ src/libfileis.h | 14 ++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/libfileis.c create mode 100644 src/libfileis.h diff --git a/src/libfileis.c b/src/libfileis.c new file mode 100644 index 0000000..51caa88 --- /dev/null +++ b/src/libfileis.c @@ -0,0 +1,27 @@ +#include /* NULL */ +#include /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR, + * S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK, + * S_ISUID, S_ISVTX */ +static char *ofn = NULL; +static struct stat s; + +int +fileis_exists(char *fn){ + if (fn == NULL) { return ofn != NULL; } + if (lstat(fn, &s) == -1) { return 0; } + ofn = fn; return 1; +} + +int fileis_block(char *fn) { return fileis_exists(fn) && S_ISBLK(s.st_mode); } +int fileis_char(char *fn) { return fileis_exists(fn) && S_ISCHR(s.st_mode); } +int fileis_dir(char *fn) { return fileis_exists(fn) && S_ISDIR(s.st_mode); } +/* fileis_exec(char *fn) { return fileis_exists(fn) && } */ +int fileis_fifo(char *fn) { return fileis_exists(fn) && S_ISFIFO(s.st_mode); } +int fileis_setgid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISGID); } +int fileis_link(char *fn) { return fileis_exists(fn) && S_ISLNK(s.st_mode); } +/* fileis_read(char *fn) { return fileis_exists(fn) && } */ +int fileis_regular(char *fn){ return fileis_exists(fn) && S_ISREG(s.st_mode); } +int fileis_socket(char *fn) { return fileis_exists(fn) && S_ISSOCK(s.st_mode); } +int fileis_setuid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISUID); } +int fileis_vtx(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISVTX); } +/* fileis_write(char *fn) { return fileis_exists(fn) && } */ diff --git a/src/libfileis.h b/src/libfileis.h new file mode 100644 index 0000000..91426f9 --- /dev/null +++ b/src/libfileis.h @@ -0,0 +1,14 @@ +int fileis_block (char *fn); +int fileis_char (char *fn); +int fileis_dir (char *fn); +/* fileis_exec (char *fn); */ +int fileis_exists (char *fn); +int fileis_fifo (char *fn); +int fileis_setgid (char *fn); +int fileis_link (char *fn); +/* fileis_read (char *fn); */ +int fileis_regular(char *fn); +int fileis_socket (char *fn); +int fileis_setuid (char *fn); +int fileis_vtx (char *fn); +/* fileis_write (char *fn); */ -- 2.46.1 From 59fa3ed3d2765764d85d211b4e793e156ded2985 Mon Sep 17 00:00:00 2001 From: DTB Date: Mon, 15 Jul 2024 13:47:55 -0600 Subject: [PATCH 02/10] scrut(1): get rid of -h --- src/scrut.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scrut.c b/src/scrut.c index d85d243..c4dbe72 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -28,7 +28,7 @@ * S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK, * S_ISUID, S_ISVTX */ -static char args[] = "bcdefghkprsuwxLS"; +static char args[] = "bcdefgkprsuwxLS"; static char ops[(sizeof args) / (sizeof *args)]; static char *program_name = "scrut"; -- 2.46.1 From 2c4349872ca684851fc6f1b22d8fb0cea7f741b9 Mon Sep 17 00:00:00 2001 From: DTB Date: Mon, 15 Jul 2024 13:55:01 -0600 Subject: [PATCH 03/10] scrut(1): banish gotos --- src/scrut.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/src/scrut.c b/src/scrut.c index c4dbe72..85016ae 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -20,9 +20,7 @@ #include /* fprintf(3), stderr, NULL */ #include /* EXIT_FAILURE, EXIT_SUCCESS */ #include /* memset(3), strchr(3) */ -#ifndef EX_USAGE -# include -#endif +#include /* EX_USAGE */ #include /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */ #include /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR, * S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK, @@ -32,21 +30,30 @@ static char args[] = "bcdefgkprsuwxLS"; static char ops[(sizeof args) / (sizeof *args)]; static char *program_name = "scrut"; +int usage(char *s){ + fprintf(stderr, "Usage: %s [-%s] file...", s, args); + return EX_USAGE; +} + int main(int argc, char *argv[]){ struct stat buf; int c; size_t i; char *p; - if(argc < 2) - goto usage; + if (argc > 0) { program_name = argv[0]; } + if (argc < 2) { return usage(program_name); } memset(ops, '\0', sizeof ops); - while((c = getopt(argc, argv, args)) != -1) - if((p = strchr(args, c)) == NULL) - goto usage; - else - ops[p - args] = c; + + while ((c = getopt(argc, argv, args)) != -1) + if ((p = strchr(args, c)) == NULL) { return usage(program_name); } + else { ops[p - args] = c; } + + if (optind == argc) { return usage(program_name); } + + argv += optind; + /* straighten out ops */ for(i = 0, p = ops; i < (sizeof ops) / (sizeof *ops); ++i) if(ops[i] != '\0'){ @@ -55,25 +62,13 @@ int main(int argc, char *argv[]){ ops[i] = '\0'; } - if(optind == argc) - goto usage; - - argv += optind; do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) return EXIT_FAILURE; /* doesn't exist or isn't stattable */ for(i = 0; ops[i] != '\0'; ++i) if(ops[i] == 'e') continue; - else if(ops[i] == 'h'){ -usage: fprintf(stderr, "Usage: %s [-%s] file...\n", - argv[0] == NULL - ? program_name - : argv[0], - args); - - return EX_USAGE; - }else if( + else if( (ops[i] == 'b' && !S_ISBLK(buf.st_mode)) || (ops[i] == 'c' -- 2.46.1 From b9c4b49603bcb780151a7d08b9cd2e34de0702f3 Mon Sep 17 00:00:00 2001 From: DTB Date: Mon, 15 Jul 2024 14:13:53 -0600 Subject: [PATCH 04/10] scrut(1): use libfileis --- Makefile | 11 +++++-- {src => include}/libfileis.h | 0 src/libfileis.c | 2 +- src/scrut.c | 61 ++++++++++++++---------------------- 4 files changed, 32 insertions(+), 42 deletions(-) rename {src => include}/libfileis.h (100%) diff --git a/Makefile b/Makefile index 5e2c70d..69df551 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ RUSTC ?= rustc RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \ --extern sysexits=build/o/libsysexits.rlib \ --extern strerror=build/o/libstrerror.rlib -CFLAGS += -I$(SYSEXITS) +CFLAGS += -I$(SYSEXITS) -Iinclude .PHONY: all all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true @@ -87,6 +87,11 @@ build/o/libsysexits.rlib: build/include/sysexits.h build/include/sysexits.h: build $(SYSEXITS)sysexits.h printf '\043define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h > $@ +.PHONY: libfileis +libfileis: build/o/libfileis.o +build/o/libfileis.o: build src/libfileis.c + $(CC) $(CFLAGS) -c -o $@ src/libfileis.c + .PHONY: dj dj: build/bin/dj build/bin/dj: src/dj.c build @@ -129,8 +134,8 @@ build/bin/rpn: src/rpn.rs build rustlibs .PHONY: scrut scrut: build/bin/scrut -build/bin/scrut: src/scrut.c build - $(CC) $(CFLAGS) -o $@ src/scrut.c +build/bin/scrut: src/scrut.c build libfileis + $(CC) $(CFLAGS) -o $@ src/scrut.c build/o/libfileis.o .PHONY: str str: build/bin/str diff --git a/src/libfileis.h b/include/libfileis.h similarity index 100% rename from src/libfileis.h rename to include/libfileis.h diff --git a/src/libfileis.c b/src/libfileis.c index 51caa88..09a16b4 100644 --- a/src/libfileis.c +++ b/src/libfileis.c @@ -7,7 +7,7 @@ static struct stat s; int fileis_exists(char *fn){ - if (fn == NULL) { return ofn != NULL; } + if (fn == NULL || fn == ofn) { return ofn != NULL; } if (lstat(fn, &s) == -1) { return 0; } ofn = fn; return 1; } diff --git a/src/scrut.c b/src/scrut.c index 85016ae..06ab451 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -22,21 +22,19 @@ #include /* memset(3), strchr(3) */ #include /* EX_USAGE */ #include /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */ -#include /* 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 static char args[] = "bcdefgkprsuwxLS"; static char ops[(sizeof args) / (sizeof *args)]; -static char *program_name = "scrut"; + +char *program_name = "scrut"; int usage(char *s){ - fprintf(stderr, "Usage: %s [-%s] file...", s, args); + fprintf(stderr, "Usage: %s [-%s] file...\n", s, args); return EX_USAGE; } int main(int argc, char *argv[]){ - struct stat buf; int c; size_t i; char *p; @@ -62,41 +60,28 @@ int main(int argc, char *argv[]){ ops[i] = '\0'; } - do{ if(access(*argv, F_OK) != 0 || lstat(*argv, &buf) == -1) + do{ + if (!fileis_exists(*argv)) return EXIT_FAILURE; /* doesn't exist or isn't stattable */ - for(i = 0; ops[i] != '\0'; ++i) - if(ops[i] == 'e') + for (i = 0; ops[i] != '\0'; ++i) + if (ops[i] == 'e') continue; - else if( - (ops[i] == 'b' - && !S_ISBLK(buf.st_mode)) - || (ops[i] == 'c' - && !S_ISCHR(buf.st_mode)) - || (ops[i] == 'd' - && !S_ISDIR(buf.st_mode)) - || (ops[i] == 'f' - && !S_ISREG(buf.st_mode)) - || (ops[i] == 'g' - && !(buf.st_mode & S_ISGID)) - || (ops[i] == 'k' - && !(buf.st_mode & S_ISVTX)) - || (ops[i] == 'p' - && !S_ISFIFO(buf.st_mode)) - || (ops[i] == 'r' - && access(*argv, R_OK) != 0) - || (ops[i] == 'u' - && !(buf.st_mode & S_ISUID)) - || (ops[i] == 'w' - && access(*argv, W_OK) != 0) - || (ops[i] == 'x' - && access(*argv, X_OK) != 0) - || (ops[i] == 'L' - && !S_ISLNK(buf.st_mode)) - || (ops[i] == 'S' - && !S_ISSOCK(buf.st_mode))) - return EXIT_FAILURE; - }while(*++argv != NULL); + else if ((ops[i] == 'b' && !fileis_block(*argv)) + || (ops[i] == 'c' && !fileis_char(*argv)) + || (ops[i] == 'd' && !fileis_dir(*argv)) + || (ops[i] == 'f' && !fileis_regular(*argv)) + || (ops[i] == 'g' && !fileis_setgid(*argv)) + || (ops[i] == 'k' && !fileis_vtx(*argv)) + || (ops[i] == 'p' && !fileis_fifo(*argv)) + || (ops[i] == 'r' && access(*argv, R_OK) != 0) + || (ops[i] == 'u' && !fileis_setuid(*argv)) + || (ops[i] == 'w' && access(*argv, W_OK) != 0) + || (ops[i] == 'x' && access(*argv, X_OK) != 0) + || (ops[i] == 'L' && !fileis_link(*argv)) + || (ops[i] == 'S' && !fileis_socket(*argv)) + ) { return EXIT_FAILURE; } + } while (*++argv != NULL); return EXIT_SUCCESS; } -- 2.46.1 From 0f12dcc5524fc9e82dd661e6484fa1e2f367ea9a Mon Sep 17 00:00:00 2001 From: DTB Date: Mon, 15 Jul 2024 14:28:00 -0600 Subject: [PATCH 05/10] scrut(1): fix ugly opt parsing --- src/scrut.c | 73 +++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/src/scrut.c b/src/scrut.c index 06ab451..139d83e 100644 --- a/src/scrut.c +++ b/src/scrut.c @@ -17,70 +17,67 @@ * along with this program. If not, see https://www.gnu.org/licenses/. */ +#include /* assert(3) */ #include /* fprintf(3), stderr, NULL */ #include /* EXIT_FAILURE, EXIT_SUCCESS */ #include /* memset(3), strchr(3) */ #include /* EX_USAGE */ -#include /* access(3), getopt(3), F_OK, R_OK, W_OK, X_OK */ +#include /* access(3), getopt(3), R_OK, W_OK, X_OK */ #include -static char args[] = "bcdefgkprsuwxLS"; -static char ops[(sizeof args) / (sizeof *args)]; - char *program_name = "scrut"; +static char opts[] = "bcdefgkprsuwxLS"; -int usage(char *s){ - fprintf(stderr, "Usage: %s [-%s] file...\n", s, args); +static int usage(char *s){ + fprintf(stderr, "Usage: %s [-%s] file...\n", s, opts); return EX_USAGE; } int main(int argc, char *argv[]){ - int c; - size_t i; - char *p; + char sel[(sizeof opts) / (sizeof *opts)]; if (argc > 0) { program_name = argv[0]; } if (argc < 2) { return usage(program_name); } - memset(ops, '\0', sizeof ops); + memset(sel, '\0', sizeof sel); - while ((c = getopt(argc, argv, args)) != -1) - if ((p = strchr(args, c)) == NULL) { return usage(program_name); } - else { ops[p - args] = c; } + { + int c; + char *p = sel; + + while ((c = getopt(argc, argv, opts)) != -1) { + if ((strchr(opts, c)) == NULL) { return usage(program_name); } + else if ((strchr(sel, c)) == NULL) { *p++ = c; } + + assert(p - sel < (sizeof opts) / (sizeof *opts)); + } + } if (optind == argc) { return usage(program_name); } argv += optind; - /* 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'; - } - do{ if (!fileis_exists(*argv)) - return EXIT_FAILURE; /* doesn't exist or isn't stattable */ + return EXIT_FAILURE; - for (i = 0; ops[i] != '\0'; ++i) - if (ops[i] == 'e') - continue; - else if ((ops[i] == 'b' && !fileis_block(*argv)) - || (ops[i] == 'c' && !fileis_char(*argv)) - || (ops[i] == 'd' && !fileis_dir(*argv)) - || (ops[i] == 'f' && !fileis_regular(*argv)) - || (ops[i] == 'g' && !fileis_setgid(*argv)) - || (ops[i] == 'k' && !fileis_vtx(*argv)) - || (ops[i] == 'p' && !fileis_fifo(*argv)) - || (ops[i] == 'r' && access(*argv, R_OK) != 0) - || (ops[i] == 'u' && !fileis_setuid(*argv)) - || (ops[i] == 'w' && access(*argv, W_OK) != 0) - || (ops[i] == 'x' && access(*argv, X_OK) != 0) - || (ops[i] == 'L' && !fileis_link(*argv)) - || (ops[i] == 'S' && !fileis_socket(*argv)) + for (size_t i = 0; sel[i] != '\0'; ++i) { + if ((sel[i] == 'b' && !fileis_block(*argv)) + || (sel[i] == 'c' && !fileis_char(*argv)) + || (sel[i] == 'd' && !fileis_dir(*argv)) + || (sel[i] != 'e') + || (sel[i] == 'f' && !fileis_regular(*argv)) + || (sel[i] == 'g' && !fileis_setgid(*argv)) + || (sel[i] == 'k' && !fileis_vtx(*argv)) + || (sel[i] == 'p' && !fileis_fifo(*argv)) + || (sel[i] == 'r' && access(*argv, R_OK) != 0) + || (sel[i] == 'u' && !fileis_setuid(*argv)) + || (sel[i] == 'w' && access(*argv, W_OK) != 0) + || (sel[i] == 'x' && access(*argv, X_OK) != 0) + || (sel[i] == 'L' && !fileis_link(*argv)) + || (sel[i] == 'S' && !fileis_socket(*argv)) ) { return EXIT_FAILURE; } + } } while (*++argv != NULL); return EXIT_SUCCESS; -- 2.46.1 From 0819eeb75db26deff1beb443eb150318d50bd202 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Jul 2024 19:11:22 -0600 Subject: [PATCH 06/10] fileis(1): scrap libfileis(3), work on rewriting scrut(1) in Rust --- Makefile | 17 ++++------ src/fileis.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++ src/libfileis.c | 27 ---------------- src/scrut.c | 84 ------------------------------------------------- 4 files changed, 86 insertions(+), 122 deletions(-) create mode 100644 src/fileis.rs delete mode 100644 src/libfileis.c delete mode 100644 src/scrut.c diff --git a/Makefile b/Makefile index 69df551..e354c31 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \ CFLAGS += -I$(SYSEXITS) -Iinclude .PHONY: all -all: dj false fop hru intcmp mm npc rpn scrut str strcmp swab true +all: dj false fileis fop hru intcmp mm npc rpn scrut str strcmp swab true # keep build/include until bindgen(1) has stdin support # https://github.com/rust-lang/rust-bindgen/issues/2703 @@ -87,11 +87,6 @@ build/o/libsysexits.rlib: build/include/sysexits.h build/include/sysexits.h: build $(SYSEXITS)sysexits.h printf '\043define EXIT_FAILURE 1\n' | cat - $(SYSEXITS)sysexits.h > $@ -.PHONY: libfileis -libfileis: build/o/libfileis.o -build/o/libfileis.o: build src/libfileis.c - $(CC) $(CFLAGS) -c -o $@ src/libfileis.c - .PHONY: dj dj: build/bin/dj build/bin/dj: src/dj.c build @@ -102,6 +97,11 @@ false: build/bin/false build/bin/false: src/false.c build $(CC) $(CFLAGS) -o $@ src/false.c +.PHONY: fileis +fileis: build/bin/fileis +build/bin/fileis: src/fileis.rs build + $(RUSTC) $(RUSTFLAGS) $(RUSTLIBS) -o $@ src/fileis.rs + .PHONY: fop fop: build/bin/fop build/bin/fop: src/fop.rs build rustlibs @@ -132,11 +132,6 @@ rpn: build/bin/rpn 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 libfileis - $(CC) $(CFLAGS) -o $@ src/scrut.c build/o/libfileis.o - .PHONY: str str: build/bin/str build/bin/str: src/str.c build diff --git a/src/fileis.rs b/src/fileis.rs new file mode 100644 index 0000000..70f62f6 --- /dev/null +++ b/src/fileis.rs @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 DTB + * 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, + process::ExitCode, +}; + +extern crate getopt; +use getopt::GetOpt; + +extern crate sysexits; +use sysexits::EX_USAGE; + +fn usage(s: &str) -> ExitCode { + eprintln!("Usage: {} [-bcdefgkprsuwxLS] file...", s); + ExitCode::from(EX_USAGE as u8) +} + +fn main() -> ExitCode { + let argv = args().collect::>(); + + let mut sel: String; // selected options + let mut optind: usize = 1; // argv[0] + + while let Some(opt) = argv.getopt("bcdefgkprsuwxLS") { + if let Ok(optchr) = opt.opt() { sel.push(optchr); } + else { return usage(&argv[0]); } + + optind = opt.ind(); + } + + if optind == argv.len() { return usage(&argv[0]); } + + for arg in argv.iter().skip(optind) { + } + +// do{ +// if (!fileis_exists(*argv)) +// return EXIT_FAILURE; +// +// for (size_t i = 0; sel[i] != '\0'; ++i) { +// if ((sel[i] == 'b' && !fileis_block(*argv)) +// || (sel[i] == 'c' && !fileis_char(*argv)) +// || (sel[i] == 'd' && !fileis_dir(*argv)) +// || (sel[i] != 'e') +// || (sel[i] == 'f' && !fileis_regular(*argv)) +// || (sel[i] == 'g' && !fileis_setgid(*argv)) +// || (sel[i] == 'k' && !fileis_vtx(*argv)) +// || (sel[i] == 'p' && !fileis_fifo(*argv)) +// || (sel[i] == 'r' && access(*argv, R_OK) != 0) +// || (sel[i] == 'u' && !fileis_setuid(*argv)) +// || (sel[i] == 'w' && access(*argv, W_OK) != 0) +// || (sel[i] == 'x' && access(*argv, X_OK) != 0) +// || (sel[i] == 'L' && !fileis_link(*argv)) +// || (sel[i] == 'S' && !fileis_socket(*argv)) +// ) { return EXIT_FAILURE; } +// } +// } while (*++argv != NULL); +// +// return EXIT_SUCCESS; + + ExitCode::SUCCESS +} diff --git a/src/libfileis.c b/src/libfileis.c deleted file mode 100644 index 09a16b4..0000000 --- a/src/libfileis.c +++ /dev/null @@ -1,27 +0,0 @@ -#include /* NULL */ -#include /* lstat(3), stat struct, S_ISBLK, S_ISCHR, S_ISDIR, - * S_ISFIFO, S_ISGID, S_ISREG, S_ISLNK, S_ISSOCK, - * S_ISUID, S_ISVTX */ -static char *ofn = NULL; -static struct stat s; - -int -fileis_exists(char *fn){ - if (fn == NULL || fn == ofn) { return ofn != NULL; } - if (lstat(fn, &s) == -1) { return 0; } - ofn = fn; return 1; -} - -int fileis_block(char *fn) { return fileis_exists(fn) && S_ISBLK(s.st_mode); } -int fileis_char(char *fn) { return fileis_exists(fn) && S_ISCHR(s.st_mode); } -int fileis_dir(char *fn) { return fileis_exists(fn) && S_ISDIR(s.st_mode); } -/* fileis_exec(char *fn) { return fileis_exists(fn) && } */ -int fileis_fifo(char *fn) { return fileis_exists(fn) && S_ISFIFO(s.st_mode); } -int fileis_setgid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISGID); } -int fileis_link(char *fn) { return fileis_exists(fn) && S_ISLNK(s.st_mode); } -/* fileis_read(char *fn) { return fileis_exists(fn) && } */ -int fileis_regular(char *fn){ return fileis_exists(fn) && S_ISREG(s.st_mode); } -int fileis_socket(char *fn) { return fileis_exists(fn) && S_ISSOCK(s.st_mode); } -int fileis_setuid(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISUID); } -int fileis_vtx(char *fn) { return fileis_exists(fn) && (s.st_mode & S_ISVTX); } -/* fileis_write(char *fn) { return fileis_exists(fn) && } */ diff --git a/src/scrut.c b/src/scrut.c deleted file mode 100644 index 139d83e..0000000 --- a/src/scrut.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2023–2024 DTB - * Copyright (c) 2024 Emma Tebibyte - * 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 /* assert(3) */ -#include /* fprintf(3), stderr, NULL */ -#include /* EXIT_FAILURE, EXIT_SUCCESS */ -#include /* memset(3), strchr(3) */ -#include /* EX_USAGE */ -#include /* access(3), getopt(3), R_OK, W_OK, X_OK */ -#include - -char *program_name = "scrut"; -static char opts[] = "bcdefgkprsuwxLS"; - -static int usage(char *s){ - fprintf(stderr, "Usage: %s [-%s] file...\n", s, opts); - return EX_USAGE; -} - -int main(int argc, char *argv[]){ - char sel[(sizeof opts) / (sizeof *opts)]; - - if (argc > 0) { program_name = argv[0]; } - if (argc < 2) { return usage(program_name); } - - memset(sel, '\0', sizeof sel); - - { - int c; - char *p = sel; - - while ((c = getopt(argc, argv, opts)) != -1) { - if ((strchr(opts, c)) == NULL) { return usage(program_name); } - else if ((strchr(sel, c)) == NULL) { *p++ = c; } - - assert(p - sel < (sizeof opts) / (sizeof *opts)); - } - } - - if (optind == argc) { return usage(program_name); } - - argv += optind; - - do{ - if (!fileis_exists(*argv)) - return EXIT_FAILURE; - - for (size_t i = 0; sel[i] != '\0'; ++i) { - if ((sel[i] == 'b' && !fileis_block(*argv)) - || (sel[i] == 'c' && !fileis_char(*argv)) - || (sel[i] == 'd' && !fileis_dir(*argv)) - || (sel[i] != 'e') - || (sel[i] == 'f' && !fileis_regular(*argv)) - || (sel[i] == 'g' && !fileis_setgid(*argv)) - || (sel[i] == 'k' && !fileis_vtx(*argv)) - || (sel[i] == 'p' && !fileis_fifo(*argv)) - || (sel[i] == 'r' && access(*argv, R_OK) != 0) - || (sel[i] == 'u' && !fileis_setuid(*argv)) - || (sel[i] == 'w' && access(*argv, W_OK) != 0) - || (sel[i] == 'x' && access(*argv, X_OK) != 0) - || (sel[i] == 'L' && !fileis_link(*argv)) - || (sel[i] == 'S' && !fileis_socket(*argv)) - ) { return EXIT_FAILURE; } - } - } while (*++argv != NULL); - - return EXIT_SUCCESS; -} -- 2.46.1 From 02b5edae05682c329f626a4f8ad5537870bfa817 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Jul 2024 20:43:39 -0600 Subject: [PATCH 07/10] fileis(1): feature parity with C scrut(1) --- src/fileis.rs | 61 ++++++++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/fileis.rs b/src/fileis.rs index 70f62f6..9ad651e 100644 --- a/src/fileis.rs +++ b/src/fileis.rs @@ -19,6 +19,7 @@ use std::{ env::args, fs::metadata, + os::unix::fs::{ FileTypeExt, MetadataExt }, process::ExitCode, }; @@ -36,11 +37,11 @@ fn usage(s: &str) -> ExitCode { fn main() -> ExitCode { let argv = args().collect::>(); - let mut sel: String; // selected options + let mut sel = String::from(""); // selected options let mut optind: usize = 1; // argv[0] while let Some(opt) = argv.getopt("bcdefgkprsuwxLS") { - if let Ok(optchr) = opt.opt() { sel.push(optchr); } + if let Ok(optchr) = opt.opt() { sel.push_str(optchr); } else { return usage(&argv[0]); } optind = opt.ind(); @@ -49,32 +50,38 @@ fn main() -> ExitCode { if optind == argv.len() { return usage(&argv[0]); } for arg in argv.iter().skip(optind) { - } + let fmeta; // file metadata + let fmode; // file mode; see chmod(1p) + let ftype; // file type -// do{ -// if (!fileis_exists(*argv)) -// return EXIT_FAILURE; -// -// for (size_t i = 0; sel[i] != '\0'; ++i) { -// if ((sel[i] == 'b' && !fileis_block(*argv)) -// || (sel[i] == 'c' && !fileis_char(*argv)) -// || (sel[i] == 'd' && !fileis_dir(*argv)) -// || (sel[i] != 'e') -// || (sel[i] == 'f' && !fileis_regular(*argv)) -// || (sel[i] == 'g' && !fileis_setgid(*argv)) -// || (sel[i] == 'k' && !fileis_vtx(*argv)) -// || (sel[i] == 'p' && !fileis_fifo(*argv)) -// || (sel[i] == 'r' && access(*argv, R_OK) != 0) -// || (sel[i] == 'u' && !fileis_setuid(*argv)) -// || (sel[i] == 'w' && access(*argv, W_OK) != 0) -// || (sel[i] == 'x' && access(*argv, X_OK) != 0) -// || (sel[i] == 'L' && !fileis_link(*argv)) -// || (sel[i] == 'S' && !fileis_socket(*argv)) -// ) { return EXIT_FAILURE; } -// } -// } while (*++argv != NULL); -// -// return EXIT_SUCCESS; + match metadata(arg) { + Ok(m) => fmeta = m, + _ => { return ExitCode::FAILURE; } // no perms or nonexistent + } + + fmode = fmeta.mode(); + 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 } -- 2.46.1 From 958f08bd9ee9fbaab1a0e1f943eec353fc12ebe6 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Jul 2024 20:45:14 -0600 Subject: [PATCH 08/10] fileis.1: rename from scrut.1 --- docs/{scrut.1 => fileis.1} | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) rename docs/{scrut.1 => fileis.1} (95%) diff --git a/docs/scrut.1 b/docs/fileis.1 similarity index 95% rename from docs/scrut.1 rename to docs/fileis.1 index 2b95bee..ed578a6 100644 --- a/docs/scrut.1 +++ b/docs/fileis.1 @@ -4,12 +4,12 @@ .\" This work is licensed under CC BY-SA 4.0. To see a copy of this license, .\" visit . .\" -.TH SCRUT 1 2024-06-06 "Harakit X.X.X" +.TH SCRUT 1 2024-07-18 "Harakit X.X.X" .SH NAME -scrut \(en scrutinize file properties +fileis \(en scrutinize file properties .SH SYNOPSIS -scrut +fileis .RB [ -LSbcdefgkprsuwx ] .B file... .\" @@ -82,5 +82,6 @@ Copyright \(co 2024 DTB. License AGPLv3+: GNU AGPL version 3 or later .\" .SH SEE ALSO .BR access (3p), +.BR chown (1p), .BR lstat (3p), .BR test (1p) -- 2.46.1 From 6d7173e438649ec5d638c9d4ffdf966320195638 Mon Sep 17 00:00:00 2001 From: DTB Date: Thu, 18 Jul 2024 20:50:12 -0600 Subject: [PATCH 09/10] libfileis(3): finish removal --- Makefile | 2 +- include/libfileis.h | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 include/libfileis.h diff --git a/Makefile b/Makefile index e354c31..b7bce98 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ RUSTC ?= rustc RUSTLIBS = --extern getopt=build/o/libgetopt.rlib \ --extern sysexits=build/o/libsysexits.rlib \ --extern strerror=build/o/libstrerror.rlib -CFLAGS += -I$(SYSEXITS) -Iinclude +CFLAGS += -I$(SYSEXITS) .PHONY: all all: dj false fileis fop hru intcmp mm npc rpn scrut str strcmp swab true diff --git a/include/libfileis.h b/include/libfileis.h deleted file mode 100644 index 91426f9..0000000 --- a/include/libfileis.h +++ /dev/null @@ -1,14 +0,0 @@ -int fileis_block (char *fn); -int fileis_char (char *fn); -int fileis_dir (char *fn); -/* fileis_exec (char *fn); */ -int fileis_exists (char *fn); -int fileis_fifo (char *fn); -int fileis_setgid (char *fn); -int fileis_link (char *fn); -/* fileis_read (char *fn); */ -int fileis_regular(char *fn); -int fileis_socket (char *fn); -int fileis_setuid (char *fn); -int fileis_vtx (char *fn); -/* fileis_write (char *fn); */ -- 2.46.1 From f4bd4de2e437e278d19c0e8ba47f67eb11896c9e Mon Sep 17 00:00:00 2001 From: emma Date: Tue, 10 Sep 2024 02:42:55 -0600 Subject: [PATCH 10/10] fileis(1): various changes to make the code more efficient and idiomatic --- src/fileis.rs | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/fileis.rs b/src/fileis.rs index 9ad651e..b81b624 100644 --- a/src/fileis.rs +++ b/src/fileis.rs @@ -1,5 +1,6 @@ /* * Copyright (c) 2024 DTB + * Copyright (c) 2024 Emma Tebibyte * SPDX-License-Identifier: AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify it under @@ -24,23 +25,27 @@ use std::{ }; extern crate getopt; -use getopt::GetOpt; - +extern crate strerror; extern crate sysexits; + +use getopt::GetOpt; +use strerror::StrError; use sysexits::EX_USAGE; -fn usage(s: &str) -> ExitCode { - eprintln!("Usage: {} [-bcdefgkprsuwxLS] file...", s); - ExitCode::from(EX_USAGE as u8) +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::>(); + let argv = args().collect::>(); - let mut sel = String::from(""); // selected options + let mut sel = String::with_capacity(OPTS.len()); // selected options let mut optind: usize = 1; // argv[0] - while let Some(opt) = argv.getopt("bcdefgkprsuwxLS") { + while let Some(opt) = argv.getopt(OPTS) { if let Ok(optchr) = opt.opt() { sel.push_str(optchr); } else { return usage(&argv[0]); } @@ -50,17 +55,16 @@ fn main() -> ExitCode { if optind == argv.len() { return usage(&argv[0]); } for arg in argv.iter().skip(optind) { - let fmeta; // file metadata - let fmode; // file mode; see chmod(1p) - let ftype; // file type + let fmeta = match metadata(arg) { + Ok(m) => m, + Err(e) => { // no perms or nonexistent + eprintln!("{}: {}: {}", argv[0], arg, e.strerror()); + return ExitCode::FAILURE; + }, + }; - match metadata(arg) { - Ok(m) => fmeta = m, - _ => { return ExitCode::FAILURE; } // no perms or nonexistent - } - - fmode = fmeta.mode(); - ftype = fmeta.file_type(); + let fmode = fmeta.mode(); + let ftype = fmeta.file_type(); for selection in sel.chars() { // run all selected tests match selection { -- 2.46.1