]> git.ipfire.org Git - thirdparty/coreutils.git/commitdiff
install: allow options -D and -t to be used together
authorBernhard Voelker <mail@bernhard-voelker.de>
Wed, 11 Jun 2014 09:28:03 +0000 (11:28 +0200)
committerBernhard Voelker <mail@bernhard-voelker.de>
Wed, 11 Jun 2014 13:48:39 +0000 (15:48 +0200)
* src/install.c (install_file_in_file_parents): Factor out the
creation of any parent directories into ...
(mkancesdirs_safe_wd): ... this new function.
(install_file_in_dir): Add the parameter 'mkdir_and_install', and
call the above new function if it evaluates to true.
(main): During parsing of the -t option, move the check whether
the target_directory exists down  after the option parsing loop,
and do not complain about stat(optarg,...) failing if -D was given.
Pass 'mkdir_and_install' to install_file_in_dir().
* doc/coreutils.texi (install invocation): Remove the (false)
restriction that -D would be ignored together with -t.  Instead,
clarify install's new bahavior.
Fix the node's reference in the top-level @direntry for consistency.
* src/install/basic-1.sh: Add tests for the now-allowed combination
of the -D and -t options.
* NEWS: Mention the improvement.

NEWS
doc/coreutils.texi
src/install.c
tests/install/basic-1.sh

diff --git a/NEWS b/NEWS
index fa1aab8d58f66e71a3153e8c47ef73e2adacc5f7..2f43da60cec16449ca408d1035fbf506b4074a49 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -104,6 +104,8 @@ GNU coreutils NEWS                                    -*- outline -*-
   causing name look-up errors.  Also look-ups are first done outside the chroot,
   in case the look-up within the chroot fails due to library conflicts etc.
 
+  install now allows the combination of the -D and -t options.
+
   numfmt supports zero padding of numbers using the standard printf
   syntax of a leading zero, for example --format="%010f".
   Also throughput was improved by up to 800% by avoiding redundant processing.
index 243b6d745c1977ee75aeca080a4892d4c9782af5..3cdfb72fed0e717430a93856aa24c0f70fb3404c 100644 (file)
@@ -70,7 +70,7 @@
 * hostid: (coreutils)hostid invocation.         Print numeric host identifier.
 * hostname: (coreutils)hostname invocation.     Print or set system name.
 * id: (coreutils)id invocation.                 Print user identity.
-* install: (coreutils)install invocation.       Copy and change attributes.
+* install: (coreutils)install invocation.       Copy files and set attributes.
 * join: (coreutils)join invocation.             Join lines on a common field.
 * kill: (coreutils)kill invocation.             Send a signal to processes.
 * link: (coreutils)link invocation.             Make hard links between files.
@@ -9071,8 +9071,8 @@ Ignored; for compatibility with old Unix versions of @command{install}.
 @opindex -D
 Create any missing parent directories of @var{dest},
 then copy @var{source} to @var{dest}.
-This option is ignored if a destination directory is specified
-via @option{--target-directory=DIR}.
+Explicitly specifying the @option{--target-directory=@var{dir}} will similarly
+ensure the presence of that hierarchy before copying @var{source} arguments.
 
 @item -d
 @itemx --directory
index c9bfdaef04f2908c5b4d03fbff888259909edad4..e7de260c66280cb79d863490a197e3a8f45fb631 100644 (file)
@@ -706,8 +706,7 @@ install_file_in_file (const char *from, const char *to,
    Return true if successful.  */
 
 static bool
-install_file_in_file_parents (char const *from, char *to,
-                              struct cp_options *x)
+mkancesdirs_safe_wd (char const *from, char *to, struct cp_options *x)
 {
   bool save_working_directory =
     ! (IS_ABSOLUTE_FILE_NAME (from) && IS_ABSOLUTE_FILE_NAME (to));
@@ -737,8 +736,18 @@ install_file_in_file_parents (char const *from, char *to,
           return false;
         }
     }
+  return status == EXIT_SUCCESS;
+}
+
+/* Copy file FROM onto file TO, creating any missing parent directories of TO.
+   Return true if successful.  */
 
