commit 94ef670b27900c9f2e47181cd79acd55c6cf6e1a
parent cb427d553af31c30c1c1ca0c1490374fee1b6cdb
Author: Michael Forney <mforney@mforney.org>
Date: Sun, 23 Nov 2014 20:25:39 +0000
ln: Add support for target directories
Also, now that we are using {sym,}linkat, implement the trivial -L and
-P options.
Diffstat:
M | ln.1 | | | 11 | +++++++++-- |
M | ln.c | | | 53 | ++++++++++++++++++++++++++++++++++++++--------------- |
2 files changed, 47 insertions(+), 17 deletions(-)
diff --git a/ln.1 b/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
diff --git a/ln.c b/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";
- to = argc < 2 ? basename(argv[0]) : argv[1];
+ 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--;
+ }
- 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;
}