ln.c revision 97531
11556Srgrimes/*
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 * 3. All advertising materials mentioning features or use of this software
141556Srgrimes *    must display the following acknowledgement:
151556Srgrimes *	This product includes software developed by the University of
161556Srgrimes *	California, Berkeley and its contributors.
171556Srgrimes * 4. Neither the name of the University nor the names of its contributors
181556Srgrimes *    may be used to endorse or promote products derived from this software
191556Srgrimes *    without specific prior written permission.
201556Srgrimes *
211556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241556Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311556Srgrimes * SUCH DAMAGE.
321556Srgrimes */
331556Srgrimes
341556Srgrimes#ifndef lint
3520420Sstevestatic char const copyright[] =
361556Srgrimes"@(#) Copyright (c) 1987, 1993, 1994\n\
371556Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381556Srgrimes#endif /* not lint */
391556Srgrimes
401556Srgrimes#ifndef lint
4136046Scharnier#if 0
4236046Scharnierstatic char sccsid[] = "@(#)ln.c	8.2 (Berkeley) 3/31/94";
4336046Scharnier#endif
4436046Scharnierstatic const char rcsid[] =
4550471Speter  "$FreeBSD: head/bin/ln/ln.c 97531 2002-05-30 00:57:38Z tjr $";
461556Srgrimes#endif /* not lint */
471556Srgrimes
481556Srgrimes#include <sys/param.h>
491556Srgrimes#include <sys/stat.h>
501556Srgrimes
511556Srgrimes#include <err.h>
521556Srgrimes#include <errno.h>
5377404Simp#include <limits.h>
541556Srgrimes#include <stdio.h>
551556Srgrimes#include <stdlib.h>
561556Srgrimes#include <string.h>
571556Srgrimes#include <unistd.h>
581556Srgrimes
591556Srgrimesint	fflag;				/* Unlink existing files. */
6076039Ssobomaxint	hflag;				/* Check new name for symlink first. */
6164638Ssheldonhint	iflag;				/* Interactive mode. */
621556Srgrimesint	sflag;				/* Symbolic, not hard, link. */
6351148Sobrienint	vflag;				/* Verbose output. */
641556Srgrimes					/* System link call. */
6590110Simpint (*linkf)(const char *, const char *);
6651148Sobrienchar	linkch;
671556Srgrimes
6890110Simpint	linkit(const char *, const char *, int);
6990110Simpvoid	usage(void);
701556Srgrimes
711556Srgrimesint
7290110Simpmain(int argc, char *argv[])
731556Srgrimes{
741556Srgrimes	struct stat sb;
7576877Skris	char *p, *sourcedir;
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	 */
8354895Ssheldonh	if ((p = rindex(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		linkf = link;
9597531Stjr		exit(linkit(argv[0], argv[1], 0));
9654895Ssheldonh	}
9754895Ssheldonh
9876039Ssobomax	while ((ch = getopt(argc, argv, "fhinsv")) != -1)
991556Srgrimes		switch (ch) {
1001556Srgrimes		case 'f':
1011556Srgrimes			fflag = 1;
10264787Ssheldonh			iflag = 0;
1031556Srgrimes			break;
10476039Ssobomax		case 'h':
10576039Ssobomax		case 'n':
10676039Ssobomax			hflag = 1;
10776039Ssobomax			break;
10864638Ssheldonh		case 'i':
10964638Ssheldonh			iflag = 1;
11064787Ssheldonh			fflag = 0;
11164638Ssheldonh			break;
1121556Srgrimes		case 's':
1131556Srgrimes			sflag = 1;
1141556Srgrimes			break;
11551148Sobrien		case 'v':
11651148Sobrien			vflag = 1;
11751148Sobrien			break;
1181556Srgrimes		case '?':
1191556Srgrimes		default:
1201556Srgrimes			usage();
1211556Srgrimes		}
1221556Srgrimes
1231556Srgrimes	argv += optind;
1241556Srgrimes	argc -= optind;
1251556Srgrimes
1261556Srgrimes	linkf = sflag ? symlink : link;
12751148Sobrien	linkch = sflag ? '-' : '=';
1281556Srgrimes
1291556Srgrimes	switch(argc) {
1301556Srgrimes	case 0:
1311556Srgrimes		usage();
13276039Ssobomax		/* NOTREACHED */
1331556Srgrimes	case 1:				/* ln target */
1341556Srgrimes		exit(linkit(argv[0], ".", 1));
1351556Srgrimes	case 2:				/* ln target source */
1361556Srgrimes		exit(linkit(argv[0], argv[1], 0));
13791084Smarkm	default:
13896373Sjedgar		;
1391556Srgrimes	}
1401556Srgrimes					/* ln target1 target2 directory */
1411556Srgrimes	sourcedir = argv[argc - 1];
14276039Ssobomax	if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
14376039Ssobomax		/*
14476039Ssobomax		 * We were asked not to follow symlinks, but found one at
14576039Ssobomax		 * the target--simulate "not a directory" error
14676039Ssobomax		 */
14776039Ssobomax		errno = ENOTDIR;
14876039Ssobomax		err(1, "%s", sourcedir);
14976039Ssobomax	}
1501556Srgrimes	if (stat(sourcedir, &sb))
1511556Srgrimes		err(1, "%s", sourcedir);
1521556Srgrimes	if (!S_ISDIR(sb.st_mode))
1531556Srgrimes		usage();
1541556Srgrimes	for (exitval = 0; *argv != sourcedir; ++argv)
1551556Srgrimes		exitval |= linkit(*argv, sourcedir, 1);
1561556Srgrimes	exit(exitval);
1571556Srgrimes}
1581556Srgrimes
1591556Srgrimesint
16090110Simplinkit(const char *target, const char *source, int isdir)
1611556Srgrimes{
1621556Srgrimes	struct stat sb;
16376877Skris	const char *p;
16464787Ssheldonh	int ch, exists, first;
16577404Simp	char path[PATH_MAX];
1661556Srgrimes
1671556Srgrimes	if (!sflag) {
1681556Srgrimes		/* If target doesn't exist, quit now. */
1691556Srgrimes		if (stat(target, &sb)) {
1701556Srgrimes			warn("%s", target);
1711556Srgrimes			return (1);
1721556Srgrimes		}
17315900Speter		/* Only symbolic links to directories. */
17415900Speter		if (S_ISDIR(sb.st_mode)) {
17515900Speter			errno = EISDIR;
17615900Speter			warn("%s", target);
1771556Srgrimes			return (1);
1781556Srgrimes		}
1791556Srgrimes	}
1801556Srgrimes
18176039Ssobomax	/*
18276039Ssobomax	 * If the source is a directory (and not a symlink if hflag),
18376039Ssobomax	 * append the target's name.
18476039Ssobomax	 */
18576039Ssobomax	if (isdir ||
18676039Ssobomax	    (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
18776039Ssobomax	    (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) {
1881556Srgrimes		if ((p = strrchr(target, '/')) == NULL)
1891556Srgrimes			p = target;
1901556Srgrimes		else
1911556Srgrimes			++p;
1921556Srgrimes		(void)snprintf(path, sizeof(path), "%s/%s", source, p);
1931556Srgrimes		source = path;
19476039Ssobomax	}
1951556Srgrimes
19676039Ssobomax	exists = !lstat(source, &sb);
1971556Srgrimes	/*
19864787Ssheldonh	 * If the file exists, then unlink it forcibly if -f was specified
19964787Ssheldonh	 * and interactively if -i was specified.
2001556Srgrimes	 */
20164787Ssheldonh	if (fflag && exists) {
20264787Ssheldonh		if (unlink(source)) {
20364787Ssheldonh			warn("%s", source);
20464787Ssheldonh			return (1);
20564787Ssheldonh		}
20664638Ssheldonh	} else if (iflag && exists) {
20764787Ssheldonh		fflush(stdout);
20864638Ssheldonh		fprintf(stderr, "replace %s? ", source);
20964638Ssheldonh
21064638Ssheldonh		first = ch = getchar();
21164638Ssheldonh		while(ch != '\n' && ch != EOF)
21264638Ssheldonh			ch = getchar();
21364787Ssheldonh		if (first != 'y' && first != 'Y') {
21464787Ssheldonh			fprintf(stderr, "not replaced\n");
21564787Ssheldonh			return (1);
21664787Ssheldonh		}
21764638Ssheldonh
21864787Ssheldonh		if (unlink(source)) {
21964638Ssheldonh			warn("%s", source);
22064638Ssheldonh			return (1);
22164638Ssheldonh		}
2221556Srgrimes	}
22364787Ssheldonh
22464787Ssheldonh	/* Attempt the link. */
22564638Ssheldonh	if ((*linkf)(target, source)) {
22664638Ssheldonh		warn("%s", source);
22764638Ssheldonh		return (1);
22864638Ssheldonh	}
22951148Sobrien	if (vflag)
23051148Sobrien		(void)printf("%s %c> %s\n", source, linkch, target);
2311556Srgrimes	return (0);
2321556Srgrimes}
2331556Srgrimes
2341556Srgrimesvoid
23590110Simpusage(void)
2361556Srgrimes{
23754895Ssheldonh	(void)fprintf(stderr, "%s\n%s\n%s\n",
23876039Ssobomax	    "usage: ln [-fhinsv] file1 file2",
23976039Ssobomax	    "       ln [-fhinsv] file ... directory",
24054895Ssheldonh	    "       link file1 file2");
2411556Srgrimes	exit(1);
2421556Srgrimes}
243