-  return (status == EXIT_SUCCESS && install_file_in_file (from, to, x));
+static bool
+install_file_in_file_parents (char const *from, char *to,
+                              const struct cp_options *x)
+{
+  return (mkancesdirs_safe_wd (from, to, (struct cp_options *)x)
+          && install_file_in_file (from, to, x));
 }
 
 /* Copy file FROM into directory TO_DIR, keeping its same name,
@@ -747,11 +756,16 @@ install_file_in_file_parents (char const *from, char *to,
 
 static bool
 install_file_in_dir (const char *from, const char *to_dir,
-                     const struct cp_options *x)
+                     const struct cp_options *x, bool mkdir_and_install)
 {
   const char *from_base = last_component (from);
   char *to = file_name_concat (to_dir, from_base, NULL);
-  bool ret = install_file_in_file (from, to, x);
+  bool ret = true;
+
+  if (mkdir_and_install)
+    ret = mkancesdirs_safe_wd (from, to, (struct cp_options *)x);
+
+  ret = ret && install_file_in_file (from, to, x);
   free (to);
   return ret;
 }
@@ -851,16 +865,6 @@ main (int argc, char **argv)
           if (target_directory)
             error (EXIT_FAILURE, 0,
                    _("multiple target directories specified"));
-          else
-            {
-              struct stat st;
-              if (stat (optarg, &st) != 0)
-                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));
-            }
           target_directory = optarg;
           break;
         case 'T':
@@ -915,6 +919,18 @@ main (int argc, char **argv)
     error (EXIT_FAILURE, 0,
            _("target directory not allowed when installing a directory"));
 
+  if (target_directory)
+    {
+      struct stat st;
+      bool stat_success = stat (target_directory, &st) == 0 ? true : false;
+      if (! mkdir_and_install && ! stat_success)
+        error (EXIT_FAILURE, errno, _("failed to access %s"),
+               quote (target_directory));
+      if (stat_success && ! S_ISDIR (st.st_mode))
+        error (EXIT_FAILURE, 0, _("target %s is not a directory"),
+               quote (target_directory));
+    }
+
   if (backup_suffix_string)
     simple_backup_suffix = xstrdup (backup_suffix_string);
 
@@ -1020,7 +1036,8 @@ main (int argc, char **argv)
           int i;
           dest_info_init (&x);
           for (i = 0; i < n_files; i++)
-            if (! install_file_in_dir (file[i], target_directory, &x))
+            if (! install_file_in_dir (file[i], target_directory, &x,
+                                       mkdir_and_install))
               exit_status = EXIT_FAILURE;
         }
     }
index 283426aca0c32f3ca6eda32559b893792cefa040..9835d463e54a689c0ff64a169e3096c794f661cb 100755 (executable)
@@ -112,4 +112,31 @@ ginstall: creating directory 'sub3/a/b/c'
 'file' -> 'sub3/a/b/c/file'
 EOF
 
+# Test -D together with -t (available since coreutils >= 8.23).
+# Let ginstall create a completely new destination hierarchy.
+ginstall -t sub4/a/b/c -Dv file >out 2>&1 || fail=1
+compare - out <<\EOF || fail=1
+ginstall: creating directory 'sub4'
+ginstall: creating directory 'sub4/a'
+ginstall: creating directory 'sub4/a/b'
+ginstall: creating directory 'sub4/a/b/c'
+'file' -> 'sub4/a/b/c/file'
+EOF
+
+# Ensure that -D with an already existing file as -t's option argument fails.
+touch sub4/file_exists || framework_failure_
+ginstall -t sub4/file_exists -Dv file >out 2>&1 && fail=1
+compare - out <<\EOF || fail=1
+ginstall: target 'sub4/file_exists' is not a directory
+EOF
+
+# Ensure that -D with an already existing directory for -t's option argument
+# succeeds.
+mkdir sub4/dir_exists || framework_failure_
+touch sub4/dir_exists || framework_failure_
+ginstall -t sub4/dir_exists -Dv file >out 2>&1 || fail=1
+compare - out <<\EOF || fail=1
+'file' -> 'sub4/dir_exists/file'
+EOF
+
 Exit $fail