Simplified Cmd trait, trimmed 600K from binary size

This commit is contained in:
Nathan Fisher 2023-01-13 01:08:32 -05:00
parent 76e7036da9
commit a8c8ba5819
27 changed files with 124 additions and 471 deletions

View File

@ -8,26 +8,10 @@ use std::{
}; };
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Base32 { pub struct Base32;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Base32 {
fn default() -> Self {
Self {
name: "base32",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Base32 { impl Cmd for Base32 {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("base32") Command::new("base32")
.author("Nathan Fisher") .author("Nathan Fisher")
@ -121,7 +105,7 @@ impl Cmd for Base32 {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -8,26 +8,10 @@ use std::{
}; };
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Base64 { pub struct Base64;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Base64 {
fn default() -> Self {
Self {
name: "base64",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Base64 { impl Cmd for Base64 {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> Command { fn cli(&self) -> Command {
Command::new("base64") Command::new("base64")
.author("Nathan Fisher") .author("Nathan Fisher")
@ -119,7 +103,7 @@ impl Cmd for Base64 {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }
@ -129,7 +113,7 @@ fn decode_base64(mut contents: String, ignore: bool) -> Result<(), Box<dyn Error
} else { } else {
contents = contents.replace('\n', ""); contents = contents.replace('\n', "");
} }
let decoded = BASE64.decode(&contents.as_bytes())?; let decoded = BASE64.decode(contents.as_bytes())?;
let output = String::from_utf8(decoded)?; let output = String::from_utf8(decoded)?;
println!("{}\n", output.trim_end()); println!("{}\n", output.trim_end());
Ok(()) Ok(())

View File

@ -2,28 +2,12 @@ use super::Cmd;
use clap::{Arg, ArgMatches, Command}; use clap::{Arg, ArgMatches, Command};
use std::io; use std::io;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Basename { pub struct Basename;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Basename {
fn default() -> Self {
Self {
name: "basename",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Basename { impl Cmd for Basename {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("basename")
.about("Print NAME with any leading directory components removed.") .about("Print NAME with any leading directory components removed.")
.long_about( .long_about(
"Print NAME with any leading directory components removed.\n\ "Print NAME with any leading directory components removed.\n\
@ -62,6 +46,6 @@ impl Cmd for Basename {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -11,20 +11,8 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Bootstrap { pub struct Bootstrap;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Bootstrap {
fn default() -> Self {
Self {
name: "bootstrap",
path: None,
}
}
}
impl Bootstrap { impl Bootstrap {
fn all() -> clap::Command { fn all() -> clap::Command {
@ -127,12 +115,8 @@ impl Bootstrap {
} }
impl Cmd for Bootstrap { impl Cmd for Bootstrap {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("bootstrap")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.author("Nathan Fisher") .author("Nathan Fisher")
.about("Install shitbox into the filesystem") .about("Install shitbox into the filesystem")
@ -212,7 +196,7 @@ impl Cmd for Bootstrap {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path None
} }
} }
@ -225,7 +209,8 @@ pub trait BootstrapCmd {
impl BootstrapCmd for dyn Cmd { impl BootstrapCmd for dyn Cmd {
fn completion(&self, outdir: &Path, gen: &str) -> Result<(), io::Error> { fn completion(&self, outdir: &Path, gen: &str) -> Result<(), io::Error> {
let name = self.name(); let cmd = self.cli();
let name = cmd.get_name();
let mut cmd = self.cli(); let mut cmd = self.cli();
if !outdir.exists() { if !outdir.exists() {
fs::create_dir_all(outdir)?; fs::create_dir_all(outdir)?;
@ -249,7 +234,7 @@ impl BootstrapCmd for dyn Cmd {
Some(p) => path.push(p.to_str(usr)), Some(p) => path.push(p.to_str(usr)),
None => return None, None => return None,
} }
path.push(self.name()); path.push(self.cli().get_name());
Some(path) Some(path)
} }
@ -289,7 +274,7 @@ impl BootstrapCmd for dyn Cmd {
fn manpage(&self, prefix: &str) -> Result<(), io::Error> { fn manpage(&self, prefix: &str) -> Result<(), io::Error> {
let command = self.cli(); let command = self.cli();
let fname = match self.name() { let fname = match command.get_name() {
"bootstrap" => "shitbox-bootstrap.1".to_string(), "bootstrap" => "shitbox-bootstrap.1".to_string(),
s => format!("{s}.1"), s => format!("{s}.1"),
}; };

View File

@ -5,10 +5,6 @@ use clap::Command;
pub struct Clear; pub struct Clear;
impl Cmd for Clear { impl Cmd for Clear {
fn name(&self) -> &str {
"clear"
}
fn cli(&self) -> Command { fn cli(&self) -> Command {
Command::new("clear") Command::new("clear")
.about("clear the terminal's screen") .about("clear the terminal's screen")

View File

@ -2,26 +2,10 @@ use super::Cmd;
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use std::path::Path; use std::path::Path;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Dirname { pub struct Dirname;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Dirname {
fn default() -> Self {
Self {
name: "dirname",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Dirname { impl Cmd for Dirname {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("dirname") Command::new("dirname")
.about("strip last component from file name") .about("strip last component from file name")
@ -61,6 +45,6 @@ impl Cmd for Dirname {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -1,30 +1,13 @@
use super::Cmd; use super::Cmd;
use crate::Path;
use clap::{Arg, Command}; use clap::{Arg, Command};
use std::{env, error::Error}; use std::{env, error::Error};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Echo { pub struct Echo;
name: &'static str,
path: Option<Path>,
}
impl Default for Echo {
fn default() -> Self {
Self {
name: "echo",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for Echo { impl Cmd for Echo {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("echo")
.about("Display a line of text") .about("Display a line of text")
.long_about("Echo the STRING(s) to standard output") .long_about("Echo the STRING(s) to standard output")
.author("Nathan Fisher") .author("Nathan Fisher")
@ -39,7 +22,7 @@ impl Cmd for Echo {
fn run(&self, _matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn Error>> { fn run(&self, _matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn Error>> {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let idx = match crate::progname() { let idx = match crate::progname() {
Some(s) if s.as_str() == self.name() => 1, Some(s) if s.as_str() == "echo" => 1,
Some(_) => 2, Some(_) => 2,
None => unreachable!(), None => unreachable!(),
}; };
@ -60,6 +43,6 @@ impl Cmd for Echo {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -1,30 +1,14 @@
use crate::Cmd; use super::Cmd;
use clap::{value_parser, Arg, ArgMatches, Command}; use clap::{value_parser, Arg, ArgMatches, Command};
use std::{ use std::{
error::Error, error::Error,
io::{self, BufRead}, io::{self, BufRead},
}; };
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Factor { pub struct Factor;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Factor {
fn default() -> Self {
Self {
name: "factor",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Factor { impl Cmd for Factor {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> Command { fn cli(&self) -> Command {
Command::new("factor") Command::new("factor")
.about("factor numbers") .about("factor numbers")
@ -59,7 +43,7 @@ impl Cmd for Factor {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -1,30 +1,13 @@
use super::Cmd; use super::Cmd;
use crate::Path;
use clap::Command; use clap::Command;
use std::{error::Error, process}; use std::{error::Error, process};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct False { pub struct False;
name: &'static str,
path: Option<Path>,
}
impl Default for False {
fn default() -> Self {
Self {
name: "false",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for False { impl Cmd for False {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("false")
.about("Does nothing unsuccessfully") .about("Does nothing unsuccessfully")
.long_about("Exit with a status code indicating failure") .long_about("Exit with a status code indicating failure")
.author("Nathan Fisher") .author("Nathan Fisher")
@ -34,7 +17,7 @@ impl Cmd for False {
process::exit(1); process::exit(1);
} }
fn path(&self) -> Option<Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -6,28 +6,12 @@ use std::{
}; };
use textwrap::{fill, wrap_algorithms::WrapAlgorithm}; use textwrap::{fill, wrap_algorithms::WrapAlgorithm};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Fold { pub struct Fold;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Fold {
fn default() -> Self {
Self {
name: "fold",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Fold { impl Cmd for Fold {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> Command { fn cli(&self) -> Command {
Command::new(self.name) Command::new("fold")
.about("Wrap each input line to fit in specified width") .about("Wrap each input line to fit in specified width")
.author("Nathan Fisher") .author("Nathan Fisher")
.after_long_help("With no FILE, or when FILE is -, read standard input") .after_long_help("With no FILE, or when FILE is -, read standard input")
@ -84,12 +68,12 @@ impl Cmd for Fold {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }
fn wrap_line(line: &str, args: &ArgMatches) { fn wrap_line(line: &str, args: &ArgMatches) {
let width = args.get_one("WIDTH").map(|x| *x).unwrap_or(80); let width = args.get_one("WIDTH").map_or(80, |x| *x);
if args.get_flag("OPTIMAL") { if args.get_flag("OPTIMAL") {
let line = line.replace('\t', " "); let line = line.replace('\t', " ");
let opts = textwrap::Options::new(width).wrap_algorithm(WrapAlgorithm::new_optimal_fit()); let opts = textwrap::Options::new(width).wrap_algorithm(WrapAlgorithm::new_optimal_fit());

View File

@ -7,10 +7,6 @@ use std::io;
pub struct Groups; pub struct Groups;
impl Cmd for Groups { impl Cmd for Groups {
fn name(&self) -> &str {
"groups"
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("groups") Command::new("groups")
.about("display current group names") .about("display current group names")

View File

@ -9,28 +9,12 @@ use std::{
}; };
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Head { pub struct Head;
name: &'static str,
path: Option<Path>,
}
impl Default for Head {
fn default() -> Self {
Self {
name: "head",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for Head { impl Cmd for Head {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> Command { fn cli(&self) -> Command {
Command::new(self.name) Command::new("head")
.author("Nathan Fisher") .author("Nathan Fisher")
.about("Display first lines of a file") .about("Display first lines of a file")
.long_about( .long_about(
@ -133,7 +117,7 @@ impl Cmd for Head {
} }
fn path(&self) -> Option<Path> { fn path(&self) -> Option<Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -1,27 +1,14 @@
use super::Cmd; use super::Cmd;
use crate::Path;
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use std::{error::Error, io}; use std::{error::Error, io};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Hostname { pub struct Hostname;
name: &'static str,
path: Option<Path>,
}
pub const HOSTNAME: Hostname = Hostname {
name: "hostname",
path: Some(Path::Bin),
};
impl Cmd for Hostname { impl Cmd for Hostname {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("hostname")
.author("The JeanG3nie <jeang3nie@hitchhiker-linux.org>") .author("Nathan Fisher")
.about("Prints the name of the current host. The super-user can set the host name by supplying an argument.") .about("Prints the name of the current host. The super-user can set the host name by supplying an argument.")
.args([ .args([
Arg::new("NAME") Arg::new("NAME")
@ -62,6 +49,6 @@ impl Cmd for Hostname {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -6,10 +6,6 @@ use std::{fs, io};
pub struct Link; pub struct Link;
impl Cmd for Link { impl Cmd for Link {
fn name(&self) -> &str {
"link"
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("link") Command::new("link")
.about("call the link function to create a link to a file") .about("call the link function to create a link to a file")
@ -29,23 +25,17 @@ impl Cmd for Link {
let Some(matches) = matches else { let Some(matches) = matches else {
return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input"))); return Err(Box::new(io::Error::new(io::ErrorKind::Other, "no input")));
}; };
let f1 = match matches.get_one::<String>("file1") { let Some(f1) = matches.get_one::<String>("file1") else {
Some(s) => s, return Err(Box::new(io::Error::new(
None => { io::ErrorKind::Other,
return Err(Box::new(io::Error::new( "missing file 1",
io::ErrorKind::Other, )))
"missing file 1",
)))
}
}; };
let f2 = match matches.get_one::<String>("file2") { let Some(f2) = matches.get_one::<String>("file2") else {
Some(s) => s, return Err(Box::new(io::Error::new(
None => { io::ErrorKind::Other,
return Err(Box::new(io::Error::new( "missing file 2",
io::ErrorKind::Other, )))
"missing file 2",
)))
}
}; };
fs::hard_link(f1, f2)?; fs::hard_link(f1, f2)?;
if matches.get_flag("verbose") { if matches.get_flag("verbose") {

View File

@ -48,12 +48,12 @@ pub use {
}; };
pub trait Cmd: fmt::Debug + Sync { pub trait Cmd: fmt::Debug + Sync {
fn name(&self) -> &str;
fn cli(&self) -> clap::Command; fn cli(&self) -> clap::Command;
fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>>; fn run(&self, matches: Option<&ArgMatches>) -> Result<(), Box<dyn Error>>;
fn path(&self) -> Option<crate::Path>; fn path(&self) -> Option<crate::Path>;
} }
#[must_use]
pub fn get(name: &str) -> Option<Box<dyn Cmd>> { pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
match name { match name {
"base64" => Some(Box::new(Base64::default())), "base64" => Some(Box::new(Base64::default())),

View File

@ -9,28 +9,12 @@ use std::{
process, process,
}; };
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Mountpoint { pub struct Mountpoint;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Mountpoint {
fn default() -> Self {
Self {
name: "mountpoint",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for Mountpoint { impl Cmd for Mountpoint {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("mountpoint")
.about("see if a directory or file is a mountpoint") .about("see if a directory or file is a mountpoint")
.author("Nathan Fisher") .author("Nathan Fisher")
.after_long_help( .after_long_help(
@ -77,12 +61,11 @@ impl Cmd for Mountpoint {
}; };
let file = matches.get_one::<String>("file").unwrap(); let file = matches.get_one::<String>("file").unwrap();
if matches.get_flag("fs-devno") { if matches.get_flag("fs-devno") {
match fs_devno(file)? { if let Some(maj_min) = fs_devno(file)? {
Some(maj_min) => println!("{maj_min}"), println!("{maj_min}");
None => { } else {
println!("{file} is not a mountpoint"); println!("{file} is not a mountpoint");
process::exit(32); process::exit(32);
}
} }
} else if matches.get_flag("devno") { } else if matches.get_flag("devno") {
let devno = devno(file)?; let devno = devno(file)?;
@ -94,18 +77,16 @@ impl Cmd for Mountpoint {
println!("{file} is a mountpoint"); println!("{file} is a mountpoint");
} }
return Ok(()); return Ok(());
} else { } else if !matches.get_flag("quiet") {
if !matches.get_flag("quiet") { println!("{file} is not a mountpoint");
println!("{file} is not a mountpoint");
}
process::exit(32);
} }
process::exit(32);
} }
Ok(()) Ok(())
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }
@ -114,7 +95,7 @@ fn is_mountpoint(path: &str) -> Result<bool, Box<dyn Error>> {
let reader = BufReader::new(fd); let reader = BufReader::new(fd);
for line in reader.lines() { for line in reader.lines() {
let line = line?; let line = line?;
if let Some(mntpt) = line.split_whitespace().skip(1).next() { if let Some(mntpt) = line.split_whitespace().nth(1) {
if mntpt == path { if mntpt == path {
return Ok(true); return Ok(true);
} }
@ -135,7 +116,7 @@ fn fs_devno(path: &str) -> Result<Option<String>, Box<dyn Error>> {
let line = line?; let line = line?;
let mut line = line.split_whitespace().skip(2); let mut line = line.split_whitespace().skip(2);
if let Some(maj_min) = line.next() { if let Some(maj_min) = line.next() {
if let Some(mntpt) = line.skip(1).next() { if let Some(mntpt) = line.nth(1) {
if mntpt == path { if mntpt == path {
return Ok(Some(String::from(maj_min))); return Ok(Some(String::from(maj_min)));
} }

View File

@ -2,28 +2,12 @@ use super::Cmd;
use clap::Command; use clap::Command;
use std::process; use std::process;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Nologin { pub struct Nologin;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Nologin {
fn default() -> Self {
Self {
name: "nologin",
path: Some(crate::Path::Sbin),
}
}
}
impl Cmd for Nologin { impl Cmd for Nologin {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("nologin")
.author("Nathan Fisher") .author("Nathan Fisher")
.about("Denies a user account login ability") .about("Denies a user account login ability")
} }
@ -34,6 +18,6 @@ impl Cmd for Nologin {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Sbin)
} }
} }

View File

@ -2,28 +2,12 @@ use super::Cmd;
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use std::io; use std::io;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Nproc { pub struct Nproc;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Nproc {
fn default() -> Self {
Self {
name: "nproc",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Nproc { impl Cmd for Nproc {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("nproc")
.author("Nathan Fisher") .author("Nathan Fisher")
.about("Print the number of processing units available") .about("Print the number of processing units available")
.arg( .arg(
@ -48,6 +32,6 @@ impl Cmd for Nproc {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -1,4 +1,4 @@
use crate::Cmd; use super::Cmd;
use clap::{Arg, ArgAction, Command}; use clap::{Arg, ArgAction, Command};
use std::{ use std::{
fs::File, fs::File,
@ -6,28 +6,12 @@ use std::{
}; };
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Rev { pub struct Rev;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Rev {
fn default() -> Self {
Self {
name: "rev",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Rev { impl Cmd for Rev {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("rev")
.about("reverse lines characterwise") .about("reverse lines characterwise")
.author("Nathan Fisher") .author("Nathan Fisher")
.args([ .args([
@ -82,7 +66,7 @@ impl Cmd for Rev {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -6,31 +6,10 @@ use std::{
process, process,
}; };
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Shitbox { pub struct Shitbox;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Shitbox {
fn default() -> Self {
Self {
name: "shitbox",
path: None,
}
}
}
pub const SHITBOX: Shitbox = Shitbox {
name: "shitbox",
path: None,
};
impl Cmd for Shitbox { impl Cmd for Shitbox {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
let subcommands: Vec<Command> = { let subcommands: Vec<Command> = {
let mut s = vec![]; let mut s = vec![];
@ -44,7 +23,7 @@ impl Cmd for Shitbox {
} }
s s
}; };
Command::new(self.name) Command::new("shitbox")
.about("The box store multitool of embedded Linux") .about("The box store multitool of embedded Linux")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.propagate_version(true) .propagate_version(true)
@ -70,6 +49,6 @@ impl Cmd for Shitbox {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path None
} }
} }

View File

@ -1,30 +1,13 @@
use super::Cmd; use super::Cmd;
use crate::Path;
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use std::{env, error::Error, thread, time::Duration}; use std::{env, error::Error, thread, time::Duration};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Sleep { pub struct Sleep;
name: &'static str,
path: Option<Path>,
}
impl Default for Sleep {
fn default() -> Self {
Self {
name: "sleep",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for Sleep { impl Cmd for Sleep {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("sleep")
.about("Suspend execution for an interval of time") .about("Suspend execution for an interval of time")
.long_about( .long_about(
"The sleep utility suspends execution for a minimum of the specified number of seconds.\n\ "The sleep utility suspends execution for a minimum of the specified number of seconds.\n\
@ -54,7 +37,7 @@ impl Cmd for Sleep {
Ok(()) Ok(())
} }
fn path(&self) -> Option<Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -1,30 +1,13 @@
use super::Cmd; use super::Cmd;
use crate::Path;
use clap::Command; use clap::Command;
use std::{error::Error, process}; use std::{error::Error, process};
#[derive(Debug)] #[derive(Debug, Default)]
pub struct True { pub struct True;
name: &'static str,
path: Option<Path>,
}
impl Default for True {
fn default() -> Self {
Self {
name: "true",
path: Some(crate::Path::Bin),
}
}
}
impl Cmd for True { impl Cmd for True {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("true")
.about("Does nothing successfully") .about("Does nothing successfully")
.long_about("Exit with a status code indicating success") .long_about("Exit with a status code indicating success")
.author("Nathan Fisher") .author("Nathan Fisher")
@ -34,7 +17,7 @@ impl Cmd for True {
process::exit(0); process::exit(0);
} }
fn path(&self) -> Option<Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::Bin)
} }
} }

View File

@ -6,10 +6,6 @@ use std::{env, fs::File, io, os::unix::prelude::MetadataExt, path::PathBuf, proc
pub struct Which; pub struct Which;
impl Cmd for Which { impl Cmd for Which {
fn name(&self) -> &str {
"which"
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("which") Command::new("which")
.about("Write the full path of COMMAND(s) to standard output") .about("Write the full path of COMMAND(s) to standard output")
@ -31,12 +27,11 @@ impl Cmd for Which {
let mut failures = 0; let mut failures = 0;
if let Some(commands) = matches.get_many::<String>("COMMAND") { if let Some(commands) = matches.get_many::<String>("COMMAND") {
for command in commands { for command in commands {
match which(command, &path) { if let Some(p) = which(command, &path) {
Some(p) => println!("{p}"), println!("{p}");
None => { } else {
println!("{}: no {} in ({})", self.name(), command, &rawpath); println!("which: no {} in ({})", command, &rawpath);
failures += 1; failures += 1;
}
} }
} }
} else { } else {

View File

@ -1,19 +1,14 @@
use std::ffi::CStr;
use { use {
super::Cmd, super::Cmd,
clap::Command, clap::Command,
libc::{geteuid, getpwuid}, libc::{geteuid, getpwuid},
std::ffi::CStr,
}; };
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Whoami; pub struct Whoami;
impl Cmd for Whoami { impl Cmd for Whoami {
fn name(&self) -> &str {
"whoami"
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new("whoami") Command::new("whoami")
.about("print effective user name") .about("print effective user name")

View File

@ -2,28 +2,12 @@ use super::Cmd;
use clap::{Arg, Command}; use clap::{Arg, Command};
use std::io; use std::io;
#[derive(Debug)] #[derive(Debug, Default)]
pub struct Yes { pub struct Yes;
name: &'static str,
path: Option<crate::Path>,
}
impl Default for Yes {
fn default() -> Self {
Self {
name: "yes",
path: Some(crate::Path::UsrBin),
}
}
}
impl Cmd for Yes { impl Cmd for Yes {
fn name(&self) -> &str {
self.name
}
fn cli(&self) -> clap::Command { fn cli(&self) -> clap::Command {
Command::new(self.name) Command::new("yes")
.about("output a string repeatedly until killed") .about("output a string repeatedly until killed")
.author("Nathan Fisher") .author("Nathan Fisher")
.arg(Arg::new("msg").num_args(1).default_value("y")) .arg(Arg::new("msg").num_args(1).default_value("y"))
@ -40,6 +24,6 @@ impl Cmd for Yes {
} }
fn path(&self) -> Option<crate::Path> { fn path(&self) -> Option<crate::Path> {
self.path Some(crate::Path::UsrBin)
} }
} }

View File

@ -16,14 +16,13 @@ pub enum Path {
} }
impl Path { impl Path {
#[must_use]
pub fn to_str(&self, usr: bool) -> &'static str { pub fn to_str(&self, usr: bool) -> &'static str {
match self { match self {
Self::Bin => "bin",
Self::UsrBin if usr => "usr/bin", Self::UsrBin if usr => "usr/bin",
Self::UsrBin => "bin",
Self::Sbin => "sbin",
Self::UsrSbin if usr => "usr/sbin", Self::UsrSbin if usr => "usr/sbin",
Self::UsrSbin => "sbin", Self::Bin | Self::UsrBin => "bin",
Self::Sbin | Self::UsrSbin => "sbin",
} }
} }
} }

View File

@ -25,35 +25,28 @@ pub fn get_eusername<'a>() -> Result<&'a str, std::str::Utf8Error> {
} }
pub fn get_uid_for_name(name: &str) -> Option<u32> { pub fn get_uid_for_name(name: &str) -> Option<u32> {
let user = match CString::new(name.as_bytes()) { let Ok(user) = CString::new(name.as_bytes()) else { return None };
Ok(n) => n, unsafe {
Err(_) => return None,
};
let uid = unsafe {
let pw = libc::getpwnam(user.as_ptr()); let pw = libc::getpwnam(user.as_ptr());
if pw.is_null() { if pw.is_null() {
None None
} else { } else {
Some((*pw).pw_uid) Some((*pw).pw_uid)
} }
}; }
uid
} }
#[must_use]
pub fn get_pgid_for_name(name: &str) -> Option<u32> { pub fn get_pgid_for_name(name: &str) -> Option<u32> {
let user = match CString::new(name.as_bytes()) { let Ok(user) = CString::new(name.as_bytes()) else { return None };
Ok(n) => n, unsafe {
Err(_) => return None,
};
let gid = unsafe {
let pw = libc::getpwnam(user.as_ptr()); let pw = libc::getpwnam(user.as_ptr());
if pw.is_null() { if pw.is_null() {
None None
} else { } else {
Some((*pw).pw_gid) Some((*pw).pw_gid)
} }
}; }
gid
} }
pub fn get_grpname<'a>() -> Result<&'a str, std::str::Utf8Error> { pub fn get_grpname<'a>() -> Result<&'a str, std::str::Utf8Error> {
@ -89,6 +82,7 @@ pub fn get_gids() -> Result<Vec<u32>, num::TryFromIntError> {
Ok(buf) Ok(buf)
} }
#[allow(clippy::cast_sign_loss)]
pub fn get_gids_for_name(name: &str) -> Result<Vec<u32>, Box<dyn Error>> { pub fn get_gids_for_name(name: &str) -> Result<Vec<u32>, Box<dyn Error>> {
let gid = match get_pgid_for_name(name) { let gid = match get_pgid_for_name(name) {
Some(g) => g as libc::gid_t, Some(g) => g as libc::gid_t,
@ -111,7 +105,7 @@ pub fn get_gids_for_name(name: &str) -> Result<Vec<u32>, Box<dyn Error>> {
))); )));
} }
} }
Ok(buf[0..ct as usize].iter().map(|x| *x as u32).collect()) Ok(buf[0..ct as usize].to_vec())
} }
pub fn get_group_names() -> Result<Vec<String>, Box<dyn Error>> { pub fn get_group_names() -> Result<Vec<String>, Box<dyn Error>> {