summary refs log tree commit diff
diff options
context:
space:
mode:
authorcfinn <cfinn68916@proton.me>2026-02-15 17:22:10 -0600
committercfinn <cfinn68916@proton.me>2026-02-15 17:22:10 -0600
commit1d29c1368f36b0fc6f7c4c66906e39d85bb63b9c (patch)
tree0797008181d962b53278e8bc62f3bc3bb774fc2a
Initial commit HEAD main
Signed-off-by: cfinn <cfinn68916@proton.me>
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock100
-rw-r--r--Cargo.toml8
-rw-r--r--src/main.rs74
4 files changed, 183 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..72eddf6
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,100 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cpufeatures"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer",
+ "crypto-common",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.182"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
+
+[[package]]
+name = "linkdedup"
+version = "0.1.0"
+dependencies = [
+ "pathdiff",
+ "sha2",
+]
+
+[[package]]
+name = "pathdiff"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
+
+[[package]]
+name = "sha2"
+version = "0.10.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
+name = "typenum"
+version = "1.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3d40618
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "linkdedup"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+sha2 = "0.10.9"
+pathdiff = "0.2.3"
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..53d505b
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,74 @@
+use pathdiff::diff_paths;
+use sha2::{Digest, Sha256};
+use std::fs::File;
+use std::io::Read;
+use std::path::{Path, PathBuf};
+
+fn get_hash(fname: &str) -> Vec<u8> {
+    let result1 = File::open(fname);
+    if result1.is_err() {
+        return vec![];
+    }
+    let mut result = result1.unwrap();
+    let mut hasher = Sha256::new();
+    loop {
+        let mut bif = [0; 1024];
+        let ur = result.read(&mut bif);
+        if ur.is_err() {
+            return vec![];
+        }
+        hasher.update(&bif);
+        if ur.unwrap() < 1024 {
+            break;
+        }
+    }
+
+    hasher.finalize().to_vec()
+}
+
+fn find_files(path: &Path) -> Vec<PathBuf> {
+    let mut ret = vec![];
+    let fss = std::fs::read_dir(path).unwrap();
+    for fs in fss {
+        let fs = fs.unwrap();
+        if fs.file_type().unwrap().is_file() {
+            ret.push(fs.path());
+        } else if fs.file_type().unwrap().is_dir() {
+            let mut p = PathBuf::from(path);
+            p.push(fs.file_name().to_str().unwrap());
+            ret.extend(find_files(p.as_path()));
+        }
+    }
+    ret
+}
+
+fn linkdup(path: &Path) {
+    let mut fhtable = vec![];
+    for z in find_files(path) {
+        let pth = z;
+        let hsh = get_hash(pth.to_str().unwrap());
+        fhtable.push((pth.to_str().unwrap().to_string(), hsh));
+    }
+    fhtable.sort_by(|x, y| Ord::cmp(&x.0, &y.0));
+    let mut fable: Vec<(String, Vec<u8>)> = vec![];
+    for f in fhtable {
+        let prstr = fable
+            .iter()
+            .filter_map(|x| if x.1 == f.1 { Some(x.0.clone()) } else { None })
+            .next();
+        if prstr.is_some() {
+            // let f2m = std::fs::metadata(f.0.clone()).unwrap();
+            std::fs::remove_file(f.0.clone()).unwrap();
+            let mut zz = PathBuf::from(f.0.clone());
+            zz.pop();
+            std::os::unix::fs::symlink(diff_paths(prstr.unwrap(), zz).unwrap(), f.0.clone())
+                .unwrap();
+        } else {
+            fable.push(f);
+        }
+    }
+}
+
+fn main() {
+    linkdup(PathBuf::from(".").as_path());
+}