1139969Simp/*-
21556Srgrimes * Copyright (c) 1987, 1993, 1994
31556Srgrimes *	The Regents of the University of California.  All rights reserved.
41556Srgrimes *
51556Srgrimes * Redistribution and use in source and binary forms, with or without
61556Srgrimes * modification, are permitted provided that the following conditions
71556Srgrimes * are met:
81556Srgrimes * 1. Redistributions of source code must retain the above copyright
91556Srgrimes *    notice, this list of conditions and the following disclaimer.
101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111556Srgrimes *    notice, this list of conditions and the following disclaimer in the
121556Srgrimes *    documentation and/or other materials provided with the distribution.
131556Srgrimes * 4. Neither the name of the University nor the names of its contributors
141556Srgrimes *    may be used to endorse or promote products derived from this software
151556Srgrimes *    without specific prior written permission.
161556Srgrimes *
171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271556Srgrimes * SUCH DAMAGE.
281556Srgrimes */
291556Srgrimes
30114433Sobrien#if 0
311556Srgrimes#ifndef lint
3220420Sstevestatic char const copyright[] =
331556Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\
341556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
351556Srgrimes#endif /* not lint */
361556Srgrimes
371556Srgrimes#ifndef lint
3836046Scharnierstatic char sccsid[] = "@(#)ln.c	8.2 (Berkeley) 3/31/94";
39114433Sobrien#endif /* not lint */
4036046Scharnier#endif
4199109Sobrien#include <sys/cdefs.h>
4299109Sobrien__FBSDID("$FreeBSD$");
431556Srgrimes
441556Srgrimes#include <sys/param.h>
451556Srgrimes#include <sys/stat.h>
461556Srgrimes
471556Srgrimes#include <err.h>
481556Srgrimes#include <errno.h>
49195768Sjilles#include <fcntl.h>
50207021Sjilles#include <libgen.h>
5177404Simp#include <limits.h>
521556Srgrimes#include <stdio.h>
531556Srgrimes#include <stdlib.h>
541556Srgrimes#include <string.h>
551556Srgrimes#include <unistd.h>
561556Srgrimes
57226961Sedstatic int	fflag;			/* Unlink existing files. */
58226961Sedstatic int	Fflag;			/* Remove empty directories also. */
59226961Sedstatic int	hflag;			/* Check new name for symlink first. */
60226961Sedstatic int	iflag;			/* Interactive mode. */
61226961Sedstatic int	Pflag;			/* Create hard links to symlinks. */
62226961Sedstatic int	sflag;			/* Symbolic, not hard, link. */
63226961Sedstatic int	vflag;			/* Verbose output. */
64226961Sedstatic int	wflag;			/* Warn if symlink target does not
65179603Skeramida					 * exist, and -f is not enabled. */
66226961Sedstatic char	linkch;
671556Srgrimes
68251261Seadlerstatic int	linkit(const char *, const char *, int);
69251261Seadlerstatic void	usage(void);
701556Srgrimes
711556Srgrimesint
7290110Simpmain(int argc, char *argv[])
731556Srgrimes{
741556Srgrimes	struct stat sb;
75173701Sru	char *p, *targetdir;
761556Srgrimes	int ch, exitval;
771556Srgrimes
7854895Ssheldonh	/*
7954895Ssheldonh	 * Test for the special case where the utility is called as
8054895Ssheldonh	 * "link", for which the functionality provided is greatly
8154895Ssheldonh	 * simplified.
8254895Ssheldonh	 */
83219680Sjilles	if ((p = strrchr(argv[0], '/')) == NULL)
8454895Ssheldonh		p = argv[0];
8554895Ssheldonh	else
8654895Ssheldonh		++p;
8754895Ssheldonh	if (strcmp(p, "link") == 0) {
8897531Stjr		while (getopt(argc, argv, "") != -1)
8954895Ssheldonh			usage();
9097531Stjr		argc -= optind;
9197531Stjr		argv += optind;
9297531Stjr		if (argc != 2)
9397531Stjr			usage();
9497531Stjr		exit(linkit(argv[0], argv[1], 0));
9554895Ssheldonh	}
9654895Ssheldonh
97195768Sjilles	while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
981556Srgrimes		switch (ch) {
99155667Sglebius		case 'F':
100155667Sglebius			Fflag = 1;
101155667Sglebius			break;
102195768Sjilles		case 'L':
103195768Sjilles			Pflag = 0;
104195768Sjilles			break;
105195768Sjilles		case 'P':
106195768Sjilles			Pflag = 1;
107195768Sjilles			break;
1081556Srgrimes		case 'f':
1091556Srgrimes			fflag = 1;
11064787Ssheldonh			iflag = 0;
111179603Skeramida			wflag = 0;
1121556Srgrimes			break;
11376039Ssobomax		case 'h':
11476039Ssobomax		case 'n':
11576039Ssobomax			hflag = 1;
11676039Ssobomax			break;
11764638Ssheldonh		case 'i':
11864638Ssheldonh			iflag = 1;
11964787Ssheldonh			fflag = 0;
12064638Ssheldonh			break;
1211556Srgrimes		case 's':
1221556Srgrimes			sflag = 1;
1231556Srgrimes			break;
12451148Sobrien		case 'v':
12551148Sobrien			vflag = 1;
12651148Sobrien			break;
127179603Skeramida		case 'w':
128179603Skeramida			wflag = 1;
129179603Skeramida			break;
1301556Srgrimes		case '?':
1311556Srgrimes		default:
1321556Srgrimes			usage();
1331556Srgrimes		}
1341556Srgrimes
1351556Srgrimes	argv += optind;
1361556Srgrimes	argc -= optind;
1371556Srgrimes
13851148Sobrien	linkch = sflag ? '-' : '=';
139155667Sglebius	if (sflag == 0)
140155667Sglebius		Fflag = 0;
141179603Skeramida	if (Fflag == 1 && iflag == 0) {
142155667Sglebius		fflag = 1;
143179603Skeramida		wflag = 0;		/* Implied when fflag != 0 */
144179603Skeramida	}
1451556Srgrimes
1461556Srgrimes	switch(argc) {
1471556Srgrimes	case 0:
1481556Srgrimes		usage();
14976039Ssobomax		/* NOTREACHED */
150173701Sru	case 1:				/* ln source */
1511556Srgrimes		exit(linkit(argv[0], ".", 1));
152173701Sru	case 2:				/* ln source target */
1531556Srgrimes		exit(linkit(argv[0], argv[1], 0));
15491084Smarkm	default:
15596373Sjedgar		;
1561556Srgrimes	}
157173701Sru					/* ln source1 source2 directory */
158173701Sru	targetdir = argv[argc - 1];
159173701Sru	if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
16076039Ssobomax		/*
16176039Ssobomax		 * We were asked not to follow symlinks, but found one at
16276039Ssobomax		 * the target--simulate "not a directory" error
16376039Ssobomax		 */
16476039Ssobomax		errno = ENOTDIR;
165173701Sru		err(1, "%s", targetdir);
16676039Ssobomax	}
167173701Sru	if (stat(targetdir, &sb))
168173701Sru		err(1, "%s", targetdir);
1691556Srgrimes	if (!S_ISDIR(sb.st_mode))
1701556Srgrimes		usage();
171173701Sru	for (exitval = 0; *argv != targetdir; ++argv)
172173701Sru		exitval |= linkit(*argv, targetdir, 1);
1731556Srgrimes	exit(exitval);
1741556Srgrimes}
1751556Srgrimes
176206773Sjilles/*
177206773Sjilles * Two pathnames refer to the same directory entry if the directories match
178206773Sjilles * and the final components' names match.
179206773Sjilles */
180206773Sjillesstatic int
181206773Sjillessamedirent(const char *path1, const char *path2)
182206773Sjilles{
183206773Sjilles	const char *file1, *file2;
184206773Sjilles	char pathbuf[PATH_MAX];
185206773Sjilles	struct stat sb1, sb2;
186206773Sjilles
187206773Sjilles	if (strcmp(path1, path2) == 0)
188206773Sjilles		return 1;
189206773Sjilles	file1 = strrchr(path1, '/');
190206773Sjilles	if (file1 != NULL)
191206773Sjilles		file1++;
192206773Sjilles	else
193206773Sjilles		file1 = path1;
194206773Sjilles	file2 = strrchr(path2, '/');
195206773Sjilles	if (file2 != NULL)
196206773Sjilles		file2++;
197206773Sjilles	else
198206773Sjilles		file2 = path2;
199206773Sjilles	if (strcmp(file1, file2) != 0)
200206773Sjilles		return 0;
201206773Sjilles	if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX)
202206773Sjilles		return 0;
203206773Sjilles	if (file1 == path1)
204206773Sjilles		memcpy(pathbuf, ".", 2);
205206773Sjilles	else {
206206773Sjilles		memcpy(pathbuf, path1, file1 - path1);
207206773Sjilles		pathbuf[file1 - path1] = '\0';
208206773Sjilles	}
209206773Sjilles	if (stat(pathbuf, &sb1) != 0)
210206773Sjilles		return 0;
211206773Sjilles	if (file2 == path2)
212206773Sjilles		memcpy(pathbuf, ".", 2);
213206773Sjilles	else {
214206773Sjilles		memcpy(pathbuf, path2, file2 - path2);
215206773Sjilles		pathbuf[file2 - path2] = '\0';
216206773Sjilles	}
217206773Sjilles	if (stat(pathbuf, &sb2) != 0)
218206773Sjilles		return 0;
219206773Sjilles	return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
220206773Sjilles}
221206773Sjilles
222251261Seadlerstatic int
223173701Srulinkit(const char *source, const char *target, int isdir)
2241556Srgrimes{
2251556Srgrimes	struct stat sb;
22676877Skris	const char *p;
22764787Ssheldonh	int ch, exists, first;
22877404Simp	char path[PATH_MAX];
229179603Skeramida	char wbuf[PATH_MAX];
230207021Sjilles	char bbuf[PATH_MAX];
2311556Srgrimes
2321556Srgrimes	if (!sflag) {
233173701Sru		/* If source doesn't exist, quit now. */
234195768Sjilles		if ((Pflag ? lstat : stat)(source, &sb)) {
235173701Sru			warn("%s", source);
2361556Srgrimes			return (1);
2371556Srgrimes		}
23815900Speter		/* Only symbolic links to directories. */
23915900Speter		if (S_ISDIR(sb.st_mode)) {
24015900Speter			errno = EISDIR;
241173701Sru			warn("%s", source);
2421556Srgrimes			return (1);
2431556Srgrimes		}
2441556Srgrimes	}
2451556Srgrimes
24676039Ssobomax	/*
247173701Sru	 * If the target is a directory (and not a symlink if hflag),
248173701Sru	 * append the source's name.
24976039Ssobomax	 */
25076039Ssobomax	if (isdir ||
251173701Sru	    (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
252173701Sru	    (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
253207021Sjilles		if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
254207021Sjilles		    (p = basename(bbuf)) == NULL ||
255207021Sjilles		    snprintf(path, sizeof(path), "%s/%s", target, p) >=
256101094Smarkm		    (ssize_t)sizeof(path)) {
25799019Stjr			errno = ENAMETOOLONG;
258173701Sru			warn("%s", source);
25999019Stjr			return (1);
26099019Stjr		}
261173701Sru		target = path;
26276039Ssobomax	}
2631556Srgrimes
2641556Srgrimes	/*
265179603Skeramida	 * If the link source doesn't exist, and a symbolic link was
266179603Skeramida	 * requested, and -w was specified, give a warning.
267179603Skeramida	 */
268179603Skeramida	if (sflag && wflag) {
269179603Skeramida		if (*source == '/') {
270179603Skeramida			/* Absolute link source. */
271179603Skeramida			if (stat(source, &sb) != 0)
272179603Skeramida				 warn("warning: %s inaccessible", source);
273179603Skeramida		} else {
274179603Skeramida			/*
275179603Skeramida			 * Relative symlink source.  Try to construct the
276179603Skeramida			 * absolute path of the source, by appending `source'
277179603Skeramida			 * to the parent directory of the target.
278179603Skeramida			 */
279207021Sjilles			strlcpy(bbuf, target, sizeof(bbuf));
280207021Sjilles			p = dirname(bbuf);
281207021Sjilles			if (p != NULL) {
282207021Sjilles				(void)snprintf(wbuf, sizeof(wbuf), "%s/%s",
283207021Sjilles						p, source);
284207021Sjilles				if (stat(wbuf, &sb) != 0)
285207021Sjilles					warn("warning: %s", source);
286207021Sjilles			}
287179603Skeramida		}
288179603Skeramida	}
289206773Sjilles
290179603Skeramida	/*
291206773Sjilles	 * If the file exists, first check it is not the same directory entry.
292206773Sjilles	 */
293206773Sjilles	exists = !lstat(target, &sb);
294206773Sjilles	if (exists) {
295206773Sjilles		if (!sflag && samedirent(source, target)) {
296206773Sjilles			warnx("%s and %s are the same directory entry",
297206773Sjilles			    source, target);
298206773Sjilles			return (1);
299206773Sjilles		}
300206773Sjilles	}
301206773Sjilles	/*
302206773Sjilles	 * Then unlink it forcibly if -f was specified
30364787Ssheldonh	 * and interactively if -i was specified.
3041556Srgrimes	 */
30564787Ssheldonh	if (fflag && exists) {
306155667Sglebius		if (Fflag && S_ISDIR(sb.st_mode)) {
307173701Sru			if (rmdir(target)) {
308173701Sru				warn("%s", target);
309155667Sglebius				return (1);
310155667Sglebius			}
311173701Sru		} else if (unlink(target)) {
312173701Sru			warn("%s", target);
31364787Ssheldonh			return (1);
31464787Ssheldonh		}
31564638Ssheldonh	} else if (iflag && exists) {
31664787Ssheldonh		fflush(stdout);
317173701Sru		fprintf(stderr, "replace %s? ", target);
31864638Ssheldonh
31964638Ssheldonh		first = ch = getchar();
32064638Ssheldonh		while(ch != '\n' && ch != EOF)
32164638Ssheldonh			ch = getchar();
32264787Ssheldonh		if (first != 'y' && first != 'Y') {
32364787Ssheldonh			fprintf(stderr, "not replaced\n");
32464787Ssheldonh			return (1);
32564787Ssheldonh		}
32664638Ssheldonh
327155667Sglebius		if (Fflag && S_ISDIR(sb.st_mode)) {
328173701Sru			if (rmdir(target)) {
329173701Sru				warn("%s", target);
330155667Sglebius				return (1);
331155667Sglebius			}
332173701Sru		} else if (unlink(target)) {
333173701Sru			warn("%s", target);
33464638Ssheldonh			return (1);
33564638Ssheldonh		}
3361556Srgrimes	}
33764787Ssheldonh
33864787Ssheldonh	/* Attempt the link. */
339195768Sjilles	if (sflag ? symlink(source, target) :
340195768Sjilles	    linkat(AT_FDCWD, source, AT_FDCWD, target,
341195768Sjilles	    Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
342173701Sru		warn("%s", target);
34364638Ssheldonh		return (1);
34464638Ssheldonh	}
34551148Sobrien	if (vflag)
346173701Sru		(void)printf("%s %c> %s\n", target, linkch, source);
3471556Srgrimes	return (0);
3481556Srgrimes}
3491556Srgrimes
350251261Seadlerstatic void
35190110Simpusage(void)
3521556Srgrimes{
35354895Ssheldonh	(void)fprintf(stderr, "%s\n%s\n%s\n",
354195768Sjilles	    "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
355195768Sjilles	    "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir",
356141578Sru	    "       link source_file target_file");
3571556Srgrimes	exit(1);
3581556Srgrimes}
359