1 /* $NetBSD: ln.c,v 1.34 2008/07/20 00:52:40 lukem 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; 41 #else 42 __RCSID("$NetBSD: ln.c,v 1.34 2008/07/20 00:52:40 lukem Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 49 #include <err.h> 50 #include <errno.h> 51 #include <locale.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 57 int fflag; /* Unlink existing files. */ 58 int hflag; /* Check new name for symlink first. */ 59 int iflag; /* Interactive mode. */ 60 int sflag; /* Symbolic, not hard, link. */ 61 int vflag; /* Verbose output */ 62 63 /* System link call. */ 64 int (*linkf)(const char *, const char *); 65 char linkch; 66 67 int linkit(const char *, const char *, int); 68 void usage(void); 69 int main(int, char *[]); 70 71 int 72 main(int argc, char *argv[]) 73 { 74 struct stat sb; 75 int ch, exitval; 76 char *sourcedir; 77 78 setprogname(argv[0]); 79 (void)setlocale(LC_ALL, ""); 80 81 while ((ch = getopt(argc, argv, "fhinsv")) != -1) 82 switch (ch) { 83 case 'f': 84 fflag = 1; 85 iflag = 0; 86 break; 87 case 'h': 88 case 'n': 89 hflag = 1; 90 break; 91 case 'i': 92 iflag = 1; 93 fflag = 0; 94 break; 95 case 's': 96 sflag = 1; 97 break; 98 case 'v': 99 vflag = 1; 100 break; 101 case '?': 102 default: 103 usage(); 104 /* NOTREACHED */ 105 } 106 107 argv += optind; 108 argc -= optind; 109 110 if (sflag) { 111 linkf = symlink; 112 linkch = '-'; 113 } else { 114 linkf = link; 115 linkch = '='; 116 } 117 118 switch(argc) { 119 case 0: 120 usage(); 121 /* NOTREACHED */ 122 case 1: /* ln target */ 123 exit(linkit(argv[0], ".", 1)); 124 /* NOTREACHED */ 125 case 2: /* ln target source */ 126 exit(linkit(argv[0], argv[1], 0)); 127 /* NOTREACHED */ 128 } 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", sourcedir); 137 /* NOTREACHED */ 138 } 139 if (stat(sourcedir, &sb)) { 140 err(EXIT_FAILURE, "%s", 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(const char *target, const char *source, int isdir) 155 { 156 struct stat sb; 157 const char *p; 158 char path[MAXPATHLEN]; 159 int ch, exists, first; 160 161 if (!sflag) { 162 /* If target doesn't exist, quit now. */ 163 if (stat(target, &sb)) { 164 warn("%s", target); 165 return (1); 166 } 167 } 168 169 /* If the source is a directory (and not a symlink if hflag), 170 append the target's name. */ 171 if (isdir || 172 (!lstat(source, &sb) && S_ISDIR(sb.st_mode)) || 173 (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) { 174 if ((p = strrchr(target, '/')) == NULL) 175 p = target; 176 else 177 ++p; 178 (void)snprintf(path, sizeof(path), "%s/%s", source, p); 179 source = path; 180 } 181 182 exists = !lstat(source, &sb); 183 184 /* 185 * If the file exists, then unlink it forcibly if -f was specified 186 * and interactively if -i was specified. 187 */ 188 if (fflag && exists) { 189 if (unlink(source)) { 190 warn("%s", source); 191 return (1); 192 } 193 } else if (iflag && exists) { 194 fflush(stdout); 195 (void)fprintf(stderr, "replace %s? ", source); 196 197 first = ch = getchar(); 198 while (ch != '\n' && ch != EOF) 199 ch = getchar(); 200 if (first != 'y' && first != 'Y') { 201 (void)fprintf(stderr, "not replaced\n"); 202 return (1); 203 } 204 205 if (unlink(source)) { 206 warn("%s", source); 207 return (1); 208 } 209 } 210 211 /* Attempt the link. */ 212 if ((*linkf)(target, source)) { 213 warn("%s", source); 214 return (1); 215 } 216 if (vflag) 217 (void)printf("%s %c> %s\n", source, linkch, target); 218 219 return (0); 220 } 221 222 void 223 usage(void) 224 { 225 226 (void)fprintf(stderr, 227 "usage:\t%s [-fhinsv] file1 file2\n\t%s [-fhinsv] file ... directory\n", 228 getprogname(), getprogname()); 229 exit(1); 230 /* NOTREACHED */ 231 } 232