-/* `ln' program to create links between files.
- Copyright (C) 1986, 1989-1991, 1995-2009 Free Software Foundation, Inc.
+/* 'ln' program to create links between files.
+ Copyright (C) 1986-2013 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#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). */
+/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "ln"
#define AUTHORS \
/* 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;
/* If nonzero, and the specified destination is a symbolic link to a
directory, treat it just as if it were a directory. Otherwise, the
- command `ln --force --no-dereference file symlink-to-dir' deletes
+ command 'ln --force --no-dereference file symlink-to-dir' deletes
symlink-to-dir before creating the new link. */
static bool dereference_dest_dir_symlinks = true;
{"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},
int err = (stat_result == 0 ? 0 : errno);
bool is_a_dir = !err && S_ISDIR (st.st_mode);
if (err && err != ENOENT)
- error (EXIT_FAILURE, err, _("accessing %s"), quote (file));
+ error (EXIT_FAILURE, err, _("failed to access %s"), quote (file));
if (is_a_dir < looks_like_a_dir)
error (EXIT_FAILURE, err, _("target %s is not a directory"), quote (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. */
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;
: lstat (source, &source_stats))
!= 0)
{
- error (0, errno, _("accessing %s"), quote (source));
+ error (0, errno, _("failed to access %s"), quote (source));
return false;
}
dest_lstat_ok = (lstat (dest, &dest_stats) == 0);
if (!dest_lstat_ok && errno != ENOENT)
{
- error (0, errno, _("accessing %s"), quote (dest));
+ error (0, errno, _("failed to access %s"), quote (dest));
return false;
}
}
the command in question doesn't use --force. */
|| (!symbolic_link && backup_type != no_backups))
&& dest_lstat_ok
- /* Allow `ln -sf --backup k k' to succeed in creating the
+ /* Allow 'ln -sf --backup k k' to succeed in creating the
self-referential symlink, but don't allow the hard-linking
- equivalent: `ln -f k k' (with or without --backup) to get
+ equivalent: 'ln -f k k' (with or without --backup) to get
beyond this point, because the error message you'd get is
misleading. */
&& (backup_type == no_backups || !symbolic_link)
}
}
+ 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))
{
error (0, errno, _("cannot remove %s"), quote (dest));
free (dest_backup);
+ free (rel_source);
return false;
}
error (0, errno,
(symbolic_link
? (errno != ENAMETOOLONG && *source
- ? _("creating symbolic link %s")
- : _("creating symbolic link %s -> %s"))
+ ? _("failed to create symbolic link %s")
+ : _("failed to create symbolic link %s -> %s"))
: (errno == EMLINK && !source_is_dir
- ? _("creating hard link to %.0s%s")
+ ? _("failed to create hard link to %.0s%s")
: (errno == EDQUOT || errno == EEXIST || errno == ENOSPC
|| errno == EROFS)
- ? _("creating hard link %s")
- : _("creating hard link %s => %s"))),
+ ? _("failed to create hard link %s")
+ : _("failed to create hard link %s => %s"))),
quote_n (0, dest), quote_n (1, source));
if (dest_backup)
}
free (dest_backup);
+ free (rel_source);
return ok;
}
usage (int status)
{
if (status != EXIT_SUCCESS)
- fprintf (stderr, _("Try `%s --help' for more information.\n"),
- program_name);
+ emit_try_help ();
else
{
printf (_("\
In the 2nd form, create a link to TARGET in the current directory.\n\
In the 3rd and 4th forms, create links to each TARGET in DIRECTORY.\n\
Create hard links by default, symbolic links with --symbolic.\n\
+By default, each destination (name of new link) should not already exist.\n\
When creating hard links, each TARGET must exist. Symbolic links\n\
can hold arbitrary text; if later resolved, a relative link is\n\
interpreted in relation to its parent directory.\n\
-\n\
-"), stdout);
- fputs (_("\
-Mandatory arguments to long options are mandatory for short options too.\n\
"), stdout);
+
+ emit_mandatory_arg_note ();
+
fputs (_("\
--backup[=CONTROL] make a backup of each existing destination file\n\
-b like --backup but does not accept an argument\n\
"), stdout);
fputs (_("\
-i, --interactive prompt whether to remove destinations\n\
- -L, --logical make hard links to symbolic link references\n\
- -n, --no-dereference treat destination that is a symlink to a\n\
- directory as if it were a normal file\n\
+ -L, --logical dereference TARGETs that are symbolic links\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 (_("\
-S, --suffix=SUFFIX override the usual backup suffix\n\
-t, --target-directory=DIRECTORY specify the DIRECTORY in which to create\n\
the links\n\
- -T, --no-target-directory treat LINK_NAME as a normal file\n\
+ -T, --no-target-directory treat LINK_NAME as a normal file always\n\
-v, --verbose print name of each linked file\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
fputs (_("\
\n\
-The backup suffix is `~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
+The backup suffix is '~', unless set with --suffix or SIMPLE_BACKUP_SUFFIX.\n\
The version control method may be selected via the --backup option or through\n\
the VERSION_CONTROL environment variable. Here are the values:\n\
\n\
"), stdout);
- printf (_("\
-Using -s ignores -L and -P. Otherwise, the last option specified controls\n\
-behavior when the source is a symbolic link, defaulting to %s.\n\
-\n\
-"), LINK_FOLLOWS_SYMLINKS ? "-L" : "-P");
fputs (_("\
none, off never make backups (even if --backup is given)\n\
numbered, t make numbered backups\n\
existing, nil numbered if numbered backups exist, simple otherwise\n\
simple, never always make simple backups\n\
"), stdout);
+ printf (_("\
+\n\
+Using -s ignores -L and -P. Otherwise, the last option specified controls\n\
+behavior when a TARGET is a symbolic link, defaulting to %s.\n\
+"), LINK_FOLLOWS_SYMLINKS ? "-L" : "-P");
emit_ancillary_info ();
}
exit (status);
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)
case 'P':
logical = false;
break;
+ case 'r':
+ relative = true;
+ break;
case 's':
symbolic_link = true;
break;
{
struct stat st;
if (stat (optarg, &st) != 0)
- error (EXIT_FAILURE, errno, _("accessing %s"), quote (optarg));
+ error (EXIT_FAILURE, errno, _("failed to access %s"),
+ quote (optarg));
if (! S_ISDIR (st.st_mode))
error (EXIT_FAILURE, 0, _("target %s is not a directory"),
quote (optarg));
? 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;