ln.c revision 1.22 1 /* $NetBSD: ln.c,v 1.22 2003/08/04 22:31:24 jschauma Exp $ */
2
3 /*
4 * Copyright (c) 1987, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94";
45 #else
46 __RCSID("$NetBSD: ln.c,v 1.22 2003/08/04 22:31:24 jschauma Exp $");
47 #endif
48 #endif /* not lint */
49
50 #include <sys/param.h>
51 #include <sys/stat.h>
52
53 #include <err.h>
54 #include <errno.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <vis.h>
60
61 int fflag; /* Unlink existing files. */
62 int hflag; /* Check new name for symlink first. */
63 int sflag; /* Symbolic, not hard, link. */
64 int vflag; /* Verbose output */
65 int stdout_ok; /* stdout connected to a terminal */
66
67 /* System link call. */
68 int (*linkf)(const char *, const char *);
69 char linkch;
70
71 int linkit(char *, char *, int);
72 void usage(void);
73 int main(int, char *[]);
74 char *printescaped(const char *);
75
76 int
77 main(int argc, char *argv[])
78 {
79 struct stat sb;
80 int ch, exitval;
81 char *sourcedir;
82
83 setprogname(argv[0]);
84 while ((ch = getopt(argc, argv, "fhnsv")) != -1)
85 switch (ch) {
86 case 'f':
87 fflag = 1;
88 break;
89 case 'h':
90 case 'n':
91 hflag = 1;
92 break;
93 case 's':
94 sflag = 1;
95 break;
96 case 'v':
97 vflag = 1;
98 break;
99 case '?':
100 default:
101 usage();
102 /* NOTREACHED */
103 }
104
105 argv += optind;
106 argc -= optind;
107
108 if (sflag) {
109 linkf = symlink;
110 linkch = '-';
111 } else {
112 linkf = link;
113 linkch = '=';
114 }
115
116 switch(argc) {
117 case 0:
118 usage();
119 /* NOTREACHED */
120 case 1: /* ln target */
121 exit(linkit(argv[0], ".", 1));
122 /* NOTREACHED */
123 case 2: /* ln target source */
124 exit(linkit(argv[0], argv[1], 0));
125 /* NOTREACHED */
126 }
127
128 stdout_ok = isatty(STDOUT_FILENO);
129
130 /* ln target1 target2 directory */
131 sourcedir = argv[argc - 1];
132 if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
133 /* we were asked not to follow symlinks, but found one at
134 the target--simulate "not a directory" error */
135 errno = ENOTDIR;
136 err(EXIT_FAILURE, "%s", printescaped(sourcedir));
137 /* NOTREACHED */
138 }
139 if (stat(sourcedir, &sb)) {
140 err(EXIT_FAILURE, "%s", printescaped(sourcedir));
141 /* NOTREACHED */
142 }
143 if (!S_ISDIR(sb.st_mode)) {
144 usage();
145 /* NOTREACHED */
146 }
147 for (exitval = 0; *argv != sourcedir; ++argv)
148 exitval |= linkit(*argv, sourcedir, 1);
149 exit(exitval);
150 /* NOTREACHED */
151 }
152
153 int
154 linkit(char *target, char *source, int isdir)
155 {
156 struct stat sb;
157 char *p, path[MAXPATHLEN];
158 char *sn, *tn;
159
160 sn = printescaped(source);
161 tn = printescaped(target);
162
163 if (!sflag) {
164 /* If target doesn't exist, quit now. */
165 if (stat(target, &sb)) {
166 warn("%s", tn);
167 free(sn);
168 free(tn);
169 return (1);
170 }
171 }
172
173 /* If the source is a directory (and not a symlink if hflag),
174 append the target's name. */
175 if (isdir ||
176 (!lstat(source, &sb) && S_ISDIR(sb.st_mode)) ||
177 (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) {
178 if ((p = strrchr(target, '/')) == NULL)
179 p = target;
180 else
181 ++p;
182 p = printescaped(p);
183 (void)snprintf(path, sizeof(path), "%s/%s", sn, p);
184 free(p);
185 source = path;
186 }
187
188 /*
189 * If the file exists, and -f was specified, unlink it.
190 * Attempt the link.
191 */
192 if ((fflag && unlink(source) < 0 && errno != ENOENT) ||
193 (*linkf)(target, source)) {
194 warn("%s", source);
195 return (1);
196 }
197 if (vflag)
198 (void)printf("%s %c> %s\n", sn, linkch, tn);
199
200 free(sn);
201 free(tn);
202 return (0);
203 }
204
205 void
206 usage(void)
207 {
208
209 (void)fprintf(stderr,
210 "Usage:\t%s [-fhns] file1 file2\n\t%s [-fhnsv] file ... directory\n",
211 getprogname(), getprogname());
212 exit(1);
213 /* NOTREACHED */
214 }
215
216 char *
217 printescaped(const char *src)
218 {
219 size_t len;
220 char *retval;
221
222 len = strlen(src);
223 if (len != 0 && SIZE_T_MAX/len <= 4) {
224 errx(EXIT_FAILURE, "%s: name too long", src);
225 /* NOTREACHED */
226 }
227
228 retval = (char *)malloc(4*len+1);
229 if (retval != NULL) {
230 if (stdout_ok)
231 (void)strvis(retval, src, VIS_NL | VIS_CSTYLE);
232 else
233 (void)strcpy(retval, src);
234 return retval;
235 } else
236 errx(EXIT_FAILURE, "out of memory!");
237 /* NOTREACHED */
238 }
239