92 lines
2.7 KiB
Rust
92 lines
2.7 KiB
Rust
/*
|
|
* 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
|
|
}
|