[dev] [sbase] [PATCH] ln: Add support for target directories

From: Michael Forney <mforney_AT_mforney.org>
Date: Sun, 23 Nov 2014 20:25:39 +0000

Also, now that we are using {sym,}linkat, implement the trivial -L and
-P options.
---
 ln.1 | 11 +++++++++--
 ln.c | 53 ++++++++++++++++++++++++++++++++++++++---------------
 2 files changed, 47 insertions(+), 17 deletions(-)
diff --git a/ln.1 b/ln.1
index 4205ea7..3b1ac98 100644
--- a/ln.1
+++ b/ln.1
_AT_@ -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
_AT_@ -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
index d8809ce..0a5bf18 100644
--- a/ln.c
+++ b/ln.c
_AT_@ -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"
_AT_@ -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':
_AT_@ -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;
 }
-- 
2.1.3.1.g339ec9c
Received on Sun Nov 23 2014 - 21:25:39 CET

This archive was generated by hypermail 2.3.0 : Sun Nov 23 2014 - 21:36:08 CET