ln: Add support for target directories

Also, now that we are using {sym,}linkat, implement the trivial -L and
-P options.
This commit is contained in:
Michael Forney 2014-11-23 20:25:39 +00:00 committed by sin
parent cb427d553a
commit 94ef670b27
2 changed files with 47 additions and 17 deletions

11
ln.1
View File

@ -3,12 +3,12 @@
ln \- make links between files ln \- make links between files
.SH SYNOPSIS .SH SYNOPSIS
.B ln .B ln
.RB [ \-fs ] .RB [ \-LPfs ]
.I file .I file
.RI [ name ] .RI [ name ]
.P .P
.B ln .B ln
.RB [ \-fs ] .RB [ \-LPfs ]
.RI [ file ...] .RI [ file ...]
.RI [ directory ] .RI [ directory ]
.SH DESCRIPTION .SH DESCRIPTION
@ -18,6 +18,13 @@ it is linked into the current directory. If multiple files are listed they will
be linked into the given directory. be linked into the given directory.
.SH OPTIONS .SH OPTIONS
.TP .TP
.B \-L
create links to the files referenced by symbolic link source files (default
behavior).
.TP
.B \-P
create links to symbolic link source files themselves.
.TP
.B \-f .B \-f
remove existing destinations. remove existing destinations.
.TP .TP

53
ln.c
View File

@ -1,9 +1,11 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <libgen.h> #include <libgen.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "util.h" #include "util.h"
@ -11,16 +13,20 @@
static void static void
usage(void) usage(void)
{ {
eprintf("usage: %s [-fs] target [linkname]\n", argv0); eprintf("usage: %1$s [-LPfs] target [linkname]\n"
" %1$s [-LPfs] target... directory\n", argv0);
} }
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
int (*flink)(const char *, const char *);
char *fname, *to; char *fname, *to;
int sflag = 0; int sflag = 0;
int fflag = 0; int fflag = 0;
int hasto = 0;
int dirfd = AT_FDCWD;
int flags = AT_SYMLINK_FOLLOW;
struct stat st;
ARGBEGIN { ARGBEGIN {
case 'f': case 'f':
@ -29,27 +35,44 @@ main(int argc, char *argv[])
case 's': case 's':
sflag = 1; sflag = 1;
break; break;
case 'L':
flags |= AT_SYMLINK_FOLLOW;
break;
case 'P':
flags &= ~AT_SYMLINK_FOLLOW;
break;
default: default:
usage(); usage();
} ARGEND; } ARGEND;
if (argc == 0 || argc > 2) if (argc == 0)
usage(); usage();
if (sflag) { fname = sflag ? "symlink" : "link";
flink = symlink;
fname = "symlink"; if (argc >= 2) {
} else { if (stat(argv[argc - 1], &st) == 0 && S_ISDIR(st.st_mode)) {
flink = link; if ((dirfd = open(argv[argc - 1], O_RDONLY)) < 0)
fname = "link"; eprintf("open:");
} else if (argc == 2) {
to = argv[1];
hasto = 1;
} else {
eprintf("destination is not a directory\n");
}
argc--;
} }
to = argc < 2 ? basename(argv[0]) : argv[1]; for (; argc > 0; argc--, argv++) {
if (!hasto)
if (fflag) to = basename(argv[0]);
remove(to); if (fflag)
if (flink(argv[0], to) < 0) remove(to);
eprintf("%s %s <- %s:", fname, argv[0], to); if ((!sflag ? linkat(AT_FDCWD, argv[0], dirfd, to, flags)
: symlinkat(argv[0], dirfd, to)) < 0) {
eprintf("%s %s <- %s:", fname, argv[0], to);
}
}
return 0; return 0;
} }