ln: explain why we use xstrdup
[platform/upstream/busybox.git] / coreutils / ln.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Mini ln implementation for busybox
4  *
5  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8  */
9
10 /* BB_AUDIT SUSv3 compliant */
11 /* BB_AUDIT GNU options missing: -d, -F, -i, and -v. */
12 /* http://www.opengroup.org/onlinepubs/007904975/utilities/ln.html */
13
14 #include "libbb.h"
15
16 /* This is a NOEXEC applet. Be very careful! */
17
18
19 #define LN_SYMLINK          1
20 #define LN_FORCE            2
21 #define LN_NODEREFERENCE    4
22 #define LN_BACKUP           8
23 #define LN_SUFFIX           16
24
25 int ln_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
26 int ln_main(int argc, char **argv)
27 {
28         int status = EXIT_SUCCESS;
29         int opts;
30         char *last;
31         char *src_name;
32         char *src;
33         char *suffix = (char*)"~";
34         struct stat statbuf;
35         int (*link_func)(const char *, const char *);
36
37         opt_complementary = "-1"; /* min one arg */
38         opts = getopt32(argv, "sfnbS:", &suffix);
39
40         last = argv[argc - 1];
41         argv += optind;
42
43         if (!argv[1]) {
44                 /* "ln PATH/TO/FILE" -> "ln PATH/TO/FILE FILE" */
45                 *--argv = last;
46                 /* xstrdup is needed: "ln -s PATH/TO/FILE/" is equivalent to
47                  * "ln -s PATH/TO/FILE/ FILE", not "ln -s PATH/TO/FILE FILE"
48                  */
49                 last = bb_get_last_path_component_strip(xstrdup(last));
50         }
51
52         do {
53                 src_name = NULL;
54                 src = last;
55
56                 if (is_directory(src,
57                                 (opts & LN_NODEREFERENCE) ^ LN_NODEREFERENCE,
58                                 NULL)
59                 ) {
60                         src_name = xstrdup(*argv);
61                         src = concat_path_file(src, bb_get_last_path_component_strip(src_name));
62                         free(src_name);
63                         src_name = src;
64                 }
65                 if (!(opts & LN_SYMLINK) && stat(*argv, &statbuf)) {
66                         // coreutils: "ln dangling_symlink new_hardlink" works
67                         if (lstat(*argv, &statbuf) || !S_ISLNK(statbuf.st_mode)) {
68                                 bb_simple_perror_msg(*argv);
69                                 status = EXIT_FAILURE;
70                                 free(src_name);
71                                 continue;
72                         }
73                 }
74
75                 if (opts & LN_BACKUP) {
76                         char *backup;
77                         backup = xasprintf("%s%s", src, suffix);
78                         if (rename(src, backup) < 0 && errno != ENOENT) {
79                                 bb_simple_perror_msg(src);
80                                 status = EXIT_FAILURE;
81                                 free(backup);
82                                 continue;
83                         }
84                         free(backup);
85                         /*
86                          * When the source and dest are both hard links to the same
87                          * inode, a rename may succeed even though nothing happened.
88                          * Therefore, always unlink().
89                          */
90                         unlink(src);
91                 } else if (opts & LN_FORCE) {
92                         unlink(src);
93                 }
94
95                 link_func = link;
96                 if (opts & LN_SYMLINK) {
97                         link_func = symlink;
98                 }
99
100                 if (link_func(*argv, src) != 0) {
101                         bb_simple_perror_msg(src);
102                         status = EXIT_FAILURE;
103                 }
104
105                 free(src_name);
106
107         } while ((++argv)[1]);
108
109         return status;
110 }