1/*- 2 * Copyright (c) 1987, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30#if 0 31#ifndef lint 32static char const copyright[] = 33"@(#) Copyright (c) 1987, 1993, 1994\n\ 34 The Regents of the University of California. All rights reserved.\n"; 35#endif /* not lint */ 36 37#ifndef lint 38static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; 39#endif /* not lint */ 40#endif 41#include <sys/cdefs.h> 42__FBSDID("$FreeBSD: src/bin/ln/ln.c,v 1.34 2006/02/14 11:08:05 glebius Exp $"); 43 44#include <sys/param.h> 45#include <sys/stat.h> 46 47#include <err.h> 48#include <errno.h> 49#include <limits.h> 50#include <stdio.h> 51#include <stdlib.h> 52#include <string.h> 53#include <unistd.h> 54 55int fflag; /* Unlink existing files. */ 56int Fflag; /* Remove empty directories also. */ 57int hflag; /* Check new name for symlink first. */ 58int iflag; /* Interactive mode. */ 59int sflag; /* Symbolic, not hard, link. */ 60int vflag; /* Verbose output. */ 61 /* System link call. */ 62int (*linkf)(const char *, const char *); 63char linkch; 64 65int linkit(const char *, const char *, int); 66void usage(void); 67 68int 69main(int argc, char *argv[]) 70{ 71 struct stat sb; 72 char *p, *sourcedir; 73 int ch, exitval; 74 75 if (argc < 1) 76 usage(); 77 /* 78 * Test for the special case where the utility is called as 79 * "link", for which the functionality provided is greatly 80 * simplified. 81 */ 82 if ((p = rindex(argv[0], '/')) == NULL) 83 p = argv[0]; 84 else 85 ++p; 86 if (strcmp(p, "link") == 0) { 87 while (getopt(argc, argv, "") != -1) 88 usage(); 89 argc -= optind; 90 argv += optind; 91 if (argc != 2) 92 usage(); 93 linkf = link; 94 exit(linkit(argv[0], argv[1], 0)); 95 } 96 97 while ((ch = getopt(argc, argv, "Ffhinsv")) != -1) 98 switch (ch) { 99 case 'F': 100 Fflag = 1; 101 break; 102 case 'f': 103 fflag = 1; 104 iflag = 0; 105 break; 106 case 'h': 107 case 'n': 108 hflag = 1; 109 break; 110 case 'i': 111 iflag = 1; 112 fflag = 0; 113 break; 114 case 's': 115 sflag = 1; 116 break; 117 case 'v': 118 vflag = 1; 119 break; 120 case '?': 121 default: 122 usage(); 123 } 124 125 argv += optind; 126 argc -= optind; 127 128 linkf = sflag ? symlink : link; 129 linkch = sflag ? '-' : '='; 130 if (sflag == 0) 131 Fflag = 0; 132 if (Fflag == 1 && iflag == 0) 133 fflag = 1; 134 135 switch(argc) { 136 case 0: 137 usage(); 138 /* NOTREACHED */ 139 case 1: /* ln target */ 140 exit(linkit(argv[0], ".", 1)); 141 case 2: /* ln target source */ 142 exit(linkit(argv[0], argv[1], 0)); 143 default: 144 ; 145 } 146 /* ln target1 target2 directory */ 147 sourcedir = argv[argc - 1]; 148 if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { 149 /* 150 * We were asked not to follow symlinks, but found one at 151 * the target--simulate "not a directory" error 152 */ 153 errno = ENOTDIR; 154 err(1, "%s", sourcedir); 155 } 156 if (stat(sourcedir, &sb)) 157 err(1, "%s", sourcedir); 158 if (!S_ISDIR(sb.st_mode)) 159 usage(); 160 for (exitval = 0; *argv != sourcedir; ++argv) 161 exitval |= linkit(*argv, sourcedir, 1); 162 exit(exitval); 163} 164 165int 166linkit(const char *target, const char *source, int isdir) 167{ 168 struct stat sb; 169 const char *p; 170 int ch, exists, first; 171 char path[PATH_MAX]; 172 173 if (!sflag) { 174 /* If target doesn't exist, quit now. */ 175 if (stat(target, &sb)) { 176 warn("%s", target); 177 return (1); 178 } 179 /* Only symbolic links to directories. */ 180 if (S_ISDIR(sb.st_mode)) { 181 errno = EISDIR; 182 warn("%s", target); 183 return (1); 184 } 185 } 186 187 /* 188 * If the source is a directory (and not a symlink if hflag), 189 * append the target's name. 190 */ 191 if (isdir || 192 (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) || 193 (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) { 194 if ((p = strrchr(target, '/')) == NULL) 195 p = target; 196 else 197 ++p; 198 if (snprintf(path, sizeof(path), "%s/%s", source, p) >= 199 (ssize_t)sizeof(path)) { 200 errno = ENAMETOOLONG; 201 warn("%s", target); 202 return (1); 203 } 204 source = path; 205 } 206 207 exists = !lstat(source, &sb); 208 /* 209 * If the file exists, then unlink it forcibly if -f was specified 210 * and interactively if -i was specified. 211 */ 212 if (fflag && exists) { 213 if (Fflag && S_ISDIR(sb.st_mode)) { 214 if (rmdir(source)) { 215 warn("%s", source); 216 return (1); 217 } 218 } else if (unlink(source)) { 219 warn("%s", source); 220 return (1); 221 } 222 } else if (iflag && exists) { 223 fflush(stdout); 224 fprintf(stderr, "replace %s? ", source); 225 226 first = ch = getchar(); 227 while(ch != '\n' && ch != EOF) 228 ch = getchar(); 229 if (first != 'y' && first != 'Y') { 230 fprintf(stderr, "not replaced\n"); 231 return (1); 232 } 233 234 if (Fflag && S_ISDIR(sb.st_mode)) { 235 if (rmdir(source)) { 236 warn("%s", source); 237 return (1); 238 } 239 } else if (unlink(source)) { 240 warn("%s", source); 241 return (1); 242 } 243 } 244 245 /* Attempt the link. */ 246 if ((*linkf)(target, source)) { 247 warn("%s", source); 248 return (1); 249 } 250 if (vflag) 251 (void)printf("%s %c> %s\n", source, linkch, target); 252 return (0); 253} 254 255void 256usage(void) 257{ 258 (void)fprintf(stderr, "%s\n%s\n%s\n", 259 "usage: ln [-Ffhinsv] source_file [target_file]", 260 " ln [-Ffhinsv] source_file ... target_dir", 261 " link source_file target_file"); 262 exit(1); 263} 264