ln.c revision 76039
1251881Speter/* 2251881Speter * Copyright (c) 1987, 1993, 1994 3251881Speter * The Regents of the University of California. All rights reserved. 4251881Speter * 5251881Speter * Redistribution and use in source and binary forms, with or without 6251881Speter * modification, are permitted provided that the following conditions 7251881Speter * are met: 8251881Speter * 1. Redistributions of source code must retain the above copyright 9251881Speter * notice, this list of conditions and the following disclaimer. 10251881Speter * 2. Redistributions in binary form must reproduce the above copyright 11251881Speter * notice, this list of conditions and the following disclaimer in the 12251881Speter * documentation and/or other materials provided with the distribution. 13251881Speter * 3. All advertising materials mentioning features or use of this software 14251881Speter * must display the following acknowledgement: 15251881Speter * This product includes software developed by the University of 16251881Speter * California, Berkeley and its contributors. 17251881Speter * 4. Neither the name of the University nor the names of its contributors 18251881Speter * may be used to endorse or promote products derived from this software 19251881Speter * without specific prior written permission. 20251881Speter * 21251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24251881Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31251881Speter * SUCH DAMAGE. 32251881Speter */ 33251881Speter 34251881Speter#ifndef lint 35251881Speterstatic char const copyright[] = 36251881Speter"@(#) Copyright (c) 1987, 1993, 1994\n\ 37251881Speter The Regents of the University of California. All rights reserved.\n"; 38251881Speter#endif /* not lint */ 39251881Speter 40251881Speter#ifndef lint 41251881Speter#if 0 42251881Speterstatic char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; 43251881Speter#endif 44251881Speterstatic const char rcsid[] = 45251881Speter "$FreeBSD: head/bin/ln/ln.c 76039 2001-04-26 17:15:57Z sobomax $"; 46251881Speter#endif /* not lint */ 47251881Speter 48251881Speter#include <sys/param.h> 49251881Speter#include <sys/stat.h> 50251881Speter 51251881Speter#include <err.h> 52251881Speter#include <errno.h> 53251881Speter#include <stdio.h> 54251881Speter#include <stdlib.h> 55251881Speter#include <string.h> 56251881Speter#include <unistd.h> 57251881Speter 58251881Speterint fflag; /* Unlink existing files. */ 59251881Speterint hflag; /* Check new name for symlink first. */ 60251881Speterint iflag; /* Interactive mode. */ 61251881Speterint sflag; /* Symbolic, not hard, link. */ 62251881Speterint vflag; /* Verbose output. */ 63251881Speter /* System link call. */ 64251881Speterint (*linkf) __P((const char *, const char *)); 65251881Speterchar linkch; 66251881Speter 67251881Speterint linkit __P((char *, char *, int)); 68251881Speterint main __P((int, char *[])); 69251881Spetervoid usage __P((void)); 70251881Speter 71251881Speterint 72251881Spetermain(argc, argv) 73251881Speter int argc; 74251881Speter char *argv[]; 75251881Speter{ 76251881Speter struct stat sb; 77251881Speter int ch, exitval; 78251881Speter char *p, *sourcedir; 79251881Speter 80251881Speter /* 81251881Speter * Test for the special case where the utility is called as 82251881Speter * "link", for which the functionality provided is greatly 83251881Speter * simplified. 84251881Speter */ 85251881Speter if ((p = rindex(argv[0], '/')) == NULL) 86251881Speter p = argv[0]; 87251881Speter else 88251881Speter ++p; 89251881Speter if (strcmp(p, "link") == 0) { 90251881Speter if (argc == 3) { 91251881Speter linkf = link; 92251881Speter exit(linkit(argv[1], argv[2], 0)); 93251881Speter } else 94251881Speter usage(); 95251881Speter } 96251881Speter 97251881Speter while ((ch = getopt(argc, argv, "fhinsv")) != -1) 98251881Speter switch (ch) { 99251881Speter case 'f': 100251881Speter fflag = 1; 101251881Speter iflag = 0; 102251881Speter break; 103251881Speter case 'h': 104251881Speter case 'n': 105251881Speter hflag = 1; 106251881Speter break; 107251881Speter case 'i': 108251881Speter iflag = 1; 109251881Speter fflag = 0; 110251881Speter break; 111251881Speter case 's': 112251881Speter sflag = 1; 113251881Speter break; 114251881Speter case 'v': 115251881Speter vflag = 1; 116251881Speter break; 117251881Speter case '?': 118251881Speter default: 119251881Speter usage(); 120251881Speter } 121251881Speter 122251881Speter argv += optind; 123251881Speter argc -= optind; 124251881Speter 125251881Speter linkf = sflag ? symlink : link; 126251881Speter linkch = sflag ? '-' : '='; 127251881Speter 128251881Speter switch(argc) { 129251881Speter case 0: 130251881Speter usage(); 131251881Speter /* NOTREACHED */ 132251881Speter case 1: /* ln target */ 133251881Speter exit(linkit(argv[0], ".", 1)); 134251881Speter case 2: /* ln target source */ 135251881Speter exit(linkit(argv[0], argv[1], 0)); 136251881Speter } 137251881Speter /* ln target1 target2 directory */ 138251881Speter sourcedir = argv[argc - 1]; 139251881Speter if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { 140251881Speter /* 141251881Speter * We were asked not to follow symlinks, but found one at 142251881Speter * the target--simulate "not a directory" error 143251881Speter */ 144251881Speter errno = ENOTDIR; 145251881Speter err(1, "%s", sourcedir); 146251881Speter } 147251881Speter if (stat(sourcedir, &sb)) 148251881Speter err(1, "%s", sourcedir); 149251881Speter if (!S_ISDIR(sb.st_mode)) 150251881Speter usage(); 151251881Speter for (exitval = 0; *argv != sourcedir; ++argv) 152251881Speter exitval |= linkit(*argv, sourcedir, 1); 153251881Speter exit(exitval); 154251881Speter} 155251881Speter 156251881Speterint 157251881Speterlinkit(target, source, isdir) 158251881Speter char *target, *source; 159251881Speter int isdir; 160251881Speter{ 161251881Speter struct stat sb; 162251881Speter int ch, exists, first; 163251881Speter char *p, path[MAXPATHLEN]; 164251881Speter 165251881Speter if (!sflag) { 166251881Speter /* If target doesn't exist, quit now. */ 167251881Speter if (stat(target, &sb)) { 168251881Speter warn("%s", target); 169251881Speter return (1); 170251881Speter } 171251881Speter /* Only symbolic links to directories. */ 172251881Speter if (S_ISDIR(sb.st_mode)) { 173251881Speter errno = EISDIR; 174251881Speter warn("%s", target); 175251881Speter return (1); 176251881Speter } 177251881Speter } 178251881Speter 179251881Speter /* 180251881Speter * If the source is a directory (and not a symlink if hflag), 181251881Speter * append the target's name. 182251881Speter */ 183251881Speter if (isdir || 184251881Speter (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) || 185251881Speter (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) { 186251881Speter if ((p = strrchr(target, '/')) == NULL) 187251881Speter p = target; 188251881Speter else 189251881Speter ++p; 190251881Speter (void)snprintf(path, sizeof(path), "%s/%s", source, p); 191251881Speter source = path; 192251881Speter } 193251881Speter 194251881Speter exists = !lstat(source, &sb); 195251881Speter /* 196251881Speter * If the file exists, then unlink it forcibly if -f was specified 197251881Speter * and interactively if -i was specified. 198251881Speter */ 199251881Speter if (fflag && exists) { 200251881Speter if (unlink(source)) { 201251881Speter warn("%s", source); 202251881Speter return (1); 203251881Speter } 204251881Speter } else if (iflag && exists) { 205251881Speter fflush(stdout); 206251881Speter fprintf(stderr, "replace %s? ", source); 207251881Speter 208251881Speter first = ch = getchar(); 209251881Speter while(ch != '\n' && ch != EOF) 210251881Speter ch = getchar(); 211251881Speter if (first != 'y' && first != 'Y') { 212251881Speter fprintf(stderr, "not replaced\n"); 213251881Speter return (1); 214251881Speter } 215251881Speter 216251881Speter if (unlink(source)) { 217251881Speter warn("%s", source); 218251881Speter return (1); 219251881Speter } 220251881Speter } 221251881Speter 222251881Speter /* Attempt the link. */ 223251881Speter if ((*linkf)(target, source)) { 224251881Speter warn("%s", source); 225251881Speter return (1); 226251881Speter } 227251881Speter if (vflag) 228251881Speter (void)printf("%s %c> %s\n", source, linkch, target); 229251881Speter return (0); 230251881Speter} 231251881Speter 232251881Spetervoid 233251881Speterusage() 234251881Speter{ 235251881Speter (void)fprintf(stderr, "%s\n%s\n%s\n", 236251881Speter "usage: ln [-fhinsv] file1 file2", 237251881Speter " ln [-fhinsv] file ... directory", 238251881Speter " link file1 file2"); 239251881Speter exit(1); 240251881Speter} 241251881Speter