coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2] ln: add the --relative option


From: harald
Subject: [PATCH v2] ln: add the --relative option
Date: Thu, 23 Feb 2012 20:50:27 +0100

From: Harald Hoyer <address@hidden>

With the "--relative --symbolic" options, ln computes the relative
symbolic link for the user.

So, ln works just as cp, but creates relative symbolic links instead
of copying the file.

I miss this feature since the beginning of using ln.

$ tree ./
./
`-- usr
    |-- bin
    `-- lib
        `-- foo
            `-- foo

4 directories, 1 file

$ ln -s -v --relative usr/lib/foo/foo usr/bin/foo
‘usr/bin/foo’ -> ‘../lib/foo/foo’

$ tree ./
./
`-- usr
    |-- bin
    |   `-- foo -> ../lib/foo/foo
    `-- lib
        `-- foo
            `-- foo

4 directories, 2 files

$ ln -s -v --relative usr/bin/foo usr/lib/foo/link-to-foo
‘usr/lib/foo/link-to-foo’ -> ‘foo’

$ tree ./
./
`-- usr
    |-- bin
    |   `-- foo -> ../lib/foo/foo
    `-- lib
        `-- foo
            |-- link-to-foo -> foo
            `-- foo

4 directories, 3 files
---
 src/ln.c |   99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 98 insertions(+), 1 deletions(-)

diff --git a/src/ln.c b/src/ln.c
index d984fd7..77965af 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -31,6 +31,7 @@
 #include "quote.h"
 #include "same.h"
 #include "yesno.h"
+#include "canonicalize.h"
 
 /* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "ln"
@@ -45,6 +46,9 @@ static enum backup_type backup_type;
 /* If true, make symbolic links; otherwise, make hard links.  */
 static bool symbolic_link;
 
+/* If true, make symbolic links relative  */
+static bool relative;
+
 /* If true, hard links are logical rather than physical.  */
 static bool logical = !!LINK_FOLLOWS_SYMLINKS;
 
@@ -90,6 +94,7 @@ static struct option const long_options[] =
   {"target-directory", required_argument, NULL, 't'},
   {"logical", no_argument, NULL, 'L'},
   {"physical", no_argument, NULL, 'P'},
+  {"relative", no_argument, NULL, 'r'},
   {"symbolic", no_argument, NULL, 's'},
   {"verbose", no_argument, NULL, 'v'},
   {GETOPT_HELP_OPTION_DECL},
@@ -120,6 +125,81 @@ target_directory_operand (char const *file)
   return is_a_dir;
 }
 
+static const char *
+convert_abs_rel (const char *from, const char *target)
+{
+  /* we use the 4*MAXPATHLEN, which should not overrun */
+  char relative_from[MAXPATHLEN*4];
+  char *realtarget=NULL, *realfrom=NULL;
+  int level=0, fromlevel=0, targetlevel=0;
+  int l, i, rl;
+
+  realtarget = xstrdup(canonicalize_filename_mode (target, CAN_MISSING));
+  realfrom = xstrdup(canonicalize_filename_mode (from, CAN_MISSING));
+
+  if ((realtarget == NULL) || (realfrom == NULL))
+    {
+      free(realtarget);
+      free(realfrom);
+      return from;
+    }
+
+  /* now calculate the relative path from <from> to <target> and
+     store it in <relative_from>
+  */
+  relative_from[0] = 0;
+  rl = 0;
+
+  /* count the pathname elements of realtarget */
+  for(targetlevel=0, i = 0; realtarget[i]; i++)
+    if (realtarget[i] == '/')
+      targetlevel++;
+
+  /* count the pathname elements of realfrom */
+  for(fromlevel=0, i = 0; realfrom[i]; i++)
+    if (realfrom[i] == '/')
+      fromlevel++;
+
+  /* count the pathname elements, which are common for both paths */
+  for(level=0, i = 0;
+      realtarget[i] && (realtarget[i] == realfrom[i]);
+      i++)
+    if (realtarget[i] == '/')
+      level++;
+
+  /* add "../" to the relative_from path, until the common pathname is
+     reached */
+  for(i = level; i < targetlevel; i++)
+    {
+      if (i != level)
+       relative_from[rl++] = '/';
+      relative_from[rl++] = '.';
+      relative_from[rl++] = '.';
+    }
+
+  /* set l to the next uncommon pathname element in realfrom */
+  for(l = 1, i = 1; i < level; i++)
+    for(l++; realfrom[l] && realfrom[l] != '/'; l++);
+  /* skip next '/' */
+  l++;
+
+  /* append the uncommon rest of realfrom to the relative_from path */
+  for(i = level; i <= fromlevel; i++)
+    {
+      if(rl)
+       relative_from[rl++] = '/';
+      while(realfrom[l] && realfrom[l] != '/')
+       relative_from[rl++] = realfrom[l++];
+      l++;
+    }
+  relative_from[rl] = 0;
+
+  free(realtarget);
+  free(realfrom);
+
+  return xstrdup(relative_from);
+}
+
 /* Make a link DEST to the (usually) existing file SOURCE.
    Symbolic links to nonexistent files are allowed.
    Return true if successful.  */
@@ -246,6 +326,12 @@ do_link (const char *source, const char *dest)
         }
     }
 
+  if (relative)
+    {
+      source = convert_abs_rel(source, dest);
+    }
+
+
   ok = ((symbolic_link ? symlink (source, dest)
          : linkat (AT_FDCWD, source, AT_FDCWD, dest,
                    logical ? AT_SYMLINK_FOLLOW : 0))
@@ -367,6 +453,7 @@ Mandatory arguments to long options are mandatory for short 
options too.\n\
   -n, --no-dereference        treat LINK_NAME as a normal file if\n\
                                 it is a symbolic link to a directory\n\
   -P, --physical              make hard links directly to symbolic links\n\
+  -r, --relative              create softlink relative to LINK_NAME\n\
   -s, --symbolic              make symbolic links instead of hard links\n\
 "), stdout);
       fputs (_("\
@@ -429,7 +516,7 @@ main (int argc, char **argv)
   symbolic_link = remove_existing_files = interactive = verbose
     = hard_dir_link = false;
 
-  while ((c = getopt_long (argc, argv, "bdfinst:vFLPS:T", long_options, NULL))
+  while ((c = getopt_long (argc, argv, "bdfinrst:vFLPS:T", long_options, NULL))
          != -1)
     {
       switch (c)
@@ -460,6 +547,9 @@ main (int argc, char **argv)
         case 'P':
           logical = false;
           break;
+        case 'r':
+          relative = true;
+          break;
         case 's':
           symbolic_link = true;
           break;
@@ -539,6 +629,13 @@ main (int argc, char **argv)
                  ? xget_version (_("backup type"), version_control_string)
                  : no_backups);
 
+  if (relative && !symbolic_link)
+    {
+        error (EXIT_FAILURE, 0,
+               _("cannot do --relative without --symbolic"));
+    }
+
+
   if (target_directory)
     {
       int i;
-- 
1.7.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]