Changeset View
Changeset View
Standalone View
Standalone View
bin/ln/ln.c
Show All 31 Lines | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <libgen.h> | #include <libgen.h> | ||||
#include <limits.h> | #include <limits.h> | ||||
#include <stdbool.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
static int fflag; /* Unlink existing files. */ | static bool fflag; /* Unlink existing files. */ | ||||
static int Fflag; /* Remove empty directories also. */ | static bool Fflag; /* Remove empty directories also. */ | ||||
static int hflag; /* Check new name for symlink first. */ | static bool hflag; /* Check new name for symlink first. */ | ||||
static int iflag; /* Interactive mode. */ | static bool iflag; /* Interactive mode. */ | ||||
static int Pflag; /* Create hard links to symlinks. */ | static bool Pflag; /* Create hard links to symlinks. */ | ||||
static int sflag; /* Symbolic, not hard, link. */ | static bool sflag; /* Symbolic, not hard, link. */ | ||||
static int vflag; /* Verbose output. */ | static bool vflag; /* Verbose output. */ | ||||
static int wflag; /* Warn if symlink target does not | static bool wflag; /* Warn if symlink target does not | ||||
* exist, and -f is not enabled. */ | * exist, and -f is not enabled. */ | ||||
static char linkch; | static char linkch; | ||||
static int linkit(const char *, const char *, int); | static int linkit(const char *, const char *, bool); | ||||
static void usage(void); | static void usage(void) __dead2; | ||||
int | int | ||||
main(int argc, char *argv[]) | main(int argc, char *argv[]) | ||||
{ | { | ||||
struct stat sb; | struct stat sb; | ||||
char *p, *targetdir; | char *p, *targetdir; | ||||
int ch, exitval; | int ch, exitval; | ||||
/* | /* | ||||
* Test for the special case where the utility is called as | * Test for the special case where the utility is called as | ||||
* "link", for which the functionality provided is greatly | * "link", for which the functionality provided is greatly | ||||
* simplified. | * simplified. | ||||
*/ | */ | ||||
if ((p = strrchr(argv[0], '/')) == NULL) | if ((p = strrchr(argv[0], '/')) == NULL) | ||||
p = argv[0]; | p = argv[0]; | ||||
else | else | ||||
++p; | ++p; | ||||
if (strcmp(p, "link") == 0) { | if (strcmp(p, "link") == 0) { | ||||
while (getopt(argc, argv, "") != -1) | while (getopt(argc, argv, "") != -1) | ||||
usage(); | usage(); | ||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if (argc != 2) | if (argc != 2) | ||||
usage(); | usage(); | ||||
exit(linkit(argv[0], argv[1], 0)); | exit(linkit(argv[0], argv[1], false)); | ||||
} | } | ||||
while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1) | while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1) | ||||
switch (ch) { | switch (ch) { | ||||
case 'F': | case 'F': | ||||
Fflag = 1; | Fflag = true; | ||||
break; | break; | ||||
case 'L': | case 'L': | ||||
Pflag = 0; | Pflag = false; | ||||
break; | break; | ||||
case 'P': | case 'P': | ||||
Pflag = 1; | Pflag = true; | ||||
break; | break; | ||||
case 'f': | case 'f': | ||||
fflag = 1; | fflag = true; | ||||
iflag = 0; | iflag = false; | ||||
wflag = 0; | wflag = false; | ||||
break; | break; | ||||
case 'h': | case 'h': | ||||
case 'n': | case 'n': | ||||
hflag = 1; | hflag = true; | ||||
break; | break; | ||||
case 'i': | case 'i': | ||||
iflag = 1; | iflag = true; | ||||
fflag = 0; | fflag = false; | ||||
break; | break; | ||||
case 's': | case 's': | ||||
sflag = 1; | sflag = true; | ||||
break; | break; | ||||
case 'v': | case 'v': | ||||
vflag = 1; | vflag = true; | ||||
break; | break; | ||||
case 'w': | case 'w': | ||||
wflag = 1; | wflag = true; | ||||
break; | break; | ||||
case '?': | case '?': | ||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
argv += optind; | argv += optind; | ||||
argc -= optind; | argc -= optind; | ||||
linkch = sflag ? '-' : '='; | linkch = sflag ? '-' : '='; | ||||
if (sflag == 0) | if (!sflag) | ||||
Fflag = 0; | Fflag = false; | ||||
if (Fflag == 1 && iflag == 0) { | if (Fflag && !iflag) { | ||||
fflag = 1; | fflag = true; | ||||
wflag = 0; /* Implied when fflag != 0 */ | wflag = false; /* Implied when fflag is true */ | ||||
allanjude: This comment should maybe be updated to `Implied when fflag is true`? | |||||
} | } | ||||
switch(argc) { | switch (argc) { | ||||
case 0: | case 0: | ||||
usage(); | usage(); | ||||
/* NOTREACHED */ | /* NOTREACHED */ | ||||
case 1: /* ln source */ | case 1: /* ln source */ | ||||
exit(linkit(argv[0], ".", 1)); | exit(linkit(argv[0], ".", true)); | ||||
case 2: /* ln source target */ | case 2: /* ln source target */ | ||||
exit(linkit(argv[0], argv[1], 0)); | exit(linkit(argv[0], argv[1], false)); | ||||
default: | default: | ||||
; | ; | ||||
} | } | ||||
/* ln source1 source2 directory */ | /* ln source1 source2 directory */ | ||||
targetdir = argv[argc - 1]; | targetdir = argv[argc - 1]; | ||||
if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) { | if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) { | ||||
/* | /* | ||||
* We were asked not to follow symlinks, but found one at | * We were asked not to follow symlinks, but found one at | ||||
* the target--simulate "not a directory" error | * the target--simulate "not a directory" error | ||||
*/ | */ | ||||
errno = ENOTDIR; | errno = ENOTDIR; | ||||
err(1, "%s", targetdir); | err(1, "%s", targetdir); | ||||
} | } | ||||
if (stat(targetdir, &sb)) | if (stat(targetdir, &sb)) | ||||
err(1, "%s", targetdir); | err(1, "%s", targetdir); | ||||
if (!S_ISDIR(sb.st_mode)) | if (!S_ISDIR(sb.st_mode)) | ||||
usage(); | usage(); | ||||
for (exitval = 0; *argv != targetdir; ++argv) | for (exitval = 0; *argv != targetdir; ++argv) | ||||
exitval |= linkit(*argv, targetdir, 1); | exitval |= linkit(*argv, targetdir, true); | ||||
exit(exitval); | exit(exitval); | ||||
} | } | ||||
/* | /* | ||||
* Two pathnames refer to the same directory entry if the directories match | * Two pathnames refer to the same directory entry if the directories match | ||||
* and the final components' names match. | * and the final components' names match. | ||||
*/ | */ | ||||
static int | static int | ||||
Show All 34 Lines | else { | ||||
pathbuf[file2 - path2] = '\0'; | pathbuf[file2 - path2] = '\0'; | ||||
} | } | ||||
if (stat(pathbuf, &sb2) != 0) | if (stat(pathbuf, &sb2) != 0) | ||||
return 0; | return 0; | ||||
return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; | return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino; | ||||
} | } | ||||
static int | static int | ||||
linkit(const char *source, const char *target, int isdir) | linkit(const char *source, const char *target, bool isdir) | ||||
{ | { | ||||
struct stat sb; | |||||
const char *p; | |||||
int ch, exists, first; | |||||
char path[PATH_MAX]; | char path[PATH_MAX]; | ||||
char wbuf[PATH_MAX]; | char wbuf[PATH_MAX]; | ||||
char bbuf[PATH_MAX]; | char bbuf[PATH_MAX]; | ||||
struct stat sb; | |||||
const char *p; | |||||
int ch, first; | |||||
bool exists; | |||||
if (!sflag) { | if (!sflag) { | ||||
/* If source doesn't exist, quit now. */ | /* If source doesn't exist, quit now. */ | ||||
if ((Pflag ? lstat : stat)(source, &sb)) { | if ((Pflag ? lstat : stat)(source, &sb)) { | ||||
warn("%s", source); | warn("%s", source); | ||||
return (1); | return (1); | ||||
} | } | ||||
/* Only symbolic links to directories. */ | /* Only symbolic links to directories. */ | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | if (*source == '/') { | ||||
warn("warning: %s", source); | warn("warning: %s", source); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* If the file exists, first check it is not the same directory entry. | * If the file exists, first check it is not the same directory entry. | ||||
*/ | */ | ||||
exists = !lstat(target, &sb); | exists = lstat(target, &sb) == 0; | ||||
if (exists) { | if (exists) { | ||||
if (!sflag && samedirent(source, target)) { | if (!sflag && samedirent(source, target)) { | ||||
warnx("%s and %s are the same directory entry", | warnx("%s and %s are the same directory entry", | ||||
source, target); | source, target); | ||||
return (1); | return (1); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 57 Lines • Show Last 20 Lines |
This comment should maybe be updated to Implied when fflag is true?