aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter <petershh@disroot.org>2022-07-19 15:32:10 +0300
committerGitHub <noreply@github.com>2022-07-19 15:32:10 +0300
commite4a5182a754d683668786a069a6f4b6f6fdff1d4 (patch)
treed18b6ca82c4516452aa360a4765e2c560b7fef00
parentb08ca53f4cbd64e718844eafb3fb5a691715aa88 (diff)
downloadshh-portable-utils-e4a5182a754d683668786a069a6f4b6f6fdff1d4.tar.xz
Add ln
-rw-r--r--.gitignore1
-rw-r--r--package/deps.mak3
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak1
-rw-r--r--src/shh-portable-utils/deps-exe/ln1
-rw-r--r--src/shh-portable-utils/ln.c133
6 files changed, 140 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 7376893..c022798 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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;
+}