summaryrefslogtreecommitdiff
path: root/src/ln.c
diff options
context:
space:
mode:
authorHarald Hoyer <harald@redhat.com>2012-03-22 20:51:20 +0000
committerPádraig Brady <P@draigBrady.com>2012-03-22 20:51:20 +0000
commit15b8318e93a947d1422e9a927f699eda1085cf0d (patch)
treed992d1a8f2dbc137446b6691c9144543c3e79cf6 /src/ln.c
parentb1428e3152afa3139a2968dd530e507ddd312051 (diff)
downloadcoreutils-15b8318e93a947d1422e9a927f699eda1085cf0d.tar.xz
ln: add the --relative option
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/Makefile.am: Reference the relpath module. * src/ln.c (usage): Mention the new option. (do_link): Call the relative conversion if specified. (convert_abs_rel): Perform the relative conversion using the relpath module. * tests/ln/relative: Add a new test. * tests/Makefile.am: Reference the new test. * doc/coreutils.texi: Document the new feature. * NEWS: Mention the new feature.
Diffstat (limited to 'src/ln.c')
-rw-r--r--src/ln.c52
1 files changed, 51 insertions, 1 deletions
diff --git a/src/ln.c b/src/ln.c
index d984fd7b4..e7ab3487e 100644
--- a/src/ln.c
+++ b/src/ln.c
@@ -29,8 +29,10 @@
#include "hash.h"
#include "hash-triple.h"
#include "quote.h"
+#include "relpath.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 +47,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 +95,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 +126,33 @@ target_directory_operand (char const *file)
return is_a_dir;
}
+/* Return FROM represented as relative to the dir of TARGET.
+ The result is malloced. */
+
+static char *
+convert_abs_rel (const char *from, const char *target)
+{
+ char *realtarget = canonicalize_filename_mode (target, CAN_MISSING);
+ char *realfrom = canonicalize_filename_mode (from, CAN_MISSING);
+
+ /* Write to a PATH_MAX buffer. */
+ char *relative_from = xmalloc (PATH_MAX);
+
+ /* Get dirname to generate paths relative to. */
+ realtarget[dir_len (realtarget)] = '\0';
+
+ if (!relpath (realfrom, realtarget, relative_from, PATH_MAX))
+ {
+ free (relative_from);
+ relative_from = NULL;
+ }
+
+ free (realtarget);
+ free (realfrom);
+
+ return relative_from ? relative_from : xstrdup (from);
+}
+
/* Make a link DEST to the (usually) existing file SOURCE.
Symbolic links to nonexistent files are allowed.
Return true if successful. */
@@ -130,6 +163,7 @@ do_link (const char *source, const char *dest)
struct stat source_stats;
struct stat dest_stats;
char *dest_backup = NULL;
+ char *rel_source = NULL;
bool dest_lstat_ok = false;
bool source_is_dir = false;
bool ok;
@@ -246,6 +280,9 @@ do_link (const char *source, const char *dest)
}
}
+ if (relative)
+ source = rel_source = convert_abs_rel (source, dest);
+
ok = ((symbolic_link ? symlink (source, dest)
: linkat (AT_FDCWD, source, AT_FDCWD, dest,
logical ? AT_SYMLINK_FOLLOW : 0))
@@ -276,6 +313,7 @@ do_link (const char *source, const char *dest)
{
error (0, errno, _("cannot remove %s"), quote (dest));
free (dest_backup);
+ free (rel_source);
return false;
}
@@ -322,6 +360,7 @@ do_link (const char *source, const char *dest)
}
free (dest_backup);
+ free (rel_source);
return ok;
}
@@ -367,6 +406,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 symbolic links relative to link location\n\
-s, --symbolic make symbolic links instead of hard links\n\
"), stdout);
fputs (_("\
@@ -429,7 +469,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 +500,9 @@ main (int argc, char **argv)
case 'P':
logical = false;
break;
+ case 'r':
+ relative = true;
+ break;
case 's':
symbolic_link = true;
break;
@@ -539,6 +582,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;