diff options
author | Peter <petershh@disroot.org> | 2022-07-19 15:32:10 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-19 15:32:10 +0300 |
commit | e4a5182a754d683668786a069a6f4b6f6fdff1d4 (patch) | |
tree | d18b6ca82c4516452aa360a4765e2c560b7fef00 | |
parent | b08ca53f4cbd64e718844eafb3fb5a691715aa88 (diff) | |
download | shh-portable-utils-e4a5182a754d683668786a069a6f4b6f6fdff1d4.tar.xz |
Add ln
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | package/deps.mak | 3 | ||||
-rw-r--r-- | package/modes | 1 | ||||
-rw-r--r-- | package/targets.mak | 1 | ||||
-rw-r--r-- | src/shh-portable-utils/deps-exe/ln | 1 | ||||
-rw-r--r-- | src/shh-portable-utils/ln.c | 133 |
6 files changed, 140 insertions, 0 deletions
@@ -14,6 +14,7 @@ src/include/shh-portable-utils/config.h /dirname /false /link +/ln /tee /true /uname diff --git a/package/deps.mak b/package/deps.mak index fa96b0a..5e42f73 100644 --- a/package/deps.mak +++ b/package/deps.mak @@ -12,6 +12,7 @@ src/shh-portable-utils/cut.o src/shh-portable-utils/cut.lo: src/shh-portable-uti src/shh-portable-utils/dirname.o src/shh-portable-utils/dirname.lo: src/shh-portable-utils/dirname.c src/shh-portable-utils/false.o src/shh-portable-utils/false.lo: src/shh-portable-utils/false.c src/shh-portable-utils/link.o src/shh-portable-utils/link.lo: src/shh-portable-utils/link.c +src/shh-portable-utils/ln.o src/shh-portable-utils/ln.lo: src/shh-portable-utils/ln.c src/shh-portable-utils/shhgetln.o src/shh-portable-utils/shhgetln.lo: src/shh-portable-utils/shhgetln.c src/shh-portable-utils/shhfuncs.h src/shh-portable-utils/tee.o src/shh-portable-utils/tee.lo: src/shh-portable-utils/tee.c src/shh-portable-utils/true.o src/shh-portable-utils/true.lo: src/shh-portable-utils/true.c @@ -36,6 +37,8 @@ false: EXTRA_LIBS := false: src/shh-portable-utils/false.o link: EXTRA_LIBS := -lskarnet link: src/shh-portable-utils/link.o +ln: EXTRA_LIBS := -lskarnet +ln: src/shh-portable-utils/ln.o tee: EXTRA_LIBS := -lskarnet tee: src/shh-portable-utils/tee.o true: EXTRA_LIBS := diff --git a/package/modes b/package/modes index 0b9026f..3cabd5f 100644 --- a/package/modes +++ b/package/modes @@ -7,6 +7,7 @@ cut 0755 dirname 0755 false 0755 link 0755 +ln 0755 tee 0755 true 0755 uniq 0755 diff --git a/package/targets.mak b/package/targets.mak index b4e7c08..237cfb4 100644 --- a/package/targets.mak +++ b/package/targets.mak @@ -8,6 +8,7 @@ cut \ dirname \ false \ link \ +ln \ tee \ true \ uname \ diff --git a/src/shh-portable-utils/deps-exe/ln b/src/shh-portable-utils/deps-exe/ln new file mode 100644 index 0000000..e7187fe --- /dev/null +++ b/src/shh-portable-utils/deps-exe/ln @@ -0,0 +1 @@ +-lskarnet diff --git a/src/shh-portable-utils/ln.c b/src/shh-portable-utils/ln.c new file mode 100644 index 0000000..54d34f6 --- /dev/null +++ b/src/shh-portable-utils/ln.c @@ -0,0 +1,133 @@ +#include <string.h> +#include <errno.h> + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <skalibs/strerr2.h> +#include <skalibs/sgetopt.h> +#include <skalibs/djbunix.h> +#include <skalibs/stralloc.h> + +#define USAGE "ln [-fs] [-L|-P] source_file target_file | ln [-fs] [-L|-P] source_file... target_dir" + +int make_link(char const*, char const*, int, int, int); + +int main(int argc, char const *const *argv) +{ + struct stat st; + int result; + int failure = 0; + int create_symlink = 0; + int force_unlink = 0; + int flag = 0; + stralloc s = STRALLOC_ZERO; + subgetopt l = SUBGETOPT_ZERO; + + PROG = "ln"; + + for (;;) { + int opt = subgetopt_r(argc, argv, "fsLP", &l); + if (opt == -1) + break; + switch (opt) { + case 'f': + force_unlink = 1; + break; + case 's': + create_symlink = 1; + break; + case 'L': + flag = AT_SYMLINK_FOLLOW; + break; + case 'P': + flag = 0; + break; + default: + strerr_dieusage(100, USAGE); + } + } + argc -= l.ind; + argv += l.ind; + + if (argc < 2) + strerr_dieusage(100, USAGE); + + result = lstat(argv[argc - 1], &st); + if (result == -1 && errno != ENOENT) { + strerr_diefu2sys(111, "lstat ", argv[argc - 1]); + } + + if (result == -1 || (!S_ISDIR(st.st_mode))) { + if (argc != 2) + strerr_dieusage(100, USAGE); + if (!S_ISDIR(st.st_mode)) { + failure = failure || make_link(argv[0], argv[1], create_symlink, + force_unlink, flag); + } + return failure ? 111 : 0; + } + + for (size_t i = 0; i < argc - 1; i++) { + s.len = 0; + if (!stralloc_cats(&s, argv[argc - 1])) { + strerr_warnwu2sys("Build target filename for ", argv[i]); + failure = 1; + continue; + } + if (!stralloc_append(&s, '/')) { + strerr_warnwu2sys("Build target filename for ", argv[i]); + failure = 1; + continue; + } + if (!sabasename(&s, argv[i], strlen(argv[i]))) { + strerr_warnwu2sys("Build target filename for ", argv[i]); + failure = 1; + continue; + } + if (!stralloc_0(&s)) { + strerr_warnwu2sys("Build target filename for ", argv[i]); + failure = 1; + continue; + } + + failure = failure || make_link(argv[i], s.s, create_symlink, + force_unlink, flag); + } + + return failure ? 111 : 0; +} + +int make_link(char const *src, char const *target, int create_symlink, + int force_unlink, int flag) +{ + int result; + if (create_symlink) + result = symlink(src, target); + else + result = linkat(AT_FDCWD, src, AT_FDCWD, target, flag); + + if (result == -1) { + if (!force_unlink || errno != EEXIST) { + strerr_warnwu4sys("create link ", target, " to ", src); + return 1; + } + if (unlink(target) == -1) { + strerr_warnwu2sys("remove ", target); + return 1; + } + + if (create_symlink) + result = symlink(src, target); + else + result = linkat(AT_FDCWD, src, AT_FDCWD, target, flag); + + if (result == -1) { + strerr_warnwu4sys("create link ", target, " to ", src); + return 1; + } + } + return 0; +} |