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

Reply via email to