From 754f604603d4d215ef72ab1de89976acad4dc38c Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Tue, 24 Jan 2023 00:51:36 -0500 Subject: [PATCH] Add `mktemp` applet --- src/cmd/hostid/mod.rs | 4 +- src/cmd/mktemp/mod.rs | 88 +++++++++++++++++++++++++++++++++++++++++++ src/cmd/mod.rs | 5 ++- src/cmd/wc/mod.rs | 14 +++++-- src/cmd/which/mod.rs | 2 +- src/mode/bit.rs | 1 - src/mode/mod.rs | 6 ++- src/mode/parser.rs | 8 +--- src/mode/who.rs | 1 - 9 files changed, 111 insertions(+), 18 deletions(-) create mode 100644 src/cmd/mktemp/mod.rs diff --git a/src/cmd/hostid/mod.rs b/src/cmd/hostid/mod.rs index 736a8f9..9741b5b 100644 --- a/src/cmd/hostid/mod.rs +++ b/src/cmd/hostid/mod.rs @@ -13,9 +13,7 @@ impl Cmd for Hostid { } fn run(&self, _matches: Option<&clap::ArgMatches>) -> Result<(), Box> { - let hostid = unsafe { - libc::gethostid() - }; + let hostid = unsafe { libc::gethostid() }; let hostid: String = format!("{hostid:x}").chars().skip(8).collect(); println!("{}", hostid); Ok(()) diff --git a/src/cmd/mktemp/mod.rs b/src/cmd/mktemp/mod.rs new file mode 100644 index 0000000..bda2c46 --- /dev/null +++ b/src/cmd/mktemp/mod.rs @@ -0,0 +1,88 @@ +use super::Cmd; +use clap::{Arg, ArgAction, Command, ValueHint}; +use std::{ffi::CString, fs, io, path::PathBuf}; + +#[derive(Debug, Default)] +pub struct MkTemp; + +impl Cmd for MkTemp { + fn cli(&self) -> clap::Command { + Command::new("mktemp") + .about("create a unique temporary file") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("directory") + .short('d') + .long("directory") + .help("create a directory instead of a file") + .action(ArgAction::SetTrue), + Arg::new("tmpdir") + .short('p') + .long("tmpdir") + .help("specify the directory to create temporary files in") + .value_hint(ValueHint::DirPath) + .num_args(1), + Arg::new("prefix") + .short('t') + .long("template") + .help("specify a prefix to append to the created files") + .num_args(1), + Arg::new("dryrun") + .short('u') + .long("dryrun") + .help("immediately remove created files and directories") + .action(ArgAction::SetTrue), + ]) + } + + fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box> { + let Some(matches) = matches else { + return Err(io::Error::new(io::ErrorKind::Other, "no input").into()); + }; + let mut path = if let Some(t) = matches.get_one::("tmpdir") { + PathBuf::from(t) + } else { + PathBuf::from("/tmp") + }; + let fname = if let Some(p) = matches.get_one::("prefix") { + format!("{p}.XXXXXX") + } else { + "mktemp.XXXXXX".to_string() + }; + path.push(&fname); + let fname = path + .to_str() + .ok_or(io::Error::new(io::ErrorKind::Other, "utf8 error"))?; + let cname = CString::new(fname)?; + let ptr = cname.into_raw(); + if matches.get_flag("directory") { + let raw_fd = unsafe { libc::mkdtemp(ptr) }; + if raw_fd.is_null() { + return Err(io::Error::last_os_error().into()); + } + let cname = unsafe { CString::from_raw(ptr) }; + let fname = cname.to_str()?; + println!("{fname}"); + if matches.get_flag("dryrun") { + fs::remove_dir(&fname)?; + } + } else { + let raw_fd = unsafe { libc::mkstemp(ptr) }; + if raw_fd == -1 { + return Err(io::Error::last_os_error().into()); + } + let cname = unsafe { CString::from_raw(ptr) }; + let fname = cname.to_str()?; + println!("{fname}"); + if matches.get_flag("dryrun") { + fs::remove_file(&fname)?; + } + } + Ok(()) + } + + fn path(&self) -> Option { + Some(crate::Path::UsrBin) + } +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 6914942..67e091a 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -30,6 +30,7 @@ mod logname; mod ls; mod mkfifo; mod mknod; +mod mktemp; mod mountpoint; mod mv; mod nologin; @@ -93,6 +94,7 @@ pub fn get(name: &str) -> Option> { "logname" => Some(Box::new(logname::Logname::default())), "mkfifo" => Some(Box::new(mkfifo::MkFifo::default())), "mknod" => Some(Box::new(mknod::MkNod::default())), + "mktemp" => Some(Box::new(mktemp::MkTemp::default())), "mountpoint" => Some(Box::new(mountpoint::Mountpoint::default())), "nologin" => Some(Box::new(nologin::Nologin::default())), "nproc" => Some(Box::new(nproc::Nproc::default())), @@ -116,7 +118,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 42] = [ +pub static COMMANDS: [&str; 43] = [ "base32", "base64", "basename", @@ -140,6 +142,7 @@ pub static COMMANDS: [&str; 42] = [ "logname", "mkfifo", "mknod", + "mktemp", "mountpoint", "nologin", "nproc", diff --git a/src/cmd/wc/mod.rs b/src/cmd/wc/mod.rs index fd197dd..545b2bf 100644 --- a/src/cmd/wc/mod.rs +++ b/src/cmd/wc/mod.rs @@ -1,13 +1,13 @@ use super::Cmd; use crate::bitflags::BitFlags; -use clap::{Arg, ArgMatches, Command, ArgAction}; +use clap::{Arg, ArgAction, ArgMatches, Command}; use std::{ cmp, error::Error, fmt::{self, Write}, fs, io::{self, stdin, Read}, - ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, AddAssign}, + ops::{AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign}, str::FromStr, }; @@ -73,7 +73,9 @@ impl Cmd for Wc { name: "Total", ..Values::default() }; - files.iter().try_for_each(|f| get_values(f, &mut totals, flags))?; + files + .iter() + .try_for_each(|f| get_values(f, &mut totals, flags))?; if files.len() > 1 { totals.print_values(flags)?; } @@ -85,7 +87,11 @@ impl Cmd for Wc { } } -fn get_values<'a>(file: &'a str, totals: &mut Values<'a>, flags: u32) -> Result<(), Box> { +fn get_values<'a>( + file: &'a str, + totals: &mut Values<'a>, + flags: u32, +) -> Result<(), Box> { let contents = if file == "-" { let mut buf = String::new(); stdin().read_to_string(&mut buf)?; diff --git a/src/cmd/which/mod.rs b/src/cmd/which/mod.rs index da2238a..710a0ff 100644 --- a/src/cmd/which/mod.rs +++ b/src/cmd/which/mod.rs @@ -1,5 +1,5 @@ -use crate::{bitflags::BitFlags, mode::Bit}; use super::Cmd; +use crate::{bitflags::BitFlags, mode::Bit}; use clap::{Arg, Command}; use std::{env, fs::File, io, os::unix::prelude::MetadataExt, path::PathBuf, process}; diff --git a/src/mode/bit.rs b/src/mode/bit.rs index 4f15870..62399f3 100644 --- a/src/mode/bit.rs +++ b/src/mode/bit.rs @@ -96,4 +96,3 @@ impl Bit { } } } - diff --git a/src/mode/mod.rs b/src/mode/mod.rs index 8ba79f8..c5b6697 100644 --- a/src/mode/mod.rs +++ b/src/mode/mod.rs @@ -4,7 +4,11 @@ mod parser; mod who; use std::fmt::{self, Write}; -pub use {bit::Bit, parser::{ParseError, Parser}, who::Who}; +pub use { + bit::Bit, + parser::{ParseError, Parser}, + who::Who, +}; /// Gets the umask for the current user #[must_use] diff --git a/src/mode/parser.rs b/src/mode/parser.rs index afbb7e4..bb5e034 100644 --- a/src/mode/parser.rs +++ b/src/mode/parser.rs @@ -1,10 +1,6 @@ -use crate::bitflags::BitFlags; use super::{get_umask, Bit, Who}; -use std::{ - error, - fmt::Display, - num::ParseIntError, -}; +use crate::bitflags::BitFlags; +use std::{error, fmt::Display, num::ParseIntError}; /// Errors which might occur when parsing Unix permissions from a string #[derive(Debug, PartialEq)] diff --git a/src/mode/who.rs b/src/mode/who.rs index c5b7992..f1f21c8 100644 --- a/src/mode/who.rs +++ b/src/mode/who.rs @@ -46,4 +46,3 @@ impl BitOrAssign for u32 { *self = *self | rhs; } } -