From 8e302f3f850e503e24054d0fdd4306af5eadf405 Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Mon, 17 Apr 2023 22:11:02 -0400 Subject: [PATCH] Add WIP truncate applet --- Cargo.lock | 113 ++++------------ corebox/commands/mod.rs | 5 +- corebox/commands/truncate/mod.rs | 213 +++++++++++++++++++++++++++++++ corebox/commands/who/mod.rs | 7 +- unistd/src/lib.rs | 18 +++ 5 files changed, 269 insertions(+), 87 deletions(-) create mode 100644 corebox/commands/truncate/mod.rs diff --git a/Cargo.lock b/Cargo.lock index c5a6b60..cc0f93c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -208,18 +208,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.1" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" +checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.2.1" +version = "4.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" dependencies = [ "bitflags", "clap_lex", @@ -231,7 +231,7 @@ version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01c22dcfb410883764b29953103d9ef7bb8fe21b3fa1158bc99986c2067294bd" dependencies = [ - "clap 4.2.1", + "clap 4.2.2", ] [[package]] @@ -240,7 +240,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7fa41f5e6aa83bd151b70fd0ceaee703d68cd669522795dc812df9edad1252c" dependencies = [ - "clap 4.2.1", + "clap 4.2.2", "clap_complete", ] @@ -256,7 +256,7 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4237e29de9c6949982ba87d51709204504fb8ed2fd38232fcb1e5bf7d4ba48c8" dependencies = [ - "clap 4.2.1", + "clap 4.2.2", "roff", ] @@ -325,7 +325,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] @@ -342,7 +342,7 @@ checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] @@ -380,9 +380,9 @@ version = "0.1.0" [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", @@ -672,9 +672,9 @@ checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" [[package]] name = "serde" -version = "1.0.159" +version = "1.0.160" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" [[package]] name = "sha1" @@ -706,7 +706,7 @@ dependencies = [ "bitflags-mini", "blake2b_simd", "chrono", - "clap 4.2.1", + "clap 4.2.2", "clap_complete", "clap_complete_nushell", "clap_mangen", @@ -788,9 +788,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.13" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", @@ -841,7 +841,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.13", + "syn 2.0.15", ] [[package]] @@ -1056,31 +1056,16 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] name = "windows-sys" -version = "0.45.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -1089,93 +1074,51 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.0" diff --git a/corebox/commands/mod.rs b/corebox/commands/mod.rs index 9248e0c..0f97d25 100644 --- a/corebox/commands/mod.rs +++ b/corebox/commands/mod.rs @@ -47,6 +47,7 @@ mod sleep; mod sync; mod touch; mod r#true; +mod truncate; mod unlink; mod wc; mod which; @@ -98,6 +99,7 @@ pub fn get(name: &str) -> Option> { "sync" => Some(Box::new(sync::Sync::default())), "touch" => Some(Box::new(touch::Touch::default())), "true" => Some(Box::new(r#true::True::default())), + "truncate" => Some(Box::new(truncate::Truncate)), "unlink" => Some(Box::new(unlink::Unlink::default())), "wc" => Some(Box::new(wc::Wc::default())), "which" => Some(Box::new(which::Which::default())), @@ -108,7 +110,7 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 45] = [ +pub static COMMANDS: [&str; 46] = [ "base32", "base64", "basename", @@ -148,6 +150,7 @@ pub static COMMANDS: [&str; 45] = [ "sync", "touch", "true", + "truncate", "unlink", "wc", "which", diff --git a/corebox/commands/truncate/mod.rs b/corebox/commands/truncate/mod.rs new file mode 100644 index 0000000..1020c22 --- /dev/null +++ b/corebox/commands/truncate/mod.rs @@ -0,0 +1,213 @@ +use { + super::Cmd, + clap::{Arg, ArgAction, ArgGroup, Command, ValueHint}, + std::{error::Error, fmt, fs, num::ParseIntError}, +}; + +#[derive(Debug, Default)] +pub struct Truncate; + +impl Cmd for Truncate { + fn cli(&self) -> clap::Command { + Command::new("truncate") + .about("truncate or extend the length of files") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + Arg::new("create") + .help("do not create files if they do not exist") + .long_help( + "Do not create files if they do not exist. The truncate \ + utility does not treat this as an error. No error messages are \ + displayed and the exit value is not affected.", + ) + .short('c') + .long("no-create") + .action(ArgAction::SetFalse), + Arg::new("reference") + .help("truncate or extend files to the length of RFILE") + .short('r') + .long("reference") + .value_name("RFILE") + .value_hint(ValueHint::FilePath) + .num_args(1), + Arg::new("size") + .help("set or adjust the file size by SIZE bytes") + .long_help( + "If the size argument is preceded by a plus sign (+), files will \ + be extended by this number of bytes. If the size argument is \ + preceded by a dash (-), file lengths will be reduced by no more \ + than this number of bytes, to a minimum length of zero bytes. If \ + the size argument is preceded by a percent sign (%), files will be \ + round up to a multiple of this number of bytes. If the size argument \ + is preceded by a slash sign (/), files will be round down to a \ + multiple of this number of bytes, to a minimum length of zero bytes. \ + Otherwise, the size argument specifies an absolute length to which all \ + files should be extended or reduced as appropriate.\n\nThe size argument \ + may be suffixed with one of K, M, G or T (either upper or lower case) to \ + indicate a multiple of Kilobytes, Megabytes, Gigabytes or Terabytes \ + respectively.", + ) + .short('s') + .long("size") + .allow_hyphen_values(true) + .value_name("SIZE") + .num_args(1), + Arg::new("file").value_name("FILE").num_args(1..), + ]) + .group( + ArgGroup::new("args") + .args(["reference", "size"]) + .required(true), + ) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { + let size = if let Some(file) = matches.get_one::("reference") { + let num: i64 = fs::metadata(file)?.len().try_into()?; + Size { operator: Operator::Equal, num } + } else if let Some(s) = matches.get_one::("size") { + parse_size(s)? + } else { + unreachable!(); + }; + Ok(()) + } + + fn path(&self) -> Option { + Some(shitbox::Path::UsrBin) + } +} + +enum Operator { + Equal, + Add, + Remove, + ModUp, + ModDown, +} + +struct Size { + operator: Operator, + num: i64, +} + +#[repr(i64)] +enum Multiplier { + Kilo = 1024, + Mega = 1024 * 1024, + Giga = 1024 * 1024 * 1024, + Tera = 1024 * 1024 * 1024 * 1024, +} + +#[derive(Debug)] +pub enum ParseSizeError { + EmptySize, + InvalidChar, + ParseIntError, +} + +impl fmt::Display for ParseSizeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{self:?}") + } +} + +impl From for ParseSizeError { + fn from(_value: ParseIntError) -> Self { + Self::ParseIntError + } +} + +impl Error for ParseSizeError {} + +fn parse_size(size: &str) -> Result { + if size.is_empty() { + return Err(ParseSizeError::EmptySize); + } + let mut operator: Option = None; + let mut num = vec![]; + let mut multiplier: Option = None; + size.chars().try_for_each(|c| { + match c { + '+' => { + if operator.is_some() || !num.is_empty() || multiplier.is_some() { + return Err(ParseSizeError::InvalidChar); + } else { + operator = Some(Operator::Add); + } + } + '-' => { + if operator.is_some() || !num.is_empty() || multiplier.is_some() { + return Err(ParseSizeError::InvalidChar); + } else { + operator = Some(Operator::Remove); + } + } + '%' => { + if operator.is_some() || !num.is_empty() || multiplier.is_some() { + return Err(ParseSizeError::InvalidChar); + } else { + operator = Some(Operator::ModUp); + } + } + '/' => { + if operator.is_some() || !num.is_empty() || multiplier.is_some() { + return Err(ParseSizeError::InvalidChar); + } else { + operator = Some(Operator::ModDown); + } + } + 'k' | 'K' => { + if multiplier.is_some() || num.is_empty() { + return Err(ParseSizeError::InvalidChar); + } else { + multiplier = Some(Multiplier::Kilo); + } + } + 'm' | 'M' => { + if multiplier.is_some() || num.is_empty() { + return Err(ParseSizeError::InvalidChar); + } else { + multiplier = Some(Multiplier::Mega); + } + } + 'g' | 'G' => { + if multiplier.is_some() || num.is_empty() { + return Err(ParseSizeError::InvalidChar); + } else { + multiplier = Some(Multiplier::Giga); + } + } + 't' | 'T' => { + if multiplier.is_some() || num.is_empty() { + return Err(ParseSizeError::InvalidChar); + } else { + multiplier = Some(Multiplier::Tera); + } + } + ch if ch.is_digit(10) => { + if multiplier.is_some() { + return Err(ParseSizeError::InvalidChar); + } else { + num.push(ch as u8); + } + } + _ => return Err(ParseSizeError::InvalidChar), + } + Ok(()) + })?; + let mut num: i64 = String::from_utf8(num).unwrap().parse()?; + if let Some(m) = multiplier { + match m { + Multiplier::Kilo => num *= 1024, + Multiplier::Mega => num *= (1024 * 1024), + Multiplier::Giga => num *= (1024 * 1024 * 1024), + Multiplier::Tera => num *= (1024 * 1024 * 1024 * 1024), + } + } + match operator { + Some(operator) => Ok(Size { operator, num }), + None => Ok(Size { operator: Operator::Equal, num }), + } +} diff --git a/corebox/commands/who/mod.rs b/corebox/commands/who/mod.rs index 1c3781d..8d01dd1 100644 --- a/corebox/commands/who/mod.rs +++ b/corebox/commands/who/mod.rs @@ -35,7 +35,12 @@ impl Cmd for Who { session: _, time, } => { - println!("{user:9}{line:13}{} {}:{}", time.date(), time.hour(), time.minute()); + println!( + "{user:9}{line:13}{} {}:{}", + time.date(), + time.hour(), + time.minute() + ); } _ => {} } diff --git a/unistd/src/lib.rs b/unistd/src/lib.rs index 75da1de..179e64e 100644 --- a/unistd/src/lib.rs +++ b/unistd/src/lib.rs @@ -16,6 +16,24 @@ fn new_utsname() -> utsname { } } +pub fn ftruncate(fd: &File, len: i64) -> Result<(), Box> { + let ret = unsafe { syscall!(FTRUNCATE, fd.as_raw_fd(), len) }; + if ret == 0 { + Ok(()) + } else { + Err(Errno::from(ret).into()) + } +} + +pub fn truncate(path: &str, len: i64) -> Result<(), Box> { + let ret = unsafe { syscall!(TRUNCATE, CString::new(path)?.as_ptr(), len) }; + if ret == 0 { + Ok(()) + } else { + Err(Errno::from(ret).into()) + } +} + #[allow(clippy::cast_sign_loss)] pub fn gethostname() -> Result> { let mut uts = new_utsname();