[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] ln: add the --relative option
From: |
harald |
Subject: |
[PATCH] ln: add the --relative option |
Date: |
Thu, 23 Feb 2012 20:03:24 +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 | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 88 insertions(+), 1 deletions(-)
diff --git a/src/ln.c b/src/ln.c
index d984fd7..6bd6e68 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,71 @@ 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));
+
+ /* 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;
+
+ 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 +316,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 +443,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 +506,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 +537,9 @@ main (int argc, char **argv)
case 'P':
logical = false;
break;
+ case 'r':
+ relative = true;
+ break;
case 's':
symbolic_link = true;
break;
@@ -539,6 +619,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
- [PATCH] ln: add the --relative option,
harald <=