summaryrefslogtreecommitdiff
path: root/support/pkgcheck/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/pkgcheck/src/main.rs')
-rw-r--r--support/pkgcheck/src/main.rs338
1 files changed, 238 insertions, 100 deletions
diff --git a/support/pkgcheck/src/main.rs b/support/pkgcheck/src/main.rs
index 9ff8390f9c..6ad786dcd9 100644
--- a/support/pkgcheck/src/main.rs
+++ b/support/pkgcheck/src/main.rs
@@ -26,10 +26,10 @@ use std::fmt::Display;
use std::str;
use utils::*;
+use scoped_threadpool::Pool;
use serde::{Deserialize, Serialize};
-use std::os::unix::fs::MetadataExt;
use std::borrow::Cow;
-use scoped_threadpool::Pool;
+use std::os::unix::fs::MetadataExt;
use tempfile::Builder;
@@ -54,16 +54,92 @@ use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering};
use rustc_hash::{FxHashMap, FxHashSet};
+use std::time::SystemTime;
use std::fmt::Arguments;
use std::sync::mpsc::{channel, Sender};
-use clap::{Command, CommandFactory, Parser, ValueHint};
-use clap_complete::{generate, Generator, Shell};
+use clap::builder::PossibleValue;
+use clap::{Command, CommandFactory, Parser, ValueEnum, ValueHint};
+//use clap_complete::{generate, Generator, Shell};
+use clap_complete::{shells, Generator};
+use clap_complete_nushell::Nushell;
#[cfg(unix)]
use walkdir::{DirEntry, WalkDir};
+/// Shell with auto-generated completion script available.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum Shell {
+ /// Bourne Again SHell (bash)
+ Bash,
+ /// Elvish shell
+ Elvish,
+ /// Friendly Interactive SHell (fish)
+ Fish,
+ /// PowerShell
+ PowerShell,
+ /// Z SHell (zsh)
+ Zsh,
+ /// Nu shell (nu)
+ Nu,
+}
+
+impl Generator for Shell {
+ fn file_name(&self, name: &str) -> String {
+ match self {
+ Shell::Bash => shells::Bash.file_name(name),
+ Shell::Elvish => shells::Elvish.file_name(name),
+ Shell::Fish => shells::Fish.file_name(name),
+ Shell::PowerShell => shells::PowerShell.file_name(name),
+ Shell::Zsh => shells::Zsh.file_name(name),
+ Shell::Nu => Nushell.file_name(name),
+ }
+ }
+
+ fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
+ match self {
+ Shell::Bash => shells::Bash.generate(cmd, buf),
+ Shell::Elvish => shells::Elvish.generate(cmd, buf),
+ Shell::Fish => shells::Fish.generate(cmd, buf),
+ Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
+ Shell::Zsh => shells::Zsh.generate(cmd, buf),
+ Shell::Nu => Nushell.generate(cmd, buf),
+ }
+ }
+}
+
+// Hand-rolled so it can work even when `derive` feature is disabled
+impl ValueEnum for Shell {
+ fn value_variants<'a>() -> &'a [Self] {
+ &[
+ Shell::Bash,
+ Shell::Elvish,
+ Shell::Fish,
+ Shell::PowerShell,
+ Shell::Zsh,
+ Shell::Nu,
+ ]
+ }
+
+ fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
+ Some(match self {
+ Shell::Bash => PossibleValue::new("bash"),
+ Shell::Elvish => PossibleValue::new("elvish"),
+ Shell::Fish => PossibleValue::new("fish"),
+ Shell::PowerShell => PossibleValue::new("powershell"),
+ Shell::Zsh => PossibleValue::new("zsh"),
+ Shell::Nu => PossibleValue::new("nu"),
+ })
+ }
+}
+
+fn is_future_mtime(now: SystemTime, mtime: SystemTime) -> bool {
+ mtime > now
+ //mtime > now + Duration::new(1800, 0)
+}
+
fn format_message(message: &Arguments, no_color: bool) -> Cow<'static, str> {
let msg_str = format!("{}", message);
if msg_str.starts_with(' ') {
@@ -99,7 +175,7 @@ pub struct PathExceptions {
fn get_config_file_name() -> Option<String> {
if let Some(config_file) = &ARGS.config_file {
if Path::new(&config_file).exists() {
- return Some(config_file.to_string())
+ return Some(config_file.to_string());
} else {
f0008!(config_file);
std::process::exit(1);
@@ -124,6 +200,8 @@ fn read_yaml_config() -> FxHashMap<String, String> {
for (p, q) in [
("armtex", "armenian"),
("babel-base", "babel"),
+ ("l3backend-dev", "latex-dev/l3backend"),
+ ("l3kernel-dev", "latex-dev/l3kernel"),
("latex-amsmath", "latex"),
("latex-amsmath-dev", "latex-dev"),
("latex-base", "latex"),
@@ -148,7 +226,10 @@ fn read_yaml_config() -> FxHashMap<String, String> {
let data = match fs::read_to_string(&config_filename) {
Ok(str) => str,
- Err(e) => { f0009!(&config_filename, e); std::process::exit(1); }
+ Err(e) => {
+ f0009!(&config_filename, e);
+ std::process::exit(1);
+ }
};
let path_exceptions = serde_yaml::from_str::<PathExceptions>(&data);
@@ -171,7 +252,10 @@ fn read_yaml_config() -> FxHashMap<String, String> {
}
pb
}
- Err(e) => { f0010!(e); std::process::exit(1);},
+ Err(e) => {
+ f0010!(e);
+ std::process::exit(1);
+ }
};
pkg_replacements
}
@@ -277,7 +361,11 @@ fn check_readme(dir_entry: &str, is_readme: &ReadmeKind, ft: &filemagic::Mimetyp
return false;
}
filemagic::Mimetype::Bom(b) => {
- e0029!(msg_name, b.as_ref());
+ // wegmit e0029!(msg_name, b.as_ref());
+ e0029!(
+ msg_name,
+ <unicode_bom::Bom as std::convert::AsRef<str>>::as_ref(b)
+ );
return false;
}
filemagic::Mimetype::Text(_le) => match File::open(dir_entry) {
@@ -329,55 +417,58 @@ fn _get_devno(entry: &DirEntry) -> u64 {
#[derive(Parser, Debug, PartialEq)]
#[clap(author, version, about, long_about = None)]
+#[command(arg_required_else_help(true))]
struct Args {
- #[clap(short = 'I', long = "ignore-dupes", help = "Ignore dupes")]
+ #[arg(short = 'I', long = "ignore-dupes", help = "Ignore dupes")]
ignore_dupes: bool,
- #[clap(long = "ignore-same-named", help = "Ignore same-named files")]
+ #[arg(long = "ignore-same-named", help = "Ignore same-named files")]
ignore_same_named: bool,
- #[clap(short = 'v', long = "verbose", help = "Verbose operation?")]
+ #[arg(short = 'v', long = "verbose", help = "Verbose operation?")]
verbose: bool,
- #[clap(short = 'L', long = "correct-le", help = "Correct line endings")]
+ #[arg(short = 'L', long = "correct-le", help = "Correct line endings")]
correct_le: bool,
- #[clap(short = 'C', long = "correct-perms", help = "Correct permissions")]
+ #[arg(short = 'C', long = "correct-perms", help = "Correct permissions")]
correct_perms: bool,
- #[clap(long = "no-colors", help = "Don't display messages in color")]
+ #[arg(long = "no-colors", help = "Don't display messages in color")]
no_colors: bool,
- #[clap(long = "urlcheck", help = "Check URLs found in README files")]
+ #[arg(long = "urlcheck", help = "Check URLs found in README files")]
urlcheck: bool,
- #[clap(short = 'T', long = "tds-zip", help = "tds zip archive", group = "tds", value_hint = ValueHint::FilePath)]
+ #[arg(short = 'T', long = "tds-zip", help = "tds zip archive", group = "tds", value_hint = ValueHint::FilePath)]
tds_zip: Option<String>,
- #[clap(
+ #[arg(
short = 'e',
long = "explain",
help = "Explain error or warning message",
group = "only_one"
)]
explain: Option<String>,
- #[clap(
+ #[arg(
long = "explain-all",
help = "Explains all error or warning messages",
group = "only_one"
)]
explain_all: bool,
- #[clap(long = "generate-completion", group = "only_one", value_enum)]
+ #[arg(long = "generate-completion", group = "only_one", value_enum)]
generator: Option<Shell>,
- #[clap(
+ #[arg(
long = "show-temp-endings",
help = "Show file endings for temporary files",
group = "only_one"
)]
show_tmp_endings: bool,
- #[clap(short = 'd', long = "package-dir", help = "Package directory", value_hint = ValueHint::DirPath)]
+ #[arg(short = 'd', long = "package-dir", help = "Package directory", value_hint = ValueHint::DirPath)]
pkg_dir: Option<String>,
- #[clap(long = "config-file", help = "Specify config file to use", value_hint = ValueHint::FilePath)]
+ #[arg(long = "config-file", help = "Specify config file to use", value_hint = ValueHint::FilePath)]
config_file: Option<String>,
}
// In the pas we took care to avoid visiting a single inode twice, which takes care of (false positive) hardlinks.
// Now we want to know if there is a hardlink in the package directory
#[cfg(unix)]
-fn check_inode(set: &mut FxHashMap<(u64, u64), Vec<String>>, filename: &str, meta: &Metadata) {
- set.entry((get_devno(meta), meta.ino())).or_insert_with(Vec::new).push(filename.to_string());
+fn check_inode(set: &mut FxHashMap<(u64, u64), Vec<String>>, filename: &str, meta: &Metadata) {
+ set.entry((get_devno(meta), meta.ino()))
+ .or_default()
+ .push(filename.to_string());
}
#[cfg(not(unix))]
@@ -387,6 +478,8 @@ fn check_inode(_: &mut FxHashSet<u64>, _: &Metadata) -> bool {
static ARGS: Lazy<Args> = Lazy::new(Args::parse);
static ERROR_OCCURRED: AtomicBool = AtomicBool::new(false);
+//Get the current time
+static NOW: Lazy<SystemTime> = Lazy::new(SystemTime::now);
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DPath {
@@ -434,14 +527,15 @@ impl DupPath {
type DupHashes = FxHashMap<(u64, Vec<u8>), DupPath>;
fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
- generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+ //generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
+ clap_complete::generate(gen, cmd, "pkgcheck", &mut std::io::stdout());
}
fn main() {
let _ = setup_logger(ARGS.no_colors);
// read yaml config file if one is given explicitly or implicitly
- let pkg_replace : FxHashMap<String, String> = read_yaml_config();
+ let pkg_replace: FxHashMap<String, String> = read_yaml_config();
match &ARGS.explain {
None => (),
@@ -716,9 +810,16 @@ fn check_tds_archive_name(tds_zip: &Option<String>) -> Option<String> {
// }
// }
-fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_replace: &FxHashMap<String, String>) {
+fn check_tds_archive(
+ pkg_name: &str,
+ tds_zip: &str,
+ hashes: &DupHashes,
+ pkg_replace: &FxHashMap<String, String>,
+) {
i0003!(tds_zip);
+ let mut lcnames: FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>> = FxHashMap::default();
+
let dir_entry = Path::new(tds_zip);
let p = get_perms(dir_entry);
if !owner_has(p, 4) || !others_have(p, 4) || x_bit_set(p) {
@@ -731,12 +832,6 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let ut = Utils::new(utils::CheckType::Tds);
- let real_pkg_name = if let Some(real_name) = pkg_replace.get(pkg_name) {
- real_name
- } else {
- pkg_name
- };
-
let tmp_dir = match Builder::new().prefix("pkgcheck").tempdir() {
Ok(tdir) => tdir,
Err(e) => {
@@ -777,7 +872,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let path = dir_entry.path().to_path_buf();
let sizeref = &mut sizes;
- sizeref.entry(fsize).or_insert_with(Vec::new).push(path);
+ sizeref.entry(fsize).or_default().push(path);
};
let mut map_files_found = false;
@@ -846,6 +941,15 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
}
};
+ // let mtime = meta.modified().unwrap();
+ // if is_future_mtime(*NOW, mtime) {
+ // let diff = mtime.duration_since(*NOW).unwrap();
+ // println!(
+ // "{} has an mtime in the future by {} seconds",
+ // &file_name,
+ // diff.as_secs()
+ // );
+ // }
let ft = get_filetype(&dir_entry);
if let FType::Error(e) = ft {
@@ -853,12 +957,27 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
continue;
}
+ // this is the path name without the temporary part
+ // from unpacking the TDS zip archive
let dir_entry_display = if dir_entry.depth() == 0 {
&dir_entry_str[tmp_dir_offset - 1..]
} else {
&dir_entry_str[tmp_dir_offset..]
};
+ let filetype = match ft {
+ FType::Directory => FileKind::Directory,
+ FType::Regular => FileKind::File,
+ FType::Symlink => {
+ e0043!(dir_entry_display);
+ continue;
+ }
+ _ => panic!(
+ "Unexpected file type for {} in zip archive",
+ dir_entry_display
+ ),
+ };
+ register_duplicate_filename(&mut lcnames, dir_entry_display, filetype);
ut.check_for_temporary_file(dir_entry_display);
// In the top level directory of a TDS zip archive
@@ -912,11 +1031,14 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
}
// if the path doesn't contain a man page...
- if !dir_entry_str.contains("/man/") {
- let pkg_name_s = format!("/{}/", real_pkg_name);
- // ...then we want to have the package name in the path
- if !dir_entry_str.contains(&pkg_name_s) {
- e0028!(real_pkg_name, dir_entry_display);
+ if !dir_entry_str.contains("/man/") && !dir_entry_str.contains(pkg_name) {
+ if let Some(real_name) = pkg_replace.get(pkg_name) {
+ let pkg_name_s = format!("/{}/", real_name);
+ if !dir_entry_str.contains(&pkg_name_s) {
+ e0028!(pkg_name_s, dir_entry_display);
+ }
+ } else {
+ e0028!(pkg_name, dir_entry_display);
}
}
@@ -952,10 +1074,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
let hashref = &mut tds_hashes;
scope.execute(move || {
for (size, path, hash) in rx.iter() {
- hashref
- .entry((size, hash))
- .or_insert_with(Vec::new)
- .push(path);
+ hashref.entry((size, hash)).or_default().push(path);
}
});
@@ -974,6 +1093,7 @@ fn check_tds_archive(pkg_name: &str, tds_zip: &str, hashes: &DupHashes, pkg_repl
e0026!(p);
}
}
+ print_casefolding_tds(&lcnames);
}
fn get_extension_from_filename(filename: &str) -> Option<&str> {
@@ -1073,6 +1193,21 @@ enum ReadmeKind {
Symlink(String),
}
+fn register_duplicate_filename(
+ lcnames: &mut FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>,
+ dir_entry: &str,
+ fk: FileKind,
+) {
+ let lc_dir_entry_str = dir_entry.to_lowercase();
+ if let Some(_dir_name) = filename(dir_entry) {
+ // let lcnref = &mut lcnames;
+ lcnames
+ .entry(PathBuf::from(lc_dir_entry_str))
+ .or_default()
+ .push((PathBuf::from(&dir_entry), fk));
+ }
+}
+
fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let mut lcnames: FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>> = FxHashMap::default();
@@ -1125,6 +1260,12 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// above in the definition of dir_entry_str
let file_name = dir_entry.file_name().to_str().unwrap().to_string();
+ let mtime = meta.modified().unwrap();
+ if is_future_mtime(*NOW, mtime) {
+ let diff = mtime.duration_since(*NOW).unwrap();
+ w0011!(&file_name, diff.as_secs(), &utils::format_duration(&diff));
+ }
+
// we check for weird stuff like socket files aso.
let ft = get_filetype(&dir_entry);
if found_unwanted_filetype(dir_entry_str, &ft) {
@@ -1136,6 +1277,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// 1. dealing with symlinks
if ft == FType::Symlink {
match get_symlink(&dir_entry) {
+ // broken symlink
Ok(None) => {
e0010!(&dir_entry_str);
continue;
@@ -1147,22 +1289,18 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
Ok(Some(p)) => {
let pd: String =
p.canonicalize().unwrap().to_string_lossy().to_string();
+ // symlink pointing to outside of the package directory tree
if !pd.starts_with(&root_absolute) {
e0030!(&dir_entry_str, p.display());
continue;
}
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
if let Some(_dir_name) = filename(dir_entry_str) {
- let lcnref = &mut lcnames;
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((
- PathBuf::from(&dir_entry_str),
- //FileKind::Symlink(&dir_entry_str.into()),
- FileKind::Symlink(pd.clone()),
- ));
+ register_duplicate_filename(
+ &mut lcnames,
+ dir_entry_str,
+ FileKind::Symlink(pd.clone()),
+ );
}
if is_readme(&file_name) {
readme_found = true;
@@ -1184,14 +1322,13 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
// 2. dealing with directories
if ft == FType::Directory {
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
+ //let lc_dir_entry_str = dir_entry_str.to_lowercase();
if let Some(_dir_name) = filename(dir_entry_str) {
- let lcnref = &mut lcnames;
-
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((PathBuf::from(dir_entry_str), FileKind::Directory));
+ register_duplicate_filename(
+ &mut lcnames,
+ dir_entry_str,
+ FileKind::Directory,
+ );
}
if !owner_has(p, 5) || !others_have(p, 5) {
@@ -1222,7 +1359,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
doubleref
.entry(PathBuf::from(file_name))
- .or_insert_with(Vec::new)
+ .or_default()
.push(PathBuf::from(&dir_entry_str));
}
@@ -1258,14 +1395,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
));
}
- let lc_dir_entry_str = dir_entry_str.to_lowercase();
-
- let lcnref = &mut lcnames;
-
- lcnref
- .entry(PathBuf::from(lc_dir_entry_str))
- .or_insert_with(Vec::new)
- .push((PathBuf::from(&dir_entry_str), FileKind::File));
+ register_duplicate_filename(&mut lcnames, dir_entry_str, FileKind::File);
}
Err(e) => {
@@ -1293,7 +1423,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let sizeref = &mut sizes;
let path = path.clone();
- sizeref.entry(fsize).or_insert_with(Vec::new).push(path);
+ sizeref.entry(fsize).or_default().push(path);
};
for (path, (meta, _file_name, is_readme)) in file_names.iter() {
let dir_entry_str = match path.to_str() {
@@ -1362,38 +1492,37 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
}
fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
},
- Some(_) | None => {
- match ft {
- filemagic::Mimetype::Text(LineEnding::Crlf) => {
- e0012!(&dir_entry_str);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ Some(_) | None => match ft {
+ filemagic::Mimetype::Text(LineEnding::Crlf) => {
+ e0012!(&dir_entry_str);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Cr) => {
- e0037!(&dir_entry_str);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ }
+ filemagic::Mimetype::Text(LineEnding::Cr) => {
+ e0037!(&dir_entry_str);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Mixed(0, 0, 0)) => (),
- filemagic::Mimetype::Text(LineEnding::Mixed(cr, lf, crlf)) => {
- //println!(">>>{}: {:?} {},{},{}", &dir_entry_str, ft, x, y, z);
- e0038!(&dir_entry_str, cr, lf, crlf);
- if ARGS.correct_le {
- fix_inconsistent_le(dir_entry_str);
- }
+ }
+ filemagic::Mimetype::Text(LineEnding::Mixed(0, 0, 0)) => (),
+ filemagic::Mimetype::Text(LineEnding::Mixed(cr, lf, crlf)) => {
+ e0038!(&dir_entry_str, cr, lf, crlf);
+ if ARGS.correct_le {
+ fix_inconsistent_le(dir_entry_str);
}
- filemagic::Mimetype::Text(LineEnding::Lf) => (),
- fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
}
- }
+ filemagic::Mimetype::Text(LineEnding::Lf) => (),
+ fmm => error!("Should not occur: {} has {:?}", dir_entry_str, fmm),
+ },
}
}
filemagic::Mimetype::Bom(b) => {
- //println!("{}: {} with BOM detected", dir_entry_str, b.as_ref());
- w0004!(&dir_entry_str, b.as_ref());
+ w0004!(
+ &dir_entry_str,
+ <unicode_bom::Bom as std::convert::AsRef<str>>::as_ref(&b)
+ );
check_and_correct_perms(dir_entry_str, p);
}
filemagic::Mimetype::Binary | filemagic::Mimetype::Script(_) => {
@@ -1462,10 +1591,7 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
let hashref = &mut hashes;
scope.execute(move || {
for (size, path, hash) in rx.iter() {
- hashref
- .entry((size, hash))
- .or_insert_with(DupPath::new)
- .push(path);
+ hashref.entry((size, hash)).or_default().push(path);
}
});
@@ -1489,18 +1615,30 @@ fn check_package(root: &str, tds_zip: &Option<String>) -> Option<DupHashes> {
}
fn print_hardlinks(hashes: &FxHashMap<(u64, u64), Vec<String>>) {
- for ((_devid,inode), eles) in hashes.iter() {
+ for ((_devid, inode), eles) in hashes.iter() {
if eles.len() > 1 {
w0010!(inode);
for hfile in eles.iter() {
info!(" >>> {}", &hfile);
}
+ }
+ }
+}
+fn print_casefolding_tds(hashes: &FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>) {
+ for (k, eles) in hashes.iter() {
+ // println!("pcf_tds: {:?}, {:?}", k, &eles);
+ if eles.len() == 1 {
+ continue;
}
+ e0042!(k.display());
+
+ for (p, ty) in eles {
+ info!(" >>> {} ({})", p.display(), ty);
+ }
}
}
-
fn print_casefolding(hashes: &FxHashMap<PathBuf, Vec<(PathBuf, FileKind)>>) {
for (k, eles) in hashes.iter() {
//println!("pcf: {:?}, {:?}", k, &eles);