ln.c revision 76877
1139749Simp/*
2117632Sharti * Copyright (c) 1987, 1993, 1994
3117632Sharti *	The Regents of the University of California.  All rights reserved.
4117632Sharti *
5117632Sharti * Redistribution and use in source and binary forms, with or without
6117632Sharti * modification, are permitted provided that the following conditions
7117632Sharti * are met:
8117632Sharti * 1. Redistributions of source code must retain the above copyright
9117632Sharti *    notice, this list of conditions and the following disclaimer.
10117632Sharti * 2. Redistributions in binary form must reproduce the above copyright
11117632Sharti *    notice, this list of conditions and the following disclaimer in the
12117632Sharti *    documentation and/or other materials provided with the distribution.
13117632Sharti * 3. All advertising materials mentioning features or use of this software
14117632Sharti *    must display the following acknowledgement:
15117632Sharti *	This product includes software developed by the University of
16117632Sharti *	California, Berkeley and its contributors.
17117632Sharti * 4. Neither the name of the University nor the names of its contributors
18117632Sharti *    may be used to endorse or promote products derived from this software
19117632Sharti *    without specific prior written permission.
20117632Sharti *
21117632Sharti * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22117632Sharti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23117632Sharti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24117632Sharti * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25117632Sharti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26117632Sharti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27117632Sharti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28117632Sharti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29117632Sharti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30117632Sharti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31117632Sharti * SUCH DAMAGE.
32117632Sharti */
33117632Sharti
34117632Sharti#ifndef lint
35117632Shartistatic char const copyright[] =
36117632Sharti"@(#) Copyright (c) 1987, 1993, 1994\n\
37117632Sharti	The Regents of the University of California.  All rights reserved.\n";
38117632Sharti#endif /* not lint */
39119418Sobrien
40117632Sharti#ifndef lint
41117632Sharti#if 0
42117632Shartistatic char sccsid[] = "@(#)ln.c	8.2 (Berkeley) 3/31/94";
43117632Sharti#endif
44117632Shartistatic const char rcsid[] =
45117632Sharti  "$FreeBSD: head/bin/ln/ln.c 76877 2001-05-20 04:54:15Z kris $";
46117632Sharti#endif /* not lint */
47117632Sharti
48117632Sharti#include <sys/param.h>
49117632Sharti#include <sys/stat.h>
50117632Sharti
51117632Sharti#include <err.h>
52117632Sharti#include <errno.h>
53117632Sharti#include <stdio.h>
54117632Sharti#include <stdlib.h>
55117632Sharti#include <string.h>
56117632Sharti#include <unistd.h>
57117632Sharti
58117632Shartiint	fflag;				/* Unlink existing files. */
59117632Shartiint	hflag;				/* Check new name for symlink first. */
60117632Shartiint	iflag;				/* Interactive mode. */
61117632Shartiint	sflag;				/* Symbolic, not hard, link. */
62117632Shartiint	vflag;				/* Verbose output. */
63117632Sharti					/* System link call. */
64117632Shartiint (*linkf) __P((const char *, const char *));
65117632Shartichar	linkch;
66117632Sharti
67117632Shartiint	linkit __P((const char *, const char *, int));
68117632Shartiint	main __P((int, char *[]));
69117632Shartivoid	usage __P((void));
70117632Sharti
71117632Shartiint
72117632Shartimain(argc, argv)
73117632Sharti	int argc;
74117632Sharti	char *argv[];
75117632Sharti{
76117632Sharti	struct stat sb;
77117632Sharti	char *p, *sourcedir;
78117632Sharti	int ch, exitval;
79117632Sharti
80117632Sharti	/*
81117632Sharti	 * Test for the special case where the utility is called as
82117632Sharti	 * "link", for which the functionality provided is greatly
83117632Sharti	 * simplified.
84117632Sharti	 */
85117632Sharti	if ((p = rindex(argv[0], '/')) == NULL)
86117632Sharti		p = argv[0];
87117632Sharti	else
88117632Sharti		++p;
89117632Sharti	if (strcmp(p, "link") == 0) {
90117632Sharti		if (argc == 3) {
91117632Sharti			linkf = link;
92117632Sharti			exit(linkit(argv[1], argv[2], 0));
93117632Sharti		} else
94117632Sharti			usage();
95117632Sharti	}
96117632Sharti
97117632Sharti	while ((ch = getopt(argc, argv, "fhinsv")) != -1)
98117632Sharti		switch (ch) {
99117632Sharti		case 'f':
100117632Sharti			fflag = 1;
101117632Sharti			iflag = 0;
102117632Sharti			break;
103117632Sharti		case 'h':
104117632Sharti		case 'n':
105117632Sharti			hflag = 1;
106117632Sharti			break;
107117632Sharti		case 'i':
108117632Sharti			iflag = 1;
109117632Sharti			fflag = 0;
110117632Sharti			break;
111117632Sharti		case 's':
112117632Sharti			sflag = 1;
113117632Sharti			break;
114117632Sharti		case 'v':
115117632Sharti			vflag = 1;
116117632Sharti			break;
117117632Sharti		case '?':
118117632Sharti		default:
119117632Sharti			usage();
120117632Sharti		}
121117632Sharti
122147256Sbrooks	argv += optind;
123117632Sharti	argc -= optind;
124117632Sharti
125117632Sharti	linkf = sflag ? symlink : link;
126117632Sharti	linkch = sflag ? '-' : '=';
127117632Sharti
128147256Sbrooks	switch(argc) {
129117632Sharti	case 0:
130117632Sharti		usage();
131117632Sharti		/* NOTREACHED */
132117632Sharti	case 1:				/* ln target */
133117632Sharti		exit(linkit(argv[0], ".", 1));
134117632Sharti	case 2:				/* ln target source */
135117632Sharti		exit(linkit(argv[0], argv[1], 0));
136117632Sharti	}
137117632Sharti					/* ln target1 target2 directory */
138117632Sharti	sourcedir = argv[argc - 1];
139117632Sharti	if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
140117632Sharti		/*
141117632Sharti		 * We were asked not to follow symlinks, but found one at
142117632Sharti		 * the target--simulate "not a directory" error
143117632Sharti		 */
144117632Sharti		errno = ENOTDIR;
145117632Sharti		err(1, "%s", sourcedir);
146117632Sharti	}
147117632Sharti	if (stat(sourcedir, &sb))
148117632Sharti		err(1, "%s", sourcedir);
149117632Sharti	if (!S_ISDIR(sb.st_mode))
150117632Sharti		usage();
151117632Sharti	for (exitval = 0; *argv != sourcedir; ++argv)
152147256Sbrooks		exitval |= linkit(*argv, sourcedir, 1);
153117632Sharti	exit(exitval);
154117632Sharti}
155117632Sharti
156117632Shartiint
157117632Shartilinkit(target, source, isdir)
158117632Sharti	const char *target, *source;
159117632Sharti	int isdir;
160117632Sharti{
161117632Sharti	struct stat sb;
162117632Sharti	const char *p;
163117632Sharti	char path[MAXPATHLEN];
164147256Sbrooks	int ch, exists, first;
165117632Sharti
166117632Sharti	if (!sflag) {
167117632Sharti		/* If target doesn't exist, quit now. */
168117632Sharti		if (stat(target, &sb)) {
169117632Sharti			warn("%s", target);
170117632Sharti			return (1);
171117632Sharti		}
172117632Sharti		/* Only symbolic links to directories. */
173117632Sharti		if (S_ISDIR(sb.st_mode)) {
174117632Sharti			errno = EISDIR;
175117632Sharti			warn("%s", target);
176117632Sharti			return (1);
177117632Sharti		}
178117632Sharti	}
179117632Sharti
180117632Sharti	/*
181117632Sharti	 * If the source is a directory (and not a symlink if hflag),
182117632Sharti	 * append the target's name.
183117632Sharti	 */
184117632Sharti	if (isdir ||
185117632Sharti	    (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
186117632Sharti	    (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) {
187117632Sharti		if ((p = strrchr(target, '/')) == NULL)
188117632Sharti			p = target;
189117632Sharti		else
190117632Sharti			++p;
191117632Sharti		(void)snprintf(path, sizeof(path), "%s/%s", source, p);
192117632Sharti		source = path;
193117632Sharti	}
194117632Sharti
195117632Sharti	exists = !lstat(source, &sb);
196117632Sharti	/*
197117632Sharti	 * If the file exists, then unlink it forcibly if -f was specified
198117632Sharti	 * and interactively if -i was specified.
199117632Sharti	 */
200117632Sharti	if (fflag && exists) {
201117632Sharti		if (unlink(source)) {
202117632Sharti			warn("%s", source);
203117632Sharti			return (1);
204117632Sharti		}
205117632Sharti	} else if (iflag && exists) {
206117632Sharti		fflush(stdout);
207117632Sharti		fprintf(stderr, "replace %s? ", source);
208117632Sharti
209117632Sharti		first = ch = getchar();
210117632Sharti		while(ch != '\n' && ch != EOF)
211117632Sharti			ch = getchar();
212117632Sharti		if (first != 'y' && first != 'Y') {
213117632Sharti			fprintf(stderr, "not replaced\n");
214117632Sharti			return (1);
215117632Sharti		}
216117632Sharti
217117632Sharti		if (unlink(source)) {
218117632Sharti			warn("%s", source);
219117632Sharti			return (1);
220117632Sharti		}
221117632Sharti	}
222117632Sharti
223117632Sharti	/* Attempt the link. */
224117632Sharti	if ((*linkf)(target, source)) {
225117632Sharti		warn("%s", source);
226117632Sharti		return (1);
227117632Sharti	}
228117632Sharti	if (vflag)
229117632Sharti		(void)printf("%s %c> %s\n", source, linkch, target);
230117632Sharti	return (0);
231117632Sharti}
232117632Sharti
233117632Shartivoid
234117632Shartiusage()
235117632Sharti{
236117632Sharti	(void)fprintf(stderr, "%s\n%s\n%s\n",
237117632Sharti	    "usage: ln [-fhinsv] file1 file2",
238117632Sharti	    "       ln [-fhinsv] file ... directory",
239117632Sharti	    "       link file1 file2");
240117632Sharti	exit(1);
241117632Sharti}
242117632Sharti