Add `touch` applet

This commit is contained in:
Nathan Fisher 2023-02-24 19:07:53 -05:00
parent 44914ced6f
commit 226bc46888
5 changed files with 460 additions and 9 deletions

331
Cargo.lock generated
View File

@ -11,6 +11,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
@ -43,6 +52,12 @@ dependencies = [
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bindgen"
version = "0.58.1"
@ -120,6 +135,18 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cexpr"
version = "0.4.0"
@ -135,6 +162,18 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
dependencies = [
"iana-time-zone",
"num-integer",
"num-traits",
"winapi",
]
[[package]]
name = "clang-sys"
version = "1.6.0"
@ -173,9 +212,9 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.1.2"
version = "4.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd125be87bf4c255ebc50de0b7f4d2a6201e8ac3dc86e39c0ad081dc5e7236fe"
checksum = "0012995dc3a54314f4710f5631d74767e73c534b8757221708303e48eef7a19b"
dependencies = [
"clap 4.1.6",
]
@ -192,29 +231,45 @@ dependencies = [
[[package]]
name = "clap_lex"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade"
checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "clap_mangen"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48283ce8d5cd9513633949a674a0442bcb507ab61ed6533863437052ddbb494b"
checksum = "bb0f09a0ca8f0dd8ac92c546b426f466ef19828185c6d504c80c48c9c2768ed9"
dependencies = [
"clap 4.1.6",
"roff",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "constant_time_eq"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279"
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.5"
@ -234,6 +289,50 @@ dependencies = [
"typenum",
]
[[package]]
name = "cxx"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf"
[[package]]
name = "cxxbridge-macro"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "data-encoding"
version = "2.3.3"
@ -267,6 +366,18 @@ dependencies = [
name = "errno"
version = "0.1.0"
[[package]]
name = "filetime"
version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"windows-sys",
]
[[package]]
name = "generic-array"
version = "0.14.6"
@ -307,6 +418,39 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -335,6 +479,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "log"
version = "0.4.17"
@ -378,6 +531,31 @@ dependencies = [
"version_check",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "os_str_bytes"
version = "6.4.1"
@ -421,6 +599,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.7.1"
@ -471,6 +658,12 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
[[package]]
name = "scratch"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2"
[[package]]
name = "sha1"
version = "0.10.5"
@ -500,6 +693,7 @@ dependencies = [
"atty",
"bitflags-mini",
"blake2b_simd",
"chrono",
"clap 4.1.6",
"clap_complete",
"clap_complete_nushell",
@ -507,6 +701,7 @@ dependencies = [
"data-encoding",
"digest",
"errno",
"filetime",
"lazy_static",
"libc",
"md-5",
@ -569,9 +764,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
dependencies = [
"proc-macro2",
"quote",
@ -698,6 +893,60 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "which"
version = "3.1.1"
@ -737,3 +986,69 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

View File

@ -30,6 +30,7 @@ clap_complete_nushell = "0.1"
clap_mangen = "0.2"
data-encoding = "2.3"
digest = "0.10"
filetime = "0.2"
lazy_static = "1.4"
libc = { workspace = true }
md5 = { version = "0.10", package = "md-5" }
@ -50,6 +51,11 @@ version = "4.1"
default_features = false
features = ["std", "help", "usage", "error-context"]
[dependencies.chrono]
version = "0.4"
default_features = false
features = ["std", "alloc", "clock"]
[dependencies.errno]
package = "errno"
path = "errno"

View File

@ -63,6 +63,7 @@ code between applets, making for an overall smaller binary.
- swaplabel
- swapoff
- sync
- touch
- true
- umount
- unlink

View File

@ -45,6 +45,7 @@ mod rm;
mod rmdir;
mod sleep;
mod sync;
mod touch;
mod r#true;
mod unlink;
mod wc;
@ -94,6 +95,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
"rmdir" => Some(Box::new(rmdir::Rmdir::default())),
"sleep" => Some(Box::new(sleep::Sleep::default())),
"sync" => Some(Box::new(sync::Sync::default())),
"touch" => Some(Box::new(touch::Touch::default())),
"true" => Some(Box::new(r#true::True::default())),
"unlink" => Some(Box::new(unlink::Unlink::default())),
"wc" => Some(Box::new(wc::Wc::default())),
@ -104,7 +106,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
}
}
pub static COMMANDS: [&str; 43] = [
pub static COMMANDS: [&str; 44] = [
"base32",
"base64",
"basename",
@ -142,6 +144,7 @@ pub static COMMANDS: [&str; 43] = [
"rmdir",
"sleep",
"sync",
"touch",
"true",
"unlink",
"wc",

View File

@ -0,0 +1,126 @@
use chrono::DateTime;
use clap::{value_parser, Arg, ArgAction, ArgGroup, Command};
use filetime::*;
use shitbox::Cmd;
use std::{error::Error, fs::File, io, path::PathBuf};
#[derive(Debug, Default)]
pub struct Touch;
impl Cmd for Touch {
fn cli(&self) -> clap::Command {
Command::new("touch")
.about("set file timestamps")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([
Arg::new("access")
.help("set the access time of the file")
.short('a')
.long("access")
.action(ArgAction::SetTrue),
Arg::new("modify")
.help("set the modification time of the file")
.short('m')
.long("modification")
.conflicts_with("access")
.action(ArgAction::SetTrue),
Arg::new("create")
.help("Don't create file if it doesn't exist, not affecting exit status.")
.short('c')
.long("nocreate")
.action(ArgAction::SetFalse),
Arg::new("datetime")
.help("Set the time of the format YYYY-MM-DDThh:mm:SS[Z] used for [-am].")
.short('d')
.long("datetime")
.value_name("time")
.num_args(1),
Arg::new("ref")
.help("Set the time used for [-am] to the modification time of ref_file.")
.short('r')
.long("ref")
.value_name("file")
.num_args(1),
Arg::new("epoch")
.help(
"Set the time used for [-am] given as the number of seconds \
since the Unix epoch 1970-01-01T00:00:00Z.",
)
.short('T')
.long("epoch")
.value_name("time")
.value_parser(value_parser!(i64))
.num_args(1),
Arg::new("time")
.help("Set the time of the format [[CC]YY]MMDDhhmm[.SS] used for [-am].")
.short('t')
.long("time")
.value_name("time")
.num_args(1),
Arg::new("file").num_args(1..).required(true),
])
.group(
ArgGroup::new("timespec")
.args(["datetime", "ref", "epoch", "time"])
.multiple(false),
)
}
fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
let ft = if let Some(time) = matches.get_one::<String>("datetime") {
FileTime::from_unix_time(parse_datetime(time)?, 0)
} else if let Some(time) = matches.get_one::<String>("time") {
FileTime::from_unix_time(parse_time(time)?, 0)
} else if let Some(ep) = matches.get_one::<i64>("epoch") {
FileTime::from_unix_time(*ep, 0)
} else if let Some(file_ref) = matches.get_one::<String>("ref") {
let meta = File::open(file_ref)?.metadata()?;
FileTime::from_last_modification_time(&meta)
} else {
FileTime::now()
};
if let Some(files) = matches.get_many::<String>("file") {
for f in files {
let path = PathBuf::from(f);
if !path.exists() && matches.get_flag("create") {
File::create(&path)?;
}
if matches.get_flag("access") {
set_file_atime(&path, ft)?;
} else if matches.get_flag("modify") {
set_file_mtime(&path, ft)?;
} else {
set_file_times(&path, ft, ft)?;
}
}
}
Ok(())
}
fn path(&self) -> Option<shitbox::Path> {
Some(shitbox::Path::UsrBin)
}
}
fn parse_datetime(time: &str) -> Result<i64, Box<dyn Error>> {
match time.len() {
19 => Ok(DateTime::parse_from_str(time, "%Y-%m-%dT%H:%M:%S")?.timestamp()),
25 => Ok(DateTime::parse_from_rfc3339(time)?.timestamp()),
_ => Err(io::Error::new(io::ErrorKind::Other, "invalid datetime string").into()),
}
}
fn parse_time(time: &str) -> Result<i64, Box<dyn Error>> {
let dt = match time.len() {
8 => DateTime::parse_from_str(time, "%m%d%H%M")?,
10 => DateTime::parse_from_str(time, "%y%m%d%H%M")?,
11 => DateTime::parse_from_str(time, "%m%d%H%M.%S")?,
12 => DateTime::parse_from_str(time, "%Y%m%d%H%M")?,
13 => DateTime::parse_from_str(time, "%y%m%d%H%M.%S")?,
15 => DateTime::parse_from_str(time, "%Y%m%d%H%M.%S")?,
_ => return Err(io::Error::new(io::ErrorKind::Other, "invalid datetime string").into()),
};
Ok(dt.timestamp())
}