]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - bin/ln/ln.c
bhnd(9): Fix a few mandoc related issues
[FreeBSD/FreeBSD.git] / bin / ln / ln.c
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 1987, 1993, 1994
5  *      The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 #if 0
33 #ifndef lint
34 static char const copyright[] =
35 "@(#) Copyright (c) 1987, 1993, 1994\n\
36         The Regents of the University of California.  All rights reserved.\n";
37 #endif /* not lint */
38
39 #ifndef lint
40 static char sccsid[] = "@(#)ln.c        8.2 (Berkeley) 3/31/94";
41 #endif /* not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include <sys/param.h>
47 #include <sys/stat.h>
48
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <libgen.h>
53 #include <limits.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 static int      fflag;                  /* Unlink existing files. */
60 static int      Fflag;                  /* Remove empty directories also. */
61 static int      hflag;                  /* Check new name for symlink first. */
62 static int      iflag;                  /* Interactive mode. */
63 static int      Pflag;                  /* Create hard links to symlinks. */
64 static int      sflag;                  /* Symbolic, not hard, link. */
65 static int      vflag;                  /* Verbose output. */
66 static int      wflag;                  /* Warn if symlink target does not
67                                          * exist, and -f is not enabled. */
68 static char     linkch;
69
70 static int      linkit(const char *, const char *, int);
71 static void     usage(void);
72
73 int
74 main(int argc, char *argv[])
75 {
76         struct stat sb;
77         char *p, *targetdir;
78         int ch, exitval;
79
80         /*
81          * Test for the special case where the utility is called as
82          * "link", for which the functionality provided is greatly
83          * simplified.
84          */
85         if ((p = strrchr(argv[0], '/')) == NULL)
86                 p = argv[0];
87         else
88                 ++p;
89         if (strcmp(p, "link") == 0) {
90                 while (getopt(argc, argv, "") != -1)
91                         usage();
92                 argc -= optind;
93                 argv += optind;
94                 if (argc != 2)
95                         usage();
96                 exit(linkit(argv[0], argv[1], 0));
97         }
98
99         while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
100                 switch (ch) {
101                 case 'F':
102                         Fflag = 1;
103                         break;
104                 case 'L':
105                         Pflag = 0;
106                         break;
107                 case 'P':
108                         Pflag = 1;
109                         break;
110                 case 'f':
111                         fflag = 1;
112                         iflag = 0;
113                         wflag = 0;
114                         break;
115                 case 'h':
116                 case 'n':
117                         hflag = 1;
118                         break;
119                 case 'i':
120                         iflag = 1;
121                         fflag = 0;
122                         break;
123                 case 's':
124                         sflag = 1;
125                         break;
126                 case 'v':
127                         vflag = 1;
128                         break;
129                 case 'w':
130                         wflag = 1;
131                         break;
132                 case '?':
133                 default:
134                         usage();
135                 }
136
137         argv += optind;
138         argc -= optind;
139
140         linkch = sflag ? '-' : '=';
141         if (sflag == 0)
142                 Fflag = 0;
143         if (Fflag == 1 && iflag == 0) {
144                 fflag = 1;
145                 wflag = 0;              /* Implied when fflag != 0 */
146         }
147
148         switch(argc) {
149         case 0:
150                 usage();
151                 /* NOTREACHED */
152         case 1:                         /* ln source */
153                 exit(linkit(argv[0], ".", 1));
154         case 2:                         /* ln source target */
155                 exit(linkit(argv[0], argv[1], 0));
156         default:
157                 ;
158         }
159                                         /* ln source1 source2 directory */
160         targetdir = argv[argc - 1];
161         if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
162                 /*
163                  * We were asked not to follow symlinks, but found one at
164                  * the target--simulate "not a directory" error
165                  */
166                 errno = ENOTDIR;
167                 err(1, "%s", targetdir);
168         }
169         if (stat(targetdir, &sb))
170                 err(1, "%s", targetdir);
171         if (!S_ISDIR(sb.st_mode))
172                 usage();
173         for (exitval = 0; *argv != targetdir; ++argv)
174                 exitval |= linkit(*argv, targetdir, 1);
175         exit(exitval);
176 }
177
178 /*
179  * Two pathnames refer to the same directory entry if the directories match
180  * and the final components' names match.
181  */
182 static int
183 samedirent(const char *path1, const char *path2)
184 {
185         const char *file1, *file2;
186         char pathbuf[PATH_MAX];
187         struct stat sb1, sb2;
188
189         if (strcmp(path1, path2) == 0)
190                 return 1;
191         file1 = strrchr(path1, '/');
192         if (file1 != NULL)
193                 file1++;
194         else
195                 file1 = path1;
196         file2 = strrchr(path2, '/');
197         if (file2 != NULL)
198                 file2++;
199         else
200                 file2 = path2;
201         if (strcmp(file1, file2) != 0)
202                 return 0;
203         if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX)
204                 return 0;
205         if (file1 == path1)
206                 memcpy(pathbuf, ".", 2);
207         else {
208                 memcpy(pathbuf, path1, file1 - path1);
209                 pathbuf[file1 - path1] = '\0';
210         }
211         if (stat(pathbuf, &sb1) != 0)
212                 return 0;
213         if (file2 == path2)
214                 memcpy(pathbuf, ".", 2);
215         else {
216                 memcpy(pathbuf, path2, file2 - path2);
217                 pathbuf[file2 - path2] = '\0';
218         }
219         if (stat(pathbuf, &sb2) != 0)
220                 return 0;
221         return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
222 }
223
224 static int
225 linkit(const char *source, const char *target, int isdir)
226 {
227         struct stat sb;
228         const char *p;
229         int ch, exists, first;
230         char path[PATH_MAX];
231         char wbuf[PATH_MAX];
232         char bbuf[PATH_MAX];
233
234         if (!sflag) {
235                 /* If source doesn't exist, quit now. */
236                 if ((Pflag ? lstat : stat)(source, &sb)) {
237                         warn("%s", source);
238                         return (1);
239                 }
240                 /* Only symbolic links to directories. */
241                 if (S_ISDIR(sb.st_mode)) {
242                         errno = EISDIR;
243                         warn("%s", source);
244                         return (1);
245                 }
246         }
247
248         /*
249          * If the target is a directory (and not a symlink if hflag),
250          * append the source's name, unless Fflag is set.
251          */
252         if (!Fflag && (isdir ||
253             (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
254             (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode)))) {
255                 if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
256                     (p = basename(bbuf)) == NULL ||
257                     snprintf(path, sizeof(path), "%s/%s", target, p) >=
258                     (ssize_t)sizeof(path)) {
259                         errno = ENAMETOOLONG;
260                         warn("%s", source);
261                         return (1);
262                 }
263                 target = path;
264         }
265
266         /*
267          * If the link source doesn't exist, and a symbolic link was
268          * requested, and -w was specified, give a warning.
269          */
270         if (sflag && wflag) {
271                 if (*source == '/') {
272                         /* Absolute link source. */
273                         if (stat(source, &sb) != 0)
274                                  warn("warning: %s inaccessible", source);
275                 } else {
276                         /*
277                          * Relative symlink source.  Try to construct the
278                          * absolute path of the source, by appending `source'
279                          * to the parent directory of the target.
280                          */
281                         strlcpy(bbuf, target, sizeof(bbuf));
282                         p = dirname(bbuf);
283                         if (p != NULL) {
284                                 (void)snprintf(wbuf, sizeof(wbuf), "%s/%s",
285                                                 p, source);
286                                 if (stat(wbuf, &sb) != 0)
287                                         warn("warning: %s", source);
288                         }
289                 }
290         }
291
292         /*
293          * If the file exists, first check it is not the same directory entry.
294          */
295         exists = !lstat(target, &sb);
296         if (exists) {
297                 if (!sflag && samedirent(source, target)) {
298                         warnx("%s and %s are the same directory entry",
299                             source, target);
300                         return (1);
301                 }
302         }
303         /*
304          * Then unlink it forcibly if -f was specified
305          * and interactively if -i was specified.
306          */
307         if (fflag && exists) {
308                 if (Fflag && S_ISDIR(sb.st_mode)) {
309                         if (rmdir(target)) {
310                                 warn("%s", target);
311                                 return (1);
312                         }
313                 } else if (unlink(target)) {
314                         warn("%s", target);
315                         return (1);
316                 }
317         } else if (iflag && exists) {
318                 fflush(stdout);
319                 fprintf(stderr, "replace %s? ", target);
320
321                 first = ch = getchar();
322                 while(ch != '\n' && ch != EOF)
323                         ch = getchar();
324                 if (first != 'y' && first != 'Y') {
325                         fprintf(stderr, "not replaced\n");
326                         return (1);
327                 }
328
329                 if (Fflag && S_ISDIR(sb.st_mode)) {
330                         if (rmdir(target)) {
331                                 warn("%s", target);
332                                 return (1);
333                         }
334                 } else if (unlink(target)) {
335                         warn("%s", target);
336                         return (1);
337                 }
338         }
339
340         /* Attempt the link. */
341         if (sflag ? symlink(source, target) :
342             linkat(AT_FDCWD, source, AT_FDCWD, target,
343             Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
344                 warn("%s", target);
345                 return (1);
346         }
347         if (vflag)
348                 (void)printf("%s %c> %s\n", target, linkch, source);
349         return (0);
350 }
351
352 static void
353 usage(void)
354 {
355         (void)fprintf(stderr, "%s\n%s\n%s\n",
356             "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
357             "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir",
358             "       link source_file target_file");
359         exit(1);
360 }