From 226bc46888506a22c9c20276c3111e16c3e53628 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Fri, 24 Feb 2023 19:07:53 -0500 Subject: [PATCH] Add `touch` applet --- Cargo.lock | 331 +++++++++++++++++++++++++++++++++- Cargo.toml | 6 + README.md | 1 + corebox/commands/mod.rs | 5 +- corebox/commands/touch/mod.rs | 126 +++++++++++++ 5 files changed, 460 insertions(+), 9 deletions(-) create mode 100644 corebox/commands/touch/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 4b57a8c..a794a18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 91b48c9..6b1e126 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 84f2add..5ef3e7a 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ code between applets, making for an overall smaller binary. - swaplabel - swapoff - sync +- touch - true - umount - unlink diff --git a/corebox/commands/mod.rs b/corebox/commands/mod.rs index d0a3b71..26eef7b 100644 --- a/corebox/commands/mod.rs +++ b/corebox/commands/mod.rs @@ -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> { "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> { } } -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", diff --git a/corebox/commands/touch/mod.rs b/corebox/commands/touch/mod.rs new file mode 100644 index 0000000..bbe83a5 --- /dev/null +++ b/corebox/commands/touch/mod.rs @@ -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> { + let ft = if let Some(time) = matches.get_one::("datetime") { + FileTime::from_unix_time(parse_datetime(time)?, 0) + } else if let Some(time) = matches.get_one::("time") { + FileTime::from_unix_time(parse_time(time)?, 0) + } else if let Some(ep) = matches.get_one::("epoch") { + FileTime::from_unix_time(*ep, 0) + } else if let Some(file_ref) = matches.get_one::("ref") { + let meta = File::open(file_ref)?.metadata()?; + FileTime::from_last_modification_time(&meta) + } else { + FileTime::now() + }; + + if let Some(files) = matches.get_many::("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 { + Some(shitbox::Path::UsrBin) + } +} + +fn parse_datetime(time: &str) -> Result> { + 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> { + 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()) +}