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
 | 
						|
}
 |