1 /* $OpenBSD: ln.c,v 1.12 2005/04/15 00:51:57 uwe Exp $ */
2 /* $NetBSD: ln.c,v 1.10 1995/03/21 09:06:10 cgd Exp $ */
3
4 /*
5 * Copyright (c) 1987, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 static const char 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 #if 0
41 static const char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94";
42 #else
43 static const char rcsid[] = "$OpenBSD: ln.c,v 1.12 2005/04/15 00:51:57 uwe Exp $";
44 #endif
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49
50 #include <err.h>
51 #include <errno.h>
52 #include <libgen.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57
58 int dirflag; /* Undocumented directory flag. */
59 int fflag; /* Unlink existing files. */
60 int hflag; /* Check new name for symlink first. */
61 int sflag; /* Symbolic, not hard, link. */
62 /* System link call. */
63 int (*linkf)(const char *, const char *);
64
65 int linkit(char *, char *, int);
66 void usage(void);
67
68 int
main(int argc,char * argv[])69 main(int argc, char *argv[])
70 {
71 struct stat sb;
72 int ch, exitval;
73 char *sourcedir;
74
75 while ((ch = getopt(argc, argv, "Ffhns")) != -1)
76 switch (ch) {
77 case 'F':
78 dirflag = 1; /* XXX: deliberately undocumented. */
79 break;
80 case 'f':
81 fflag = 1;
82 break;
83 case 'h':
84 case 'n':
85 hflag = 1;
86 break;
87 case 's':
88 sflag = 1;
89 break;
90 default:
91 usage();
92 }
93
94 argv += optind;
95 argc -= optind;
96
97 linkf = sflag ? symlink : link;
98
99 switch(argc) {
100 case 0:
101 usage();
102 case 1: /* ln target */
103 exit(linkit(argv[0], ".", 1));
104 case 2: /* ln target source */
105 exit(linkit(argv[0], argv[1], 0));
106 }
107 /* ln target1 target2 directory */
108 sourcedir = argv[argc - 1];
109 if (stat(sourcedir, &sb))
110 err(1, "%s", sourcedir);
111 if (!S_ISDIR(sb.st_mode))
112 usage();
113 for (exitval = 0; *argv != sourcedir; ++argv)
114 exitval |= linkit(*argv, sourcedir, 1);
115 exit(exitval);
116 }
117
118 int
linkit(char * target,char * source,int isdir)119 linkit(char *target, char *source, int isdir)
120 {
121 struct stat sb;
122 char *p, path[MAXPATHLEN];
123 int (*statf)(const char *, struct stat *);
124 int n;
125
126 if (!sflag) {
127 /* If target doesn't exist, quit now. */
128 if (stat(target, &sb)) {
129 warn("%s", target);
130 return (1);
131 }
132 /* Only symbolic links to directories, unless -F option used. */
133 if (!dirflag && S_ISDIR(sb.st_mode)) {
134 errno = EISDIR;
135 warn("%s", target);
136 return (1);
137 }
138 }
139
140 statf = hflag ? lstat : stat;
141
142 /* If the source is a directory, append the target's name. */
143 if (isdir || (!statf(source, &sb) && S_ISDIR(sb.st_mode))) {
144 if ((p = basename(target)) == NULL) {
145 warn("%s", target);
146 return (1);
147 }
148 n = snprintf(path, sizeof(path), "%s/%s", source, p);
149 if (n < 0 || n >= sizeof(path)) {
150 errno = ENAMETOOLONG;
151 warn("%s/%s", source, p);
152 return (1);
153 }
154 source = path;
155 }
156
157 /*
158 * If the file exists, and -f was specified, unlink it.
159 * Attempt the link.
160 */
161 if ((fflag && unlink(source) < 0 && errno != ENOENT) ||
162 (*linkf)(target, source)) {
163 warn("%s", source);
164 return (1);
165 }
166
167 return (0);
168 }
169
170 void
usage(void)171 usage(void)
172 {
173 extern char *__progname;
174
175 (void)fprintf(stderr,
176 "usage: %s [-fhns] file1 file2\n"
177 " %s [-fs] file ... directory\n",
178 __progname, __progname);
179 exit(1);
180 }
181