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:
parent
cb427d553a
commit
94ef670b27
11
ln.1
11
ln.1
|
@ -3,12 +3,12 @@
|
|||
ln \- make links between files
|
||||
.SH SYNOPSIS
|
||||
.B ln
|
||||
.RB [ \-fs ]
|
||||
.RB [ \-LPfs ]
|
||||
.I file
|
||||
.RI [ name ]
|
||||
.P
|
||||
.B ln
|
||||
.RB [ \-fs ]
|
||||
.RB [ \-LPfs ]
|
||||
.RI [ file ...]
|
||||
.RI [ directory ]
|
||||
.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.
|
||||
.SH OPTIONS
|
||||
.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
|
||||
remove existing destinations.
|
||||
.TP
|
||||
|
|
53
ln.c
53
ln.c
|
@ -1,9 +1,11 @@
|
|||
/* See LICENSE file for copyright and license details. */
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "util.h"
|
||||
|
@ -11,16 +13,20 @@
|
|||
static 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
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
int (*flink)(const char *, const char *);
|
||||
char *fname, *to;
|
||||
int sflag = 0;
|
||||
int fflag = 0;
|
||||
int hasto = 0;
|
||||
int dirfd = AT_FDCWD;
|
||||
int flags = AT_SYMLINK_FOLLOW;
|
||||
struct stat st;
|
||||
|
||||
ARGBEGIN {
|
||||
case 'f':
|
||||
|
@ -29,27 +35,44 @@ main(int argc, char *argv[])
|
|||
case 's':
|
||||
sflag = 1;
|
||||
break;
|
||||
case 'L':
|
||||
flags |= AT_SYMLINK_FOLLOW;
|
||||
break;
|
||||
case 'P':
|
||||
flags &= ~AT_SYMLINK_FOLLOW;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
} ARGEND;
|
||||
|
||||
if (argc == 0 || argc > 2)
|
||||
if (argc == 0)
|
||||
usage();
|
||||
|
||||
if (sflag) {
|
||||
flink = symlink;
|
||||
fname = "symlink";
|
||||
} else {
|
||||
flink = link;
|
||||
fname = "link";
|
||||
fname = sflag ? "symlink" : "link";
|
||||
|
||||
if (argc >= 2) {
|
||||
if (stat(argv[argc - 1], &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
if ((dirfd = open(argv[argc - 1], O_RDONLY)) < 0)
|
||||
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];
|
||||
|
||||
if (fflag)
|
||||
remove(to);
|
||||
if (flink(argv[0], to) < 0)
|
||||
eprintf("%s %s <- %s:", fname, argv[0], to);
|
||||
for (; argc > 0; argc--, argv++) {
|
||||
if (!hasto)
|
||||
to = basename(argv[0]);
|
||||
if (fflag)
|
||||
remove(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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user