From 43acf6a95fc4230fd72520d3600e8d987490aeeb Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 30 Aug 2022 22:01:01 -0400 Subject: initial commit --- .gitignore | 1 + hlcp | Bin 0 -> 348056 bytes main.ha | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 .gitignore create mode 100755 hlcp create mode 100644 main.ha diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..145f5d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +test_* diff --git a/hlcp b/hlcp new file mode 100755 index 0000000..69bb49a Binary files /dev/null and b/hlcp differ diff --git a/main.ha b/main.ha new file mode 100644 index 0000000..28d8658 --- /dev/null +++ b/main.ha @@ -0,0 +1,158 @@ +use fmt; +use fs; +use getopt; +use io; +use os; +use strings; + +// Version 0.5 +// TODO: do something about existing directories/files + +// Also does the same exact thing as `cp -lr` except cp is less likely to have +// strange bugs in it. + +// Global Vars +let verbose: bool = false; +let test_run: bool = false; + +export fn main() void = { + const help: [_]getopt::help = [ + "Hard Link Copy for directories.", + ('d', "destination", "The destination which will be hard link copied to.\n If it does not exist it will be created."), + ('s', "source", "The source directory which will be hard link copied from. Must be a directory."), + ('t', "Test-run. This indicates that nothing should actually occur but look like it will. Useful with verbose."), + ('v', "Toggle verbose - quiet by default unless error."), + ]; + const cmd = getopt::parse(os::args, help...); + defer getopt::finish(&cmd); + + let destination: str = ""; + let source: str = ""; + for (let i = 0z; i < len(cmd.opts); i += 1) { + const opt = cmd.opts[i]; + switch (opt.0) { + case 'd' => + destination = opt.1; + case 's' => + source = opt.1; + case 't' => + test_run = !test_run; + case 'v' => + verbose = !verbose; + }; + }; + + if (len(destination) > 0 && len(source) > 0) { + if (destination == source) { + fmt::fatal("destination cannot be the source."); + }; + } else { + usage(help); + }; + + if (! fs::exists(os::cwd, source) ) { + fmt::fatal("source doesn't exist."); + }; + + const source_fh = itorator(source); + const source_resolve = strings::dup(fs::resolve(os::cwd, source)); + const dest_resolve = strings::dup(fs::resolve(os::cwd, destination)); + + if (! fs::exists(os::cwd, destination) ) { + const mode = fs::mode::USER_RWX + fs::mode::GROUP_RX + fs::mode::OTHER_RX; + match (os::mkdir(dest_resolve, mode)) { + case void => + yield; + case let err: fs::error => + fmt::fatalf("Cannot mkdir {}: {}", dest_resolve, fs::strerror(err)); + }; + }; + + needful(source_fh, source_resolve, dest_resolve); + os::exit(0); +}; + +fn itorator(source: str) *fs::iterator = { + const source_fh = match (os::iter( fs::resolve(os::cwd, source) )) { + case let f: *fs::iterator => + yield f; + case let err: fs::error => + fmt::fatalf("Unable to open {}: {}", + source, fs::strerror(err)); + }; + return source_fh; +}; + +fn needful(source_fh: *fs::iterator, source: str, destination: str) void = { + for (true) { + const file = match (fs::next(source_fh)) { + case let f: fs::dirent => + yield f; + case void => + break; + }; + + if (file.name == "." || file.name == "..") { + continue; + }; + + + const source_concat = + strings::dup(fs::resolve(os::cwd, strings::concat(source, "/", file.name))); + defer source_concat; + + const dest_concat = + strings::dup(fs::resolve(os::cwd, strings::concat(destination, "/", file.name))); + defer dest_concat; + + //fmt::println(source_concat)!; + //fmt::println(dest_concat)!; + //fmt::println(fs::mode_str(file.ftype))!; + + if (fs::isdir(file.ftype)) { + if (verbose) { + fmt::printfln("{} is a directory", source_concat)!; + }; + if (! fs::exists(os::cwd, dest_concat) ) { + if (! test_run) { + // Ideally would just copy file.ftype + // but file.ftype is either d--------- or + // ----------. Dunno why. + const mode = + fs::mode::USER_RWX + fs::mode::GROUP_RX + fs::mode::OTHER_RX; + match (os::mkdir(dest_concat, mode)) { + case void => + yield; + case let err: fs::error => + fmt::errorfln("Cannot mkdir {}: {}", + dest_concat, fs::strerror(err))!; + }; + }; + if (verbose) { + fmt::printfln("{} making dir.", dest_concat)!; + }; + }; + + const dir_fh = itorator(source_concat); + needful(dir_fh, fs::resolve(os::cwd, source_concat), dest_concat); + } else { + if (! test_run) { + match (fs::link(os::cwd, source_concat, dest_concat)) { + case void => + yield; + case let err: fs::error => + fmt::errorfln("Cannot hardlink {}: {}", + dest_concat, fs::strerror(err))!; + }; + }; + if (verbose) { + fmt::printfln("{} hard linked.", dest_concat)!; + }; + }; + }; +}; + +@noreturn fn usage(help: []getopt::help) void = { + getopt::printusage(os::stderr, os::args[0], help); + os::exit(1); +}; -- cgit v1.2.3