Compare commits
16 Commits
0.13.17
...
958f08bd9e
| Author | SHA1 | Date | |
|---|---|---|---|
|
958f08bd9e
|
|||
|
02b5edae05
|
|||
|
0819eeb75d
|
|||
|
0f12dcc552
|
|||
|
b9c4b49603
|
|||
|
2c4349872c
|
|||
|
59fa3ed3d2
|
|||
|
71655a8559
|
|||
|
9addfc9284
|
|||
|
8f8de5de2b
|
|||
|
0df2c9f566
|
|||
|
b7bc1f16ad
|
|||
|
ca6865688a
|
|||
|
1fd768057c
|
|||
|
35d54d84b0
|
|||
|
a141b95293
|
14
Makefile
14
Makefile
@@ -29,10 +29,10 @@ 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
|
||||
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
|
||||
@@ -97,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
|
||||
@@ -127,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
|
||||
$(CC) $(CFLAGS) -o $@ src/scrut.c
|
||||
|
||||
.PHONY: str
|
||||
str: build/bin/str
|
||||
build/bin/str: src/str.c build
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
.\" 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 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)
|
||||
@@ -11,7 +11,6 @@ swab \(en swap bytes
|
||||
.SH SYNOPSIS
|
||||
|
||||
swab
|
||||
.RB [ -f ]
|
||||
.RB [ -w\ word_size ]
|
||||
.\"
|
||||
.SH DESCRIPTION
|
||||
@@ -20,8 +19,6 @@ Swap the latter and former halves of a block of bytes.
|
||||
.\"
|
||||
.SH OPTIONS
|
||||
|
||||
.IP \fB-f\fP
|
||||
Ignore SIGINT signal.
|
||||
.IP \fB-w\fP\ \fIword_size\fP
|
||||
Configures the word size; that is, the size in bytes of the block size on which
|
||||
to operate. The default word size is 2. The word size must be cleanly divisible
|
||||
|
||||
14
include/libfileis.h
Normal file
14
include/libfileis.h
Normal file
@@ -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); */
|
||||
9
src/dj.c
9
src/dj.c
@@ -263,12 +263,12 @@ int main(int argc, char *argv[]){
|
||||
assert(io[0].bufuse == 0);
|
||||
|
||||
{ /* read */
|
||||
char skipping;
|
||||
long skipping;
|
||||
size_t t;
|
||||
|
||||
/* hack to intentionally get a partial read from Io_read */
|
||||
if((skipping = (io[0].seek > 0)) && io[0].seek < io[0].bs)
|
||||
io[0].bufuse = io[0].bs - io[0].seek;
|
||||
if((skipping = MIN(io[0].seek, io[0].bs)) > 0)
|
||||
io[0].bufuse = io[0].bs - (size_t)skipping;
|
||||
|
||||
t = io[0].bufuse;
|
||||
if(Io_read(&io[0])->bufuse == t && !noerror && io[0].error == 0)
|
||||
@@ -290,7 +290,8 @@ int main(int argc, char *argv[]){
|
||||
}
|
||||
}
|
||||
|
||||
if(skipping){
|
||||
if(skipping > 0){
|
||||
io[0].seek -= skipping;
|
||||
io[0].bufuse = 0;
|
||||
count += (count != 0);
|
||||
continue;
|
||||
|
||||
87
src/fileis.rs
Normal file
87
src/fileis.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2024 DTB <trinity@trinity.moe>
|
||||
* 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;
|
||||
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::<Vec<String>>();
|
||||
|
||||
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_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; // file metadata
|
||||
let fmode; // file mode; see chmod(1p)
|
||||
let ftype; // file type
|
||||
|
||||
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
|
||||
}
|
||||
107
src/scrut.c
107
src/scrut.c
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023–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/.
|
||||
*/
|
||||
|
||||
#include <stdio.h> /* fprintf(3), stderr, NULL */
|
||||
#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 */
|
||||
|
||||
static char args[] = "bcdefghkprsuwxLS";
|
||||
static char ops[(sizeof args) / (sizeof *args)];
|
||||
static char *program_name = "scrut";
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
struct stat buf;
|
||||
int c;
|
||||
size_t i;
|
||||
char *p;
|
||||
|
||||
if(argc < 2)
|
||||
goto usage;
|
||||
|
||||
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;
|
||||
/* 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)
|
||||
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(
|
||||
(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);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
24
src/swab.rs
24
src/swab.rs
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -18,7 +19,7 @@
|
||||
|
||||
use std::{
|
||||
env::args,
|
||||
io::{ stdin, stdout, Error, ErrorKind, Read, Write },
|
||||
io::{ stdin, stdout, Error, Read, Write },
|
||||
process::ExitCode,
|
||||
vec::Vec
|
||||
};
|
||||
@@ -38,7 +39,7 @@ fn oserr(s: &str, e: Error) -> ExitCode {
|
||||
}
|
||||
|
||||
fn usage(s: &str) -> ExitCode {
|
||||
eprintln!("Usage: {} [-f] [-w word_size]", s);
|
||||
eprintln!("Usage: {} [-w word_size]", s);
|
||||
ExitCode::from(EX_USAGE as u8)
|
||||
}
|
||||
|
||||
@@ -48,24 +49,26 @@ fn main() -> ExitCode {
|
||||
let mut input = stdin();
|
||||
let mut output = stdout().lock();
|
||||
|
||||
let mut force = false;
|
||||
let mut wordsize: usize = 2;
|
||||
let mut optind: usize = 1; // argv[0]
|
||||
let mut wordsize: usize = 2; // Equivalent to dd(1p).
|
||||
|
||||
while let Some(opt) = argv.getopt("fw:") {
|
||||
while let Some(opt) = argv.getopt("w:") {
|
||||
match opt.opt() {
|
||||
Ok("f") => force = true,
|
||||
Ok("w") => {
|
||||
if let Some(arg) = opt.arg() {
|
||||
match arg.parse::<usize>() {
|
||||
Ok(w) if w % 2 == 0 => { wordsize = w; () },
|
||||
match opt.arg().unwrap().parse::<usize>() {
|
||||
Ok(w) if w % 2 == 0 => { wordsize = w; },
|
||||
_ => { return usage(&argv[0]); },
|
||||
}
|
||||
}
|
||||
optind = opt.ind();
|
||||
},
|
||||
_ => { return usage(&argv[0]); }
|
||||
}
|
||||
}
|
||||
|
||||
if optind < argv.len() {
|
||||
return usage(&argv[0]);
|
||||
}
|
||||
|
||||
buf.resize(wordsize, 0);
|
||||
|
||||
loop {
|
||||
@@ -83,7 +86,6 @@ fn main() -> ExitCode {
|
||||
break oserr(&argv[0], e)
|
||||
}
|
||||
},
|
||||
Err(e) if e.kind() == ErrorKind::Interrupted && force => continue,
|
||||
Err(e) => break oserr(&argv[0], e)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user