mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
implement locate and updatedb (#536)
--------- Co-authored-by: Sylvestre Ledru <sylvestre@debian.org>
This commit is contained in:
Generated
+229
@@ -87,6 +87,21 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aa3a22042e45de04255c7bf3626e239f450200fd0493c1e382263544b20aea6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"libc",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -242,6 +257,12 @@ version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "displaydoc"
|
||||
version = "0.2.5"
|
||||
@@ -271,6 +292,12 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -325,15 +352,19 @@ name = "findutils"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"argmax",
|
||||
"assert_cmd",
|
||||
"chrono",
|
||||
"clap",
|
||||
"ctor",
|
||||
"faccess",
|
||||
"filetime",
|
||||
"itertools",
|
||||
"nix 0.31.3",
|
||||
"onig",
|
||||
"regex",
|
||||
"rstest",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"uucore",
|
||||
"uutests",
|
||||
"walkdir",
|
||||
@@ -390,6 +421,48 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af43fadb8a98512d547e37b4e92e0ced13e205c061b87b4623eff01d918d6968"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.32"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-macro",
|
||||
"futures-task",
|
||||
"pin-project-lite",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.4.2"
|
||||
@@ -498,6 +571,15 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.18"
|
||||
@@ -688,6 +770,12 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
@@ -715,6 +803,33 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
"predicates-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0de1b847b39c8131db0467e9df1ff60e6d0562ab8e9a16e568ad0fdb372e2f2"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.1"
|
||||
@@ -735,6 +850,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
||||
dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.106"
|
||||
@@ -805,6 +929,12 @@ version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
|
||||
|
||||
[[package]]
|
||||
name = "relative-path"
|
||||
version = "1.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||
|
||||
[[package]]
|
||||
name = "rlimit"
|
||||
version = "0.11.0"
|
||||
@@ -814,12 +944,51 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d"
|
||||
dependencies = [
|
||||
"futures-timer",
|
||||
"futures-util",
|
||||
"rstest_macros",
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rstest_macros"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"glob",
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"relative-path",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.4"
|
||||
@@ -909,6 +1078,12 @@ version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.1"
|
||||
@@ -965,6 +1140,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.18"
|
||||
@@ -1029,6 +1210,36 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "1.1.1+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.25.12+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"toml_datetime",
|
||||
"toml_parser",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_parser"
|
||||
version = "1.1.2+spec-1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
|
||||
dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.5.1"
|
||||
@@ -1135,6 +1346,15 @@ dependencies = [
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
@@ -1424,6 +1644,15 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.51.0"
|
||||
|
||||
+13
-1
@@ -12,18 +12,22 @@ authors = ["uutils developers"]
|
||||
[dependencies]
|
||||
argmax = "0.4.0"
|
||||
chrono = "0.4.45"
|
||||
clap = "4.6"
|
||||
clap = { version = "4.6", features = ["env"] }
|
||||
faccess = "0.2.4"
|
||||
nix = { version = "0.31", features = ["fs", "user"] }
|
||||
onig = { version = "6.5", default-features = false }
|
||||
regex = "1.12"
|
||||
uucore = { version = "0.9.0", features = ["entries", "fs", "fsext", "mode"] }
|
||||
walkdir = "2.5"
|
||||
itertools = "0.14.0"
|
||||
thiserror = "2.0.12"
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2"
|
||||
ctor = "1.0"
|
||||
filetime = "0.2"
|
||||
nix = { version = "0.31", features = ["fs"] }
|
||||
rstest = "0.25.0"
|
||||
tempfile = "3"
|
||||
uutests = "0.9.0"
|
||||
|
||||
@@ -31,6 +35,14 @@ uutests = "0.9.0"
|
||||
name = "find"
|
||||
path = "src/find/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "locate"
|
||||
path = "src/locate/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "updatedb"
|
||||
path = "src/updatedb/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "xargs"
|
||||
path = "src/xargs/main.rs"
|
||||
|
||||
@@ -74,6 +74,7 @@ use std::{
|
||||
use super::{Config, Dependencies};
|
||||
|
||||
pub use entry::{FileType, WalkEntry, WalkError};
|
||||
pub use regex::RegexType;
|
||||
|
||||
/// Symlink following mode.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
|
||||
@@ -5,4 +5,7 @@
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
pub mod find;
|
||||
#[cfg(unix)]
|
||||
pub mod locate;
|
||||
pub mod updatedb;
|
||||
pub mod xargs;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect();
|
||||
std::process::exit(findutils::locate::locate_main(strs.as_slice()));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn main() {
|
||||
// TODO: locate currently uses UNIX-specific OsString APIs. If those can be worked around, locate
|
||||
// should function normally on Windows. If and when that happens, make sure to make a separate
|
||||
// windows test database with \ instead of /.
|
||||
println!("locate is unsupported on Windows");
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
#[cfg(not(windows))]
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<String>>();
|
||||
let strs: Vec<&str> = args.iter().map(std::convert::AsRef::as_ref).collect();
|
||||
std::process::exit(findutils::updatedb::updatedb_main(strs.as_slice()));
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn main() {
|
||||
println!("updatedb is unsupported on Windows");
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt::Display,
|
||||
fs::OpenOptions,
|
||||
io::{stderr, BufRead, BufReader, BufWriter, Write},
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
str::FromStr,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use uucore::error::UResult;
|
||||
|
||||
use crate::find::{find_main, Dependencies};
|
||||
|
||||
// TODO: the `localuser` and `netuser` arguments are accepted but not yet honored; handling them
|
||||
// will likely involve splitting the find logic into two calls.
|
||||
pub struct Config {
|
||||
find_options: String,
|
||||
local_paths: Vec<PathBuf>,
|
||||
net_paths: Vec<String>,
|
||||
prune_paths: Vec<PathBuf>,
|
||||
prune_fs: Vec<String>,
|
||||
output: PathBuf,
|
||||
db_format: DbFormat,
|
||||
}
|
||||
|
||||
impl From<ArgMatches> for Config {
|
||||
fn from(value: ArgMatches) -> Self {
|
||||
Self {
|
||||
find_options: value
|
||||
.get_one::<String>("findoptions")
|
||||
.cloned()
|
||||
.unwrap_or_else(String::new),
|
||||
local_paths: value
|
||||
.get_one::<String>("localpaths")
|
||||
.map(|s| {
|
||||
s.split_whitespace()
|
||||
.filter_map(|s| PathBuf::from_str(s).ok())
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(|| vec![PathBuf::from("/")]),
|
||||
net_paths: value
|
||||
.get_one::<String>("netpaths")
|
||||
.map(|s| s.split_whitespace().map(|s| s.to_owned()).collect())
|
||||
.unwrap_or_default(),
|
||||
prune_paths: value
|
||||
.get_one::<String>("prunepaths")
|
||||
.map(|s| s.split_whitespace().map(PathBuf::from).collect())
|
||||
.unwrap_or_else(|| {
|
||||
["/tmp", "/usr/tmp", "/var/tmp", "/afs"]
|
||||
.into_iter()
|
||||
.map(PathBuf::from)
|
||||
.collect()
|
||||
}),
|
||||
prune_fs: value
|
||||
.get_one::<String>("prunefs")
|
||||
.map(|s| s.split_whitespace().map(|s| s.to_owned()).collect())
|
||||
.unwrap_or_else(|| {
|
||||
["nfs", "NFS", "proc"]
|
||||
.into_iter()
|
||||
.map(str::to_string)
|
||||
.collect()
|
||||
}),
|
||||
db_format: value
|
||||
.get_one::<DbFormat>("dbformat")
|
||||
.copied()
|
||||
.unwrap_or_default(),
|
||||
output: value
|
||||
.get_one::<PathBuf>("output")
|
||||
.cloned()
|
||||
// FIXME: the default should be platform-dependent
|
||||
.unwrap_or_else(|| PathBuf::from("/usr/local/var/locatedb")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum DbFormat {
|
||||
#[default]
|
||||
Locate02,
|
||||
}
|
||||
|
||||
// used for locate's --statistics
|
||||
impl Display for DbFormat {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Locate02 => f.write_str("GNU LOCATE02"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn uu_app() -> Command {
|
||||
Command::new("updatedb")
|
||||
.version(crate_version!())
|
||||
.arg(
|
||||
Arg::new("findoptions")
|
||||
.long("findoptions")
|
||||
.require_equals(true)
|
||||
.env("FINDOPTIONS")
|
||||
.action(ArgAction::Append),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("localpaths")
|
||||
.long("localpaths")
|
||||
.require_equals(true)
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("netpaths")
|
||||
.long("netpaths")
|
||||
.require_equals(true)
|
||||
.env("NETPATHS")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("prunepaths")
|
||||
.long("prunepaths")
|
||||
.require_equals(true)
|
||||
.env("PRUNEPATHS")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("prunefs")
|
||||
.long("prunefs")
|
||||
.require_equals(true)
|
||||
.env("PRUNEFS")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("output")
|
||||
.long("output")
|
||||
.require_equals(true)
|
||||
.value_parser(value_parser!(PathBuf))
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("localuser")
|
||||
.long("localuser")
|
||||
.require_equals(true)
|
||||
.env("LOCALUSER")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("netuser")
|
||||
.long("netuser")
|
||||
.require_equals(true)
|
||||
.env("NETUSER")
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("dbformat")
|
||||
.long("dbformat")
|
||||
.require_equals(true)
|
||||
.value_parser(["LOCATE02"])
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
}
|
||||
|
||||
// The LOCATE02 format elides bytes from the path until the first byte that differs from the
|
||||
// previous entry. It keeps a running total of the prefix length, and uses 1 or 3 bytes to write
|
||||
// the difference from the previous prefix length. Paths are provided in sorted order by find.
|
||||
struct Frcoder<'a> {
|
||||
reader: BufReader<&'a [u8]>,
|
||||
prev: Option<Vec<u8>>,
|
||||
prefix: usize,
|
||||
ty: DbFormat,
|
||||
}
|
||||
|
||||
impl<'a> Frcoder<'a> {
|
||||
fn new(v: &'a [u8], ty: DbFormat) -> Self {
|
||||
Self {
|
||||
reader: BufReader::new(v),
|
||||
prev: None,
|
||||
prefix: 0,
|
||||
ty,
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_header(&self) -> Vec<u8> {
|
||||
match self.ty {
|
||||
DbFormat::Locate02 => "\0LOCATE02\0".as_bytes().to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Frcoder<'_> {
|
||||
type Item = Vec<u8>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut path = Vec::new();
|
||||
// find prints nul bytes after each path
|
||||
if self.reader.read_until(b'\0', &mut path).ok()? == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let prefix = path
|
||||
.iter()
|
||||
.zip(self.prev.as_deref().unwrap_or_default())
|
||||
.take_while(|(a, b)| a == b)
|
||||
.count();
|
||||
|
||||
let diff = prefix as i32 - self.prefix as i32;
|
||||
|
||||
// if the prefix delta exceeds 0x7f, we use 0x80 to signal that the next two bytes comprise
|
||||
// the delta
|
||||
let mut out = Vec::new();
|
||||
if diff.abs() > 0x7f {
|
||||
out.push(0x80);
|
||||
out.extend((diff as i16).to_be_bytes());
|
||||
} else {
|
||||
out.push(diff as u8);
|
||||
}
|
||||
|
||||
out.extend(path.iter().skip(prefix));
|
||||
|
||||
self.prefix = prefix;
|
||||
self.prev = Some(path);
|
||||
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
|
||||
// capture find's stdout
|
||||
struct CapturedDependencies {
|
||||
output: Rc<RefCell<dyn Write>>,
|
||||
now: SystemTime,
|
||||
}
|
||||
|
||||
impl CapturedDependencies {
|
||||
fn new(output: Rc<RefCell<dyn Write>>) -> Self {
|
||||
Self {
|
||||
output,
|
||||
now: SystemTime::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Dependencies for CapturedDependencies {
|
||||
fn get_output(&self) -> &RefCell<dyn Write> {
|
||||
self.output.as_ref()
|
||||
}
|
||||
|
||||
fn now(&self) -> SystemTime {
|
||||
self.now
|
||||
}
|
||||
|
||||
fn confirm(&self, _prompt: &str) -> bool {
|
||||
// updatedb runs non-interactively; there is nothing to confirm.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn do_updatedb(args: &[&str]) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let config = Config::from(matches);
|
||||
|
||||
// offload most of the logic to find. Build the argument list as discrete tokens rather than
|
||||
// a single string so that paths and options aren't re-split on whitespace and so that an empty
|
||||
// `prunefs`/`prunepaths` can't produce an invalid expression (e.g. an empty `( )` group).
|
||||
let mut tokens: Vec<String> = Vec::new();
|
||||
tokens.extend(
|
||||
config
|
||||
.local_paths
|
||||
.iter()
|
||||
.filter_map(|p| p.to_str())
|
||||
.map(String::from),
|
||||
);
|
||||
tokens.extend(config.net_paths.iter().cloned());
|
||||
// FINDOPTIONS is a single string of options, split on whitespace as GNU updatedb does
|
||||
tokens.extend(config.find_options.split_whitespace().map(String::from));
|
||||
|
||||
// Each clause prunes a subtree (by filesystem type or by path); the final clause prints the
|
||||
// entry. Clauses are OR-ed together so a path that matches no prune clause gets printed.
|
||||
let mut clauses: Vec<Vec<String>> = Vec::new();
|
||||
if !config.prune_fs.is_empty() {
|
||||
let mut clause = vec![String::from("(")];
|
||||
for (i, fs) in config.prune_fs.iter().enumerate() {
|
||||
if i > 0 {
|
||||
clause.push(String::from("-or"));
|
||||
}
|
||||
clause.push(String::from("-fstype"));
|
||||
clause.push(fs.clone());
|
||||
}
|
||||
clause.push(String::from(")"));
|
||||
clause.push(String::from("-prune"));
|
||||
clauses.push(clause);
|
||||
}
|
||||
for path in config.prune_paths.iter().filter_map(|p| p.to_str()) {
|
||||
clauses.push(vec![
|
||||
String::from("-regex"),
|
||||
path.to_string(),
|
||||
String::from("-prune"),
|
||||
]);
|
||||
}
|
||||
clauses.push(vec![String::from("-print0")]);
|
||||
|
||||
for (i, clause) in clauses.into_iter().enumerate() {
|
||||
if i > 0 {
|
||||
tokens.push(String::from("-or"));
|
||||
}
|
||||
tokens.extend(clause);
|
||||
}
|
||||
tokens.push(String::from("-sorted"));
|
||||
|
||||
let mut find_args = vec!["find"];
|
||||
find_args.extend(tokens.iter().map(String::as_str));
|
||||
|
||||
let output = Rc::new(RefCell::new(Vec::new()));
|
||||
let deps = CapturedDependencies::new(output.clone());
|
||||
find_main(find_args.as_slice(), &deps);
|
||||
|
||||
let mut writer = BufWriter::new(
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(config.output)?,
|
||||
);
|
||||
|
||||
let output = output.borrow();
|
||||
let frcoder = Frcoder::new(output.as_slice(), config.db_format);
|
||||
writer.write_all(&frcoder.generate_header())?;
|
||||
for v in frcoder {
|
||||
writer.write_all(v.as_slice())?;
|
||||
}
|
||||
|
||||
writer.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn updatedb_main(args: &[&str]) -> i32 {
|
||||
match do_updatedb(args) {
|
||||
Ok(()) => 0,
|
||||
Err(e) => {
|
||||
let _ = writeln!(&mut stderr(), "Error: {e}");
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,213 @@
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
mod common;
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
use assert_cmd::{assert::OutputAssertExt, cargo::CommandCargoExt};
|
||||
use rstest::rstest;
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const DB_FLAG: &str = "--database=test_data/db/test_data_db";
|
||||
#[cfg(not(windows))]
|
||||
const INVALID_DB_FLAG: &str = "--database=test_data/db/invalid_db";
|
||||
#[cfg(not(windows))]
|
||||
const OLD_DB_FLAG: &str = "--database=test_data/db/old_db";
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_no_matches() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["usr", DB_FLAG])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_match() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["test_data", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_no_matches_basename() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["test_data1234567890", "--basename", DB_FLAG])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_match_basename() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["abbbc", "--basename", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_existing() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["abbbc", "--existing", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_non_existing() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["abbbc", "--non-existing", DB_FLAG])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_statistics() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["", "--statistics", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("emacs")]
|
||||
#[case("grep")]
|
||||
#[case("posix-basic")]
|
||||
#[case("posix-extended")]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_regex(#[case] input: &str) {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args([
|
||||
"abbbc",
|
||||
"--regex",
|
||||
format!("--regextype={input}").as_str(),
|
||||
DB_FLAG,
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_all() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["abb", "bbc", "--all", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_all_regex() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["abb", "b*c", "--all", "--regex", DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_invalid_db() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["test_data", INVALID_DB_FLAG])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_outdated_db() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["test_data", OLD_DB_FLAG])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_print_help() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.arg("--help")
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_invalid_flag() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.arg("--unknown")
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
// an un-compilable regex should be reported as an error rather than silently matching nothing
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_locate_invalid_regex() {
|
||||
Command::cargo_bin("locate")
|
||||
.expect("couldn't find locate binary")
|
||||
.args(["[", "--regex", DB_FLAG])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_updatedb() {
|
||||
Command::cargo_bin("updatedb")
|
||||
.expect("couldn't find updatedb binary")
|
||||
.args(["--localpaths=./test_data", "--output=/dev/null"])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_updatedb_invalid_flag() {
|
||||
Command::cargo_bin("updatedb")
|
||||
.expect("couldn't find updatedb binary")
|
||||
.args(["--unknown"])
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
|
||||
// empty prunefs/prunepaths must not produce an invalid find expression (e.g. an empty `( )` group)
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn test_updatedb_empty_prune() {
|
||||
Command::cargo_bin("updatedb")
|
||||
.expect("couldn't find updatedb binary")
|
||||
.args([
|
||||
"--localpaths=./test_data",
|
||||
"--output=/dev/null",
|
||||
"--prunefs=",
|
||||
"--prunepaths=",
|
||||
])
|
||||
.assert()
|
||||
.success();
|
||||
}
|
||||
Reference in New Issue
Block a user