Hi,
We use the ln builtin in a project in order to avoid several forks made
by calling the coreutils ln executable. Attached is a patch to implement
the -n option to the ln builtin, making calls like ln -snf ... doable as
with the coreutils version.
Feel free to add this patch to the sources if you think it's worth it.
Kind regards,
Julien
>From dff9b244b233415d5081c3e4b40500e01929c74a Mon Sep 17 00:00:00 2001
From: Julien Thomas <jtho...@exosec.fr>
Date: Mon, 27 May 2013 17:45:11 +0200
Subject: [PATCH 1/3] ln: Whitespace cleanup, remove tabulations
---
ln.c | 61 +++++++++++++++++++++++++++++++------------------------------
1 file changed, 31 insertions(+), 30 deletions(-)
diff --git a/ln.c b/ln.c
index ec73636..8fb0561 100644
--- a/ln.c
+++ b/ln.c
@@ -63,17 +63,17 @@ ln_builtin (list)
while ((opt = internal_getopt (list, "fs")) != -1)
{
switch (opt)
- {
- case 'f':
- flags |= LN_UNLINK;
- break;
- case 's':
- flags |= LN_SYMLINK;
- break;
- default:
- builtin_usage ();
- return (EX_USAGE);
- }
+ {
+ case 'f':
+ flags |= LN_UNLINK;
+ break;
+ case 's':
+ flags |= LN_SYMLINK;
+ break;
+ default:
+ builtin_usage ();
+ return (EX_USAGE);
+ }
}
list = loptend;
@@ -85,10 +85,10 @@ ln_builtin (list)
linkfn = (flags & LN_SYMLINK) ? symlink : link;
- if (list->next == 0) /* ln target, equivalent to ln target . */
+ if (list->next == 0) /* ln target, equivalent to ln target . */
return (dolink (list->word->word, ".", flags));
- if (list->next->next == 0) /* ln target source */
+ if (list->next->next == 0) /* ln target source */
return (dolink (list->word->word, list->next->word->word, flags));
/* ln target1 target2 ... directory */
@@ -156,16 +156,16 @@ dolink (src, dst, flags)
if ((flags & LN_SYMLINK) == 0)
{
if (stat (src, &ssb) != 0)
- {
- builtin_error ("%s: %s", src, strerror (errno));
- return (EXECUTION_FAILURE);
- }
+ {
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
if (S_ISDIR (ssb.st_mode))
- {
- errno = EISDIR;
- builtin_error ("%s: %s", src, strerror (errno));
- return (EXECUTION_FAILURE);
- }
+ {
+ errno = EISDIR;
+ builtin_error ("%s: %s", src, strerror (errno));
+ return (EXECUTION_FAILURE);
+ }
}
/* If the destination is a directory, create the final filename by appending
@@ -174,9 +174,9 @@ dolink (src, dst, flags)
if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
{
if ((p = strrchr (src, '/')) == 0)
- p = src;
+ p = src;
else
- p++;
+ p++;
dst_path = mkdirpath (dst, p);
dst = dst_path;
@@ -217,10 +217,11 @@ char *ln_doc[] = {
/* The standard structure describing a builtin command. bash keeps an array
of these structures. */
struct builtin ln_struct = {
- "ln", /* builtin name */
- ln_builtin, /* function implementing the builtin */
- BUILTIN_ENABLED, /* initial flags for builtin */
- ln_doc, /* array of long documentation strings. */
- "ln [-fs] file1 [file2] OR ln [-fs] file ... directory", /* usage synopsis; becomes short_doc */
- 0 /* reserved for internal use */
+ "ln", /* builtin name */
+ ln_builtin, /* function implementing the builtin */
+ BUILTIN_ENABLED, /* initial flags for builtin */
+ ln_doc, /* array of long documentation strings. */
+ /* usage synopsis; becomes short_doc */
+ "ln [-fs] file1 [file2] OR ln [-fs] file ... directory",
+ 0 /* reserved for internal use */
};
--
1.7.10.4
>From a235a5072390854986c09f197097757acbc52ca5 Mon Sep 17 00:00:00 2001
From: Julien Thomas <jtho...@exosec.fr>
Date: Mon, 27 May 2013 17:45:39 +0200
Subject: [PATCH 2/3] ln: Add missing return type in ln_builtin function
definition
---
ln.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/ln.c b/ln.c
index 8fb0561..63bd38c 100644
--- a/ln.c
+++ b/ln.c
@@ -50,6 +50,7 @@ typedef int unix_link_syscall_t __P((const char *, const char *));
static unix_link_syscall_t *linkfn;
static int dolink ();
+int
ln_builtin (list)
WORD_LIST *list;
{
--
1.7.10.4
>From 0d0c745f4c5dd2c346b15fc0ac597986ce2b2b42 Mon Sep 17 00:00:00 2001
From: Julien Thomas <jtho...@exosec.fr>
Date: Mon, 27 May 2013 17:52:52 +0200
Subject: [PATCH 3/3] ln: Implement -n option as in corutils ln
This option treats destination that is a symlink to a directory
as if it was a normal file. It allows ln -snf ... calls as doable
with coreutils ln.
The option is only available if HAVE_LSTAT is defined as it's the
way the original builtin was implemented.
---
ln.c | 37 +++++++++++++++++++++++++++++--------
1 file changed, 29 insertions(+), 8 deletions(-)
diff --git a/ln.c b/ln.c
index 63bd38c..386f32f 100644
--- a/ln.c
+++ b/ln.c
@@ -46,6 +46,7 @@ typedef int unix_link_syscall_t __P((const char *, const char *));
#define LN_SYMLINK 0x01
#define LN_UNLINK 0x02
+#define LN_NO_DEREF_DDIR_SYM 0x04
static unix_link_syscall_t *linkfn;
static int dolink ();
@@ -61,7 +62,11 @@ ln_builtin (list)
flags = 0;
reset_internal_getopt ();
+#if defined (HAVE_LSTAT)
+ while ((opt = internal_getopt (list, "fsn")) != -1)
+#else
while ((opt = internal_getopt (list, "fs")) != -1)
+#endif
{
switch (opt)
{
@@ -71,6 +76,11 @@ ln_builtin (list)
case 's':
flags |= LN_SYMLINK;
break;
+#if defined (HAVE_LSTAT)
+ case 'n':
+ flags |= LN_NO_DEREF_DDIR_SYM;
+ break;
+#endif
default:
builtin_usage ();
return (EX_USAGE);
@@ -139,8 +149,10 @@ mkdirpath (dir, file)
#if defined (HAVE_LSTAT)
# define LSTAT lstat
+# define LSTAT_OR_STAT_IF(c, f, b) ((c) ? lstat((f), (b)) : stat((f), (b)))
#else
# define LSTAT stat
+# define LSTAT_OR_STAT_IF(c, f, b) (stat((f), (b)))
#endif
static int
@@ -172,7 +184,8 @@ dolink (src, dst, flags)
/* If the destination is a directory, create the final filename by appending
the basename of the source to the destination. */
dst_path = 0;
- if ((stat (dst, &dsb) == 0) && S_ISDIR (dsb.st_mode))
+ if ((LSTAT_OR_STAT_IF(flags & LN_NO_DEREF_DDIR_SYM, dst, &dsb) == 0) &&
+ (S_ISDIR (dsb.st_mode)) != 0)
{
if ((p = strrchr (src, '/')) == 0)
p = src;
@@ -206,13 +219,17 @@ dolink (src, dst, flags)
}
char *ln_doc[] = {
- "Link files.",
- "",
- "Create a new directory entry with the same modes as the original",
- "file. The -f option means to unlink any existing file, permitting",
- "the link to occur. The -s option means to create a symbolic link.",
- "By default, ln makes hard links.",
- (char *)NULL
+ "Link files.",
+ "",
+ "Create a new directory entry with the same modes as the original file.",
+ "Available options:",
+ " -f Unlink any existing file, permitting the link to occur.",
+ " -s Create a symbolic link (by default, ln makes hard links).",
+#if defined (HAVE_LSTAT)
+ " -n Treat destination that is a symlink to a directory as if it",
+ " were a normal file.",
+#endif
+ (char *)NULL
};
/* The standard structure describing a builtin command. bash keeps an array
@@ -223,6 +240,10 @@ struct builtin ln_struct = {
BUILTIN_ENABLED, /* initial flags for builtin */
ln_doc, /* array of long documentation strings. */
/* usage synopsis; becomes short_doc */
+#if defined (HAVE_LSTAT)
+ "ln [-fsn] file1 [file2] OR ln [-fsn] file ... directory",
+#else
"ln [-fs] file1 [file2] OR ln [-fs] file ... directory",
+#endif
0 /* reserved for internal use */
};
--
1.7.10.4