Add `mktemp` applet

This commit is contained in:
Nathan Fisher 2023-01-24 00:51:36 -05:00
parent 5076dcc350
commit 754f604603
9 changed files with 111 additions and 18 deletions

View File

@ -13,9 +13,7 @@ impl Cmd for Hostid {
}
fn run(&self, _matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
let hostid = unsafe {
libc::gethostid()
};
let hostid = unsafe { libc::gethostid() };
let hostid: String = format!("{hostid:x}").chars().skip(8).collect();
println!("{}", hostid);
Ok(())

88
src/cmd/mktemp/mod.rs Normal file
View File

@ -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<dyn std::error::Error>> {
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::<String>("tmpdir") {
PathBuf::from(t)
} else {
PathBuf::from("/tmp")
};
let fname = if let Some(p) = matches.get_one::<String>("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<crate::Path> {
Some(crate::Path::UsrBin)
}
}

View File

@ -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<Box<dyn Cmd>> {
"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<Box<dyn Cmd>> {
}
}
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",

View File

@ -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<dyn Error>> {
fn get_values<'a>(
file: &'a str,
totals: &mut Values<'a>,
flags: u32,
) -> Result<(), Box<dyn Error>> {
let contents = if file == "-" {
let mut buf = String::new();
stdin().read_to_string(&mut buf)?;

View File

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

View File

@ -96,4 +96,3 @@ impl Bit {
}
}
}

View File

@ -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]

View File

@ -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)]

View File

@ -46,4 +46,3 @@ impl BitOrAssign<Who> for u32 {
*self = *self | rhs;
}
}