maint: improve error messages upon failed read, write, access, close
[platform/upstream/coreutils.git] / src / ln.c
index 6eeddde..1aa1473 100644 (file)
--- a/src/ln.c
+++ b/src/ln.c
@@ -1,5 +1,5 @@
-/* `ln' program to create links between files.
-   Copyright (C) 1986, 1989-1991, 1995-2012 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 \
@@ -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;
 
@@ -65,7 +70,7 @@ static bool hard_dir_link;
 
 /* 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;
 
@@ -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},
@@ -114,12 +120,39 @@ target_directory_operand (char const *file)
   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.  */
@@ -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;
@@ -144,7 +178,7 @@ do_link (const char *source, const char *dest)
            : lstat (source, &source_stats))
           != 0)
         {
-          error (0, errno, _("accessing %s"), quote (source));
+          error (0, errno, _("failed to access %s"), quote (source));
           return false;
         }
 
@@ -165,7 +199,7 @@ do_link (const char *source, const char *dest)
       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;
         }
     }
@@ -195,9 +229,9 @@ do_link (const char *source, const char *dest)
           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)
@@ -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;
 }
 
@@ -348,11 +387,10 @@ 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\
@@ -367,6 +405,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 (_("\
@@ -390,9 +429,9 @@ the VERSION_CONTROL environment variable.  Here are the values:\n\
   numbered, t     make numbered backups\n\
   existing, nil   numbered if numbered backups exist, simple otherwise\n\
   simple, never   always make simple backups\n\
-\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");
@@ -429,7 +468,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 +499,9 @@ main (int argc, char **argv)
         case 'P':
           logical = false;
           break;
+        case 'r':
+          relative = true;
+          break;
         case 's':
           symbolic_link = true;
           break;
@@ -470,7 +512,8 @@ main (int argc, char **argv)
             {
               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));
@@ -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;