ln.c revision 76039
1251881Speter/*
2251881Speter * Copyright (c) 1987, 1993, 1994
3251881Speter *	The Regents of the University of California.  All rights reserved.
4251881Speter *
5251881Speter * Redistribution and use in source and binary forms, with or without
6251881Speter * modification, are permitted provided that the following conditions
7251881Speter * are met:
8251881Speter * 1. Redistributions of source code must retain the above copyright
9251881Speter *    notice, this list of conditions and the following disclaimer.
10251881Speter * 2. Redistributions in binary form must reproduce the above copyright
11251881Speter *    notice, this list of conditions and the following disclaimer in the
12251881Speter *    documentation and/or other materials provided with the distribution.
13251881Speter * 3. All advertising materials mentioning features or use of this software
14251881Speter *    must display the following acknowledgement:
15251881Speter *	This product includes software developed by the University of
16251881Speter *	California, Berkeley and its contributors.
17251881Speter * 4. Neither the name of the University nor the names of its contributors
18251881Speter *    may be used to endorse or promote products derived from this software
19251881Speter *    without specific prior written permission.
20251881Speter *
21251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24251881Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31251881Speter * SUCH DAMAGE.
32251881Speter */
33251881Speter
34251881Speter#ifndef lint
35251881Speterstatic char const copyright[] =
36251881Speter"@(#) Copyright (c) 1987, 1993, 1994\n\
37251881Speter	The Regents of the University of California.  All rights reserved.\n";
38251881Speter#endif /* not lint */
39251881Speter
40251881Speter#ifndef lint
41251881Speter#if 0
42251881Speterstatic char sccsid[] = "@(#)ln.c	8.2 (Berkeley) 3/31/94";
43251881Speter#endif
44251881Speterstatic const char rcsid[] =
45251881Speter  "$FreeBSD: head/bin/ln/ln.c 76039 2001-04-26 17:15:57Z sobomax $";
46251881Speter#endif /* not lint */
47251881Speter
48251881Speter#include <sys/param.h>
49251881Speter#include <sys/stat.h>
50251881Speter
51251881Speter#include <err.h>
52251881Speter#include <errno.h>
53251881Speter#include <stdio.h>
54251881Speter#include <stdlib.h>
55251881Speter#include <string.h>
56251881Speter#include <unistd.h>
57251881Speter
58251881Speterint	fflag;				/* Unlink existing files. */
59251881Speterint	hflag;				/* Check new name for symlink first. */
60251881Speterint	iflag;				/* Interactive mode. */
61251881Speterint	sflag;				/* Symbolic, not hard, link. */
62251881Speterint	vflag;				/* Verbose output. */
63251881Speter					/* System link call. */
64251881Speterint (*linkf) __P((const char *, const char *));
65251881Speterchar	linkch;
66251881Speter
67251881Speterint	linkit __P((char *, char *, int));
68251881Speterint	main __P((int, char *[]));
69251881Spetervoid	usage __P((void));
70251881Speter
71251881Speterint
72251881Spetermain(argc, argv)
73251881Speter	int argc;
74251881Speter	char *argv[];
75251881Speter{
76251881Speter	struct stat sb;
77251881Speter	int ch, exitval;
78251881Speter	char *p, *sourcedir;
79251881Speter
80251881Speter	/*
81251881Speter	 * Test for the special case where the utility is called as
82251881Speter	 * "link", for which the functionality provided is greatly
83251881Speter	 * simplified.
84251881Speter	 */
85251881Speter	if ((p = rindex(argv[0], '/')) == NULL)
86251881Speter		p = argv[0];
87251881Speter	else
88251881Speter		++p;
89251881Speter	if (strcmp(p, "link") == 0) {
90251881Speter		if (argc == 3) {
91251881Speter			linkf = link;
92251881Speter			exit(linkit(argv[1], argv[2], 0));
93251881Speter		} else
94251881Speter			usage();
95251881Speter	}
96251881Speter
97251881Speter	while ((ch = getopt(argc, argv, "fhinsv")) != -1)
98251881Speter		switch (ch) {
99251881Speter		case 'f':
100251881Speter			fflag = 1;
101251881Speter			iflag = 0;
102251881Speter			break;
103251881Speter		case 'h':
104251881Speter		case 'n':
105251881Speter			hflag = 1;
106251881Speter			break;
107251881Speter		case 'i':
108251881Speter			iflag = 1;
109251881Speter			fflag = 0;
110251881Speter			break;
111251881Speter		case 's':
112251881Speter			sflag = 1;
113251881Speter			break;
114251881Speter		case 'v':
115251881Speter			vflag = 1;
116251881Speter			break;
117251881Speter		case '?':
118251881Speter		default:
119251881Speter			usage();
120251881Speter		}
121251881Speter
122251881Speter	argv += optind;
123251881Speter	argc -= optind;
124251881Speter
125251881Speter	linkf = sflag ? symlink : link;
126251881Speter	linkch = sflag ? '-' : '=';
127251881Speter
128251881Speter	switch(argc) {
129251881Speter	case 0:
130251881Speter		usage();
131251881Speter		/* NOTREACHED */
132251881Speter	case 1:				/* ln target */
133251881Speter		exit(linkit(argv[0], ".", 1));
134251881Speter	case 2:				/* ln target source */
135251881Speter		exit(linkit(argv[0], argv[1], 0));
136251881Speter	}
137251881Speter					/* ln target1 target2 directory */
138251881Speter	sourcedir = argv[argc - 1];
139251881Speter	if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
140251881Speter		/*
141251881Speter		 * We were asked not to follow symlinks, but found one at
142251881Speter		 * the target--simulate "not a directory" error
143251881Speter		 */
144251881Speter		errno = ENOTDIR;
145251881Speter		err(1, "%s", sourcedir);
146251881Speter	}
147251881Speter	if (stat(sourcedir, &sb))
148251881Speter		err(1, "%s", sourcedir);
149251881Speter	if (!S_ISDIR(sb.st_mode))
150251881Speter		usage();
151251881Speter	for (exitval = 0; *argv != sourcedir; ++argv)
152251881Speter		exitval |= linkit(*argv, sourcedir, 1);
153251881Speter	exit(exitval);
154251881Speter}
155251881Speter
156251881Speterint
157251881Speterlinkit(target, source, isdir)
158251881Speter	char *target, *source;
159251881Speter	int isdir;
160251881Speter{
161251881Speter	struct stat sb;
162251881Speter	int ch, exists, first;
163251881Speter	char *p, path[MAXPATHLEN];
164251881Speter
165251881Speter	if (!sflag) {
166251881Speter		/* If target doesn't exist, quit now. */
167251881Speter		if (stat(target, &sb)) {
168251881Speter			warn("%s", target);
169251881Speter			return (1);
170251881Speter		}
171251881Speter		/* Only symbolic links to directories. */
172251881Speter		if (S_ISDIR(sb.st_mode)) {
173251881Speter			errno = EISDIR;
174251881Speter			warn("%s", target);
175251881Speter			return (1);
176251881Speter		}
177251881Speter	}
178251881Speter
179251881Speter	/*
180251881Speter	 * If the source is a directory (and not a symlink if hflag),
181251881Speter	 * append the target's name.
182251881Speter	 */
183251881Speter	if (isdir ||
184251881Speter	    (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
185251881Speter	    (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) {
186251881Speter		if ((p = strrchr(target, '/')) == NULL)
187251881Speter			p = target;
188251881Speter		else
189251881Speter			++p;
190251881Speter		(void)snprintf(path, sizeof(path), "%s/%s", source, p);
191251881Speter		source = path;
192251881Speter	}
193251881Speter
194251881Speter	exists = !lstat(source, &sb);
195251881Speter	/*
196251881Speter	 * If the file exists, then unlink it forcibly if -f was specified
197251881Speter	 * and interactively if -i was specified.
198251881Speter	 */
199251881Speter	if (fflag && exists) {
200251881Speter		if (unlink(source)) {
201251881Speter			warn("%s", source);
202251881Speter			return (1);
203251881Speter		}
204251881Speter	} else if (iflag && exists) {
205251881Speter		fflush(stdout);
206251881Speter		fprintf(stderr, "replace %s? ", source);
207251881Speter
208251881Speter		first = ch = getchar();
209251881Speter		while(ch != '\n' && ch != EOF)
210251881Speter			ch = getchar();
211251881Speter		if (first != 'y' && first != 'Y') {
212251881Speter			fprintf(stderr, "not replaced\n");
213251881Speter			return (1);
214251881Speter		}
215251881Speter
216251881Speter		if (unlink(source)) {
217251881Speter			warn("%s", source);
218251881Speter			return (1);
219251881Speter		}
220251881Speter	}
221251881Speter
222251881Speter	/* Attempt the link. */
223251881Speter	if ((*linkf)(target, source)) {
224251881Speter		warn("%s", source);
225251881Speter		return (1);
226251881Speter	}
227251881Speter	if (vflag)
228251881Speter		(void)printf("%s %c> %s\n", source, linkch, target);
229251881Speter	return (0);
230251881Speter}
231251881Speter
232251881Spetervoid
233251881Speterusage()
234251881Speter{
235251881Speter	(void)fprintf(stderr, "%s\n%s\n%s\n",
236251881Speter	    "usage: ln [-fhinsv] file1 file2",
237251881Speter	    "       ln [-fhinsv] file ... directory",
238251881Speter	    "       link file1 file2");
239251881Speter	exit(1);
240251881Speter}
241251881Speter