Subject: [PATCH] install-sh: be stricter in catching invalid usages

From: Stefano Lattarini <[email-masked]>
Date: Thu, 26 Dec 2013 00:18:14 +0100

severity 15376 minor
tags 15376 + patch
close 15376
stop

Reference:
http://debbugs.gnu.org/cgi/bugreport.cgi?bug=15376

Hi Tobias, thanks for the report and the patch, and sorry for the
shameful delay.  I've adjusted the problem you reported, plus other
similar issues I noticed along the way, with the attached patch.

Thank you,
  Stefano

>From 222337e60bfc87456773a4c7cbbbd3192fde956d Mon Sep 17 00:00:00 2001
Message-Id: <222337e60[email-masked]>
From: Stefano Lattarini <[email-masked]>
Date: Thu, 26 Dec 2013 00:07:27 +0100
Subject: [PATCH] install-sh: be stricter in catching invalid usages

Such usages (which are rejected by GNU install as well) are:

  - options -d and -t used together;

  - argument passed to option -t must be a directory;

  - if there are two or more SOURCEFILE arguments, the
    DESTINATION argument must be a directory.

Note that we still allow the use of options -d and -T together, by
making -d take the precedence; this is for compatibility with GNU
install.

This change fixes, among other things, automake bug#15376.

* lib/install-sh: Adjust.
* t/install-sh-unittests.sh: Enhance.
* NEWS: Update.
* THANKS: Add reporter of bug#15376.

Helped-by: Tobias Hansen <[email-masked]>
Signed-off-by: Stefano Lattarini <[email-masked]>
---
 NEWS                      | 13 +++++++++---
 THANKS                    |  1 +
 lib/install-sh            | 31 ++++++++++++++++++++++-----
 t/install-sh-unittests.sh | 54 ++++++++++++++++++++++++++++++++---------------
 4 files changed, 74 insertions(+), 25 deletions(-)

diff --git a/NEWS b/NEWS
index a6e1979..8d902c0 100644
--- a/NEWS
+++ b/NEWS
@@ -64,15 +64,22 @@
 
 New in 1.15:
 
-* Cleanups and modernizations:
+* Improvements and refactorings in the install-sh script:
 
-  - The install-sh script has been modernized, and now makes the following
-    assumptions *unconditionally*:
+  - It has been modernized, and now makes the following assumptions
+    *unconditionally*:
     (1) a working 'dirname' program is available;
     (2) the ${var:-value} shell parameters substitution works;
     (3) the "set -f" and "set +f" shell commands work, and, respectively,
         disable and enable shell globbing.
 
+  - The script implements stricter error checking, an it will now complain
+    and bail out if:
+    (1) the options -d and -t are used together;
+    (2) the argument passed to option -t must be a directory;
+    (3) if there are two or more SOURCEFILE arguments, the
+        DESTINATION argument must be a directory.
+
 * Automake-generated testsuites:
 
   - The default test-driver used by the Automake-generates testsuites now
diff --git a/THANKS b/THANKS
index e4f70f3..2b4f8ee 100644
--- a/THANKS
+++ b/THANKS
@@ -401,6 +401,7 @@ Tim Mooney                      [email-masked]
 Tim Retout                      [email-masked]
 Tim Rice                        [email-masked]
 Tim Van Holder                  [email-masked]
+Tobias Hansen                   [email-masked]
 Toshio Kuratomi                 [email-masked]
 Tom Epperly                     [email-masked]
 Tom Rini                        [email-masked]
diff --git a/lib/install-sh b/lib/install-sh
index 0436737..d8de87f 100755
--- a/lib/install-sh
+++ b/lib/install-sh
@@ -1,7 +1,7 @@
 #!/bin/sh
 # install - install a program, script, or datafile
 
-scriptversion=2013-10-30.23; # UTC
+scriptversion=2013-12-25.23; # UTC
 
 # This originates from X11R5 (mit/util/scripts/install.sh), which was
 # later released in X11R6 (xc/config/util/install.sh) with the
@@ -82,7 +82,7 @@ dir_arg=
 dst_arg=
 
 copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
 
 usage="\
 Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -139,14 +139,16 @@ while test $# -ne 0; do
 
     -s) stripcmd=$stripprog;;
 
-    -t) dst_arg=$2
+    -t)
+        is_target_a_directory=always
+        dst_arg=$2
         # Protect names problematic for 'test' and other utilities.
         case $dst_arg in
           -* | [=\(\)!]) dst_arg=./$dst_arg;;
         esac
         shift;;
 
-    -T) no_target_directory=true;;
+    -T) is_target_a_directory=never;;
 
     --version) echo "$0 $scriptversion"; exit $?;;
 
@@ -161,6 +163,16 @@ while test $# -ne 0; do
   shift
 done
 
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+  if test -n "$dst_arg"; then
+    echo "$0: target directory not allowed when installing a directory." >&2
+    exit 1
+  fi
+fi
+
 if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
   # When -d is used, all remaining arguments are directories to create.
   # When -t is used, the destination is already specified.
@@ -181,6 +193,15 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
   done
 fi
 
+if test -z "$dir_arg"; then
+  if test $# -gt 1 || test "$is_target_a_directory" = always; then
+    if test ! -d "$dst_arg"; then
+      echo "$0: $dst_arg: Is not a directory." >&2
+      exit 1
+    fi
+  fi
+fi
+
 if test $# -eq 0; then
   if test -z "$dir_arg"; then
     echo "$0: no input file specified." >&2
@@ -253,7 +274,7 @@ do
     # If destination is a directory, append the input filename; won't work
     # if double slashes aren't ignored.
     if test -d "$dst"; then
-      if test -n "$no_target_directory"; then
+      if test "$is_target_a_directory" = never; then
         echo "$0: $dst_arg: Is a directory" >&2
         exit 1
       fi
diff --git a/t/install-sh-unittests.sh b/t/install-sh-unittests.sh
index 8a60d74..1b85c9c 100644
--- a/t/install-sh-unittests.sh
+++ b/t/install-sh-unittests.sh
@@ -25,26 +25,46 @@ get_shell_script install-sh
 ./install-sh && exit 1
 ./install-sh -m 644 dest && exit 1
 
-# Directories.
+# Incorrect usages.
+: > bar
+: > baz
+: > qux
+./install-sh -d -t foo && exit 1
+./install-sh -d -t foo bar && exit 1
+./install-sh -t foo bar && exit 1
+./install-sh bar baz foo && exit 1
+mkdir foo
+./install-sh -d -t foo && exit 1
+./install-sh -d -t foo bar && exit 1
+rmdir foo
+rm -f bar baz qux
 
-# It should be OK to create no directory.  We sometimes need
-# this when directory are conditionally defined.
-./install-sh -d
-# One directory.
-./install-sh -d d0
-test -d d0
-# Multiple directories (for make installdirs).
-./install-sh -d d1 d2 d3 d4
-test -d d1
-test -d d2
-test -d d3
-test -d d4
-# Subdirectories.
-./install-sh -d p1/p2/p3 p4//p5//p6//
-test -d p1/p2/p3
-test -d p4/p5/p6
+# Directories.
+for opts in '-d' '-d -T' '-T -d' '-d -T -d' '-T -d -T -d -T'; do
+  # It should be OK to create no directory.  We sometimes need
+  # this when directory are conditionally defined.
+  ./install-sh $opts
+  # One directory.
+  ./install-sh $opts d0
+  test -d d0
+  # Multiple directories (for make installdirs).
+  ./install-sh $opts d1 d2 d3 d4
+  test -d d1
+  test -d d2
+  test -d d3
+  test -d d4
+  rmdir d[0-9]
+  # Subdirectories.
+  ./install-sh $opts p1/p2/p3 p4//p5//p6//
+  test -d p1/p2/p3
+  test -d p4/p5/p6
+  rmdir p[0-9]/p[0-9]/p[0-9]
+  rmdir p[0-9]/p[0-9]
+  rmdir p[0-9]
+done
 
 # Files.
+mkdir d0 d1 d2 d3 d4
 : > x
 ./install-sh -c -m 644 x y
 test -f x
-- 
1.8.5.rc0.335.g7794a68