summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjake <jake@jakes-mail.top>2022-08-30 22:01:01 -0400
committerjake <jake@jakes-mail.top>2022-08-30 22:01:01 -0400
commit43acf6a95fc4230fd72520d3600e8d987490aeeb (patch)
tree8984e2028fb7bd548baf1c5f8975e7bc61452f81
initial commitHEADmaster
-rw-r--r--.gitignore1
-rwxr-xr-xhlcpbin0 -> 348056 bytes
-rw-r--r--main.ha158
3 files changed, 159 insertions, 0 deletions
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
--- /dev/null
+++ b/hlcp
Binary files 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);
+};