aboutsummaryrefslogtreecommitdiff
path: root/bin/ln/ln.c
diff options
context:
space:
mode:
Diffstat (limited to 'bin/ln/ln.c')
-rw-r--r--bin/ln/ln.c154
1 files changed, 86 insertions, 68 deletions
diff --git a/bin/ln/ln.c b/bin/ln/ln.c
index e37dee361b19..3055c7563cca 100644
--- a/bin/ln/ln.c
+++ b/bin/ln/ln.c
@@ -29,20 +29,6 @@
* SUCH DAMAGE.
*/
-#if 0
-#ifndef lint
-static char const copyright[] =
-"@(#) Copyright (c) 1987, 1993, 1994\n\
- The Regents of the University of California. All rights reserved.\n";
-#endif /* not lint */
-
-#ifndef lint
-static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94";
-#endif /* not lint */
-#endif
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/stat.h>
@@ -51,30 +37,32 @@ __FBSDID("$FreeBSD$");
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-static int fflag; /* Unlink existing files. */
-static int Fflag; /* Remove empty directories also. */
-static int hflag; /* Check new name for symlink first. */
-static int iflag; /* Interactive mode. */
-static int Pflag; /* Create hard links to symlinks. */
-static int sflag; /* Symbolic, not hard, link. */
-static int vflag; /* Verbose output. */
-static int wflag; /* Warn if symlink target does not
+static bool fflag; /* Unlink existing files. */
+static bool Fflag; /* Remove empty directories also. */
+static bool hflag; /* Check new name for symlink first. */
+static bool iflag; /* Interactive mode. */
+static bool Pflag; /* Create hard links to symlinks. */
+static bool sflag; /* Symbolic, not hard, link. */
+static bool vflag; /* Verbose output. */
+static bool wflag; /* Warn if symlink target does not
* exist, and -f is not enabled. */
static char linkch;
-static int linkit(const char *, const char *, int);
-static void usage(void);
+static int linkit(const char *, const char *, bool);
+static void link_usage(void) __dead2;
+static void usage(void) __dead2;
int
main(int argc, char *argv[])
{
struct stat sb;
- char *p, *targetdir;
+ char *targetdir;
int ch, exitval;
/*
@@ -82,52 +70,55 @@ main(int argc, char *argv[])
* "link", for which the functionality provided is greatly
* simplified.
*/
- if ((p = strrchr(argv[0], '/')) == NULL)
- p = argv[0];
- else
- ++p;
- if (strcmp(p, "link") == 0) {
+ if (strcmp(getprogname(), "link") == 0) {
while (getopt(argc, argv, "") != -1)
- usage();
+ link_usage();
argc -= optind;
argv += optind;
if (argc != 2)
- usage();
- exit(linkit(argv[0], argv[1], 0));
+ link_usage();
+ if (lstat(argv[1], &sb) == 0)
+ errc(1, EEXIST, "%s", argv[1]);
+ /*
+ * We could simply call link(2) here, but linkit()
+ * performs additional checks and gives better
+ * diagnostics.
+ */
+ exit(linkit(argv[0], argv[1], false));
}
while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
switch (ch) {
case 'F':
- Fflag = 1;
+ Fflag = true;
break;
case 'L':
- Pflag = 0;
+ Pflag = false;
break;
case 'P':
- Pflag = 1;
+ Pflag = true;
break;
case 'f':
- fflag = 1;
- iflag = 0;
- wflag = 0;
+ fflag = true;
+ iflag = false;
+ wflag = false;
break;
case 'h':
case 'n':
- hflag = 1;
+ hflag = true;
break;
case 'i':
- iflag = 1;
- fflag = 0;
+ iflag = true;
+ fflag = false;
break;
case 's':
- sflag = 1;
+ sflag = true;
break;
case 'v':
- vflag = 1;
+ vflag = true;
break;
case 'w':
- wflag = 1;
+ wflag = true;
break;
case '?':
default:
@@ -138,21 +129,21 @@ main(int argc, char *argv[])
argc -= optind;
linkch = sflag ? '-' : '=';
- if (sflag == 0)
- Fflag = 0;
- if (Fflag == 1 && iflag == 0) {
- fflag = 1;
- wflag = 0; /* Implied when fflag != 0 */
+ if (!sflag)
+ Fflag = false;
+ if (Fflag && !iflag) {
+ fflag = true;
+ wflag = false; /* Implied when fflag is true */
}
- switch(argc) {
+ switch (argc) {
case 0:
usage();
/* NOTREACHED */
case 1: /* ln source */
- exit(linkit(argv[0], ".", 1));
+ exit(linkit(argv[0], ".", true));
case 2: /* ln source target */
- exit(linkit(argv[0], argv[1], 0));
+ exit(linkit(argv[0], argv[1], false));
default:
;
}
@@ -171,7 +162,7 @@ main(int argc, char *argv[])
if (!S_ISDIR(sb.st_mode))
usage();
for (exitval = 0; *argv != targetdir; ++argv)
- exitval |= linkit(*argv, targetdir, 1);
+ exitval |= linkit(*argv, targetdir, true);
exit(exitval);
}
@@ -221,15 +212,23 @@ samedirent(const char *path1, const char *path2)
return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
}
+/*
+ * Create a link to source. If target is a directory (and some additional
+ * conditions apply, see comments within) the link will be created within
+ * target and have the basename of source. Otherwise, the link will be
+ * named target. If isdir is true, target has already been determined to
+ * be a directory; otherwise, we will check, if needed.
+ */
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 wbuf[PATH_MAX];
char bbuf[PATH_MAX];
+ struct stat sb;
+ const char *p;
+ int ch, first;
+ bool append, exists;
if (!sflag) {
/* If source doesn't exist, quit now. */
@@ -246,14 +245,27 @@ linkit(const char *source, const char *target, int isdir)
}
/*
- * If the target is a directory (and not a symlink if hflag),
- * append the source's name, unless Fflag is set.
+ * Append a slash and the source's basename if:
+ * - the target is "." or ends in "/" or "/.", or
+ * - the target is a directory (and not a symlink if hflag) and
+ * Fflag is not set
*/
- if (!Fflag && (isdir ||
- (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
- (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode)))) {
+ if ((p = strrchr(target, '/')) == NULL)
+ p = target;
+ else
+ p++;
+ append = false;
+ if (p[0] == '\0' || (p[0] == '.' && p[1] == '\0')) {
+ append = true;
+ } else if (!Fflag) {
+ if (isdir || (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
+ (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
+ append = true;
+ }
+ }
+ if (append) {
if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
- (p = basename(bbuf)) == NULL ||
+ (p = basename(bbuf)) == NULL /* can't happen */ ||
snprintf(path, sizeof(path), "%s/%s", target, p) >=
(ssize_t)sizeof(path)) {
errno = ENAMETOOLONG;
@@ -292,7 +304,7 @@ linkit(const char *source, const char *target, int isdir)
/*
* 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 (!sflag && samedirent(source, target)) {
warnx("%s and %s are the same directory entry",
@@ -350,11 +362,17 @@ linkit(const char *source, const char *target, int isdir)
}
static void
+link_usage(void)
+{
+ (void)fprintf(stderr, "usage: link source_file target_file\n");
+ exit(1);
+}
+
+static void
usage(void)
{
- (void)fprintf(stderr, "%s\n%s\n%s\n",
+ (void)fprintf(stderr, "%s\n%s\n",
"usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
- " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir",
- " link source_file target_file");
+ " ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir");
exit(1);
}