The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=bee7cf9e97f6d7bdc918421a93270fa88659808b

commit bee7cf9e97f6d7bdc918421a93270fa88659808b
Author:     Dag-Erling Smørgrav <d...@freebsd.org>
AuthorDate: 2024-04-04 14:14:50 +0000
Commit:     Dag-Erling Smørgrav <d...@freebsd.org>
CommitDate: 2024-04-04 14:14:50 +0000

    ln: Improve link(1) variant of ln(1).
    
    * Give link(1) its own usage message.
    * Use getprogname(3) instead of rolling our own.
    * Verify that the target file does not already exist.
    * Add tests specific to link(1).
    
    MFC after:      3 days
    Sponsored by:   Klara, Inc.
    Reviewed by:    allanjude
    Differential Revision:  https://reviews.freebsd.org/D44635
---
 bin/ln/ln.c             | 32 +++++++++++++++---------
 bin/ln/tests/ln_test.sh | 65 ++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 83 insertions(+), 14 deletions(-)

diff --git a/bin/ln/ln.c b/bin/ln/ln.c
index 31fe3b35e25e..6300658effa1 100644
--- a/bin/ln/ln.c
+++ b/bin/ln/ln.c
@@ -55,13 +55,14 @@ static bool wflag;                  /* Warn if symlink 
target does not
 static char    linkch;
 
 static int     linkit(const char *, const char *, bool);
+static void    link_usage(void) __dead2;
 static void    usage(void) __dead2;
 
 int
 main(int argc, char *argv[])
 {
        struct stat sb;
-       char *p, *targetdir;
+       char *targetdir;
        int ch, exitval;
 
        /*
@@ -69,17 +70,20 @@ main(int argc, char *argv[])
         * "link", for which the functionality provided is greatly
         * simplified.
         */
-       if ((p = strrchr(argv[0], '/')) == NULL)
-               p = argv[0];
-       else
-               ++p;
-       if (strcmp(p, "link") == 0) {
+       if (strcmp(getprogname(), "link") == 0) {
                while (getopt(argc, argv, "") != -1)
-                       usage();
+                       link_usage();
                argc -= optind;
                argv += optind;
                if (argc != 2)
-                       usage();
+                       link_usage();
+               if (lstat(argv[1], &sb) == 0)
+                       errc(1, EEXIST, "%s", argv[1]);
+               /*
+                * We could simply call link(2) here, but linkit()
+                * performs additional checks and gives better
+                * diagnostics.
+                */
                exit(linkit(argv[0], argv[1], false));
        }
 
@@ -337,12 +341,18 @@ linkit(const char *source, const char *target, bool isdir)
        return (0);
 }
 
+static void
+link_usage(void)
+{
+       (void)fprintf(stderr, "usage: link source_file target_file\n");
+       exit(1);
+}
+
 static void
 usage(void)
 {
-       (void)fprintf(stderr, "%s\n%s\n%s\n",
+       (void)fprintf(stderr, "%s\n%s\n",
            "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file 
[target_file]",
-           "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... 
target_dir",
-           "       link source_file target_file");
+           "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... 
target_dir");
        exit(1);
 }
diff --git a/bin/ln/tests/ln_test.sh b/bin/ln/tests/ln_test.sh
index 8e5dcf81e61f..82bc556842d8 100644
--- a/bin/ln/tests/ln_test.sh
+++ b/bin/ln/tests/ln_test.sh
@@ -90,7 +90,7 @@ target_exists_hard_body()
 {
        atf_check touch A B
        atf_check -s exit:1 -e inline:'ln: B: File exists\n' \
-               ln A B
+           ln A B
 }
 
 atf_test_case target_exists_symbolic
@@ -103,7 +103,7 @@ target_exists_symbolic_body()
 {
        atf_check touch A B
        atf_check -s exit:1 -e inline:'ln: B: File exists\n' \
-               ln -s A B
+           ln -s A B
 }
 
 atf_test_case shf_flag_dir
@@ -210,10 +210,65 @@ sw_flag_head()
 sw_flag_body()
 {
        atf_check -s exit:0 -e inline:'ln: warning: A: No such file or 
directory\n' \
-               ln -sw A B
+           ln -sw A B
        atf_check_symlink_to A B
 }
 
+atf_test_case link_argc
+link_argc_head() {
+       atf_set "descr" "Verify that link(1) requires exactly two arguments"
+}
+link_argc_body() {
+       atf_check -s exit:1 -e match:"usage: link" \
+           link foo
+       atf_check -s exit:1 -e match:"No such file" \
+           link foo bar
+       atf_check -s exit:1 -e match:"No such file" \
+           link -- foo bar
+       atf_check -s exit:1 -e match:"usage: link" \
+           link foo bar baz
+}
+
+atf_test_case link_basic
+link_basic_head() {
+       atf_set "descr" "Verify that link(1) creates a link"
+}
+link_basic_body() {
+       touch foo
+       atf_check link foo bar
+       atf_check_same_file foo bar
+       rm bar
+       ln -s foo bar
+       atf_check link bar baz
+       atf_check_same_file foo baz
+}
+
+atf_test_case link_eexist
+link_eexist_head() {
+       atf_set "descr" "Verify that link(1) fails if the target exists"
+}
+link_eexist_body() {
+       touch foo bar
+       atf_check -s exit:1 -e match:"bar.*exists" \
+           link foo bar
+       ln -s non-existent baz
+       atf_check -s exit:1 -e match:"baz.*exists" \
+           link foo baz
+}
+
+atf_test_case link_eisdir
+link_eisdir_head() {
+       atf_set "descr" "Verify that link(1) fails if the source is a directory"
+}
+link_eisdir_body() {
+       mkdir foo
+       atf_check -s exit:1 -e match:"foo.*directory" \
+           link foo bar
+       ln -s foo bar
+       atf_check -s exit:1 -e match:"bar.*directory" \
+           link bar baz
+}
+
 atf_init_test_cases()
 {
        atf_add_test_case L_flag
@@ -229,4 +284,8 @@ atf_init_test_cases()
        atf_add_test_case s_flag
        atf_add_test_case s_flag_broken
        atf_add_test_case sw_flag
+       atf_add_test_case link_argc
+       atf_add_test_case link_basic
+       atf_add_test_case link_eexist
+       atf_add_test_case link_eisdir
 }

Reply via email to