]> git.ipfire.org Git - thirdparty/libtool.git/commitdiff
Fix cwrapper argument mangling on w32.
authorBruno Haible <bruno@clisp.org>
Tue, 11 Nov 2008 05:51:24 +0000 (06:51 +0100)
committerRalf Wildenhues <Ralf.Wildenhues@gmx.de>
Tue, 11 Nov 2008 05:53:39 +0000 (06:53 +0100)
* libltdl/config/ltmain.m4sh (func_emit_cwrapperexe_src): On
mingw, preprocess the argument vector through prepare_spawn.
* tests/execute-mode.at (execute mode): Output args
newline-separated.  Extend tests by more argument pairs that
contain special characters, where the w32 cwrapper fails.
Also test a real compiled program, linked against an uninstalled
library, to expose cwrapper issues.
* NEWS: Update.

Signed-off-by: Ralf Wildenhues <Ralf.Wildenhues@gmx.de>
ChangeLog
NEWS
libltdl/config/ltmain.m4sh
tests/execute-mode.at

index 2148a2c4342e4fbb32bdbc0f793faf045b755b91..1abce44e01e43316338980734206b7aaead1ed6b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2008-11-11  Bruno Haible  <bruno@clisp.org>
+           Ralf Wildenhues  <Ralf.Wildenhues@gmx.de>
+
+       Fix cwrapper argument mangling on w32.
+       * libltdl/config/ltmain.m4sh (func_emit_cwrapperexe_src): On
+       mingw, preprocess the argument vector through prepare_spawn.
+       * tests/execute-mode.at (execute mode): Output args
+       newline-separated.  Extend tests by more argument pairs that
+       contain special characters, where the w32 cwrapper fails.
+       Also test a real compiled program, linked against an uninstalled
+       library, to expose cwrapper issues.
+       * NEWS: Update.
+
 2008-11-10  Ralf Wildenhues  <Ralf.Wildenhues@gmx.de>
 
        Update to GFDL 1.3.
diff --git a/NEWS b/NEWS
index 4945c5170b8c8c9651d68021744fdfdfea8e1545..a38bfb3a7539b4888add9cc0ec7af0c334020c7a 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ New in 2.2.8 2008-??-??: git version 2.2.7a, Libtool team:
 
   - Fix 2.2.6 regression that prevented using the libltdl macros together
     with Autoconf 2.59 (`possibly undefined macro: LT_LIBEXT').
+  - Fix 2.2.4 regression that caused arguments with special characters
+    to be mangled by the compile wrapper for uninstalled programs on MinGW.
 
 * Miscellaneous changes:
 
index 005c6a8f80d9d88a0801035b05a0fa3642aa799f..e7a5ff0cf598eda6c781d18e0447430d2c1b0e92 100644 (file)
@@ -2887,6 +2887,7 @@ void lt_opt_process_env_append (const char *arg);
 int lt_split_name_value (const char *arg, char** name, char** value);
 void lt_update_exe_path (const char *name, const char *value);
 void lt_update_lib_path (const char *name, const char *value);
+char **prepare_spawn (char **argv);
 
 static const char *script_text_part1 =
 EOF
@@ -3167,6 +3168,7 @@ EOF
              mingw*)
                cat <<"EOF"
   /* execv doesn't actually work on mingw as expected on unix */
+  newargz = prepare_spawn (newargz);
   rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
   if (rval == -1)
     {
@@ -3630,8 +3632,126 @@ lt_update_lib_path (const char *name, const char *value)
     }
 }
 
+EOF
+           case $host_os in
+             mingw*)
+               cat <<"EOF"
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Win32 CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+ */
+#define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XMALLOC (char *, argc + 1);
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+       new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+       {
+         int quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+         size_t length;
+         unsigned int backslashes;
+         const char *s;
+         char *quoted_string;
+         char *p;
+
+         length = 0;
+         backslashes = 0;
+         if (quote_around)
+           length++;
+         for (s = string; *s != '\0'; s++)
+           {
+             char c = *s;
+             if (c == '"')
+               length += backslashes + 1;
+             length++;
+             if (c == '\\')
+               backslashes++;
+             else
+               backslashes = 0;
+           }
+         if (quote_around)
+           length += backslashes + 1;
+
+         quoted_string = XMALLOC (char, length + 1);
 
+         p = quoted_string;
+         backslashes = 0;
+         if (quote_around)
+           *p++ = '"';
+         for (s = string; *s != '\0'; s++)
+           {
+             char c = *s;
+             if (c == '"')
+               {
+                 unsigned int j;
+                 for (j = backslashes + 1; j > 0; j--)
+                   *p++ = '\\';
+               }
+             *p++ = c;
+             if (c == '\\')
+               backslashes++;
+             else
+               backslashes = 0;
+           }
+         if (quote_around)
+           {
+             unsigned int j;
+             for (j = backslashes; j > 0; j--)
+               *p++ = '\\';
+             *p++ = '"';
+           }
+         *p = '\0';
+
+         new_argv[i] = quoted_string;
+       }
+      else
+       new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
 EOF
+               ;;
+           esac
 }
 # end: func_emit_cwrapperexe_src
 
index 349c829f790e16163485c0585a10216876e7160e..acbe0810fcec2399551e1cea934a0bac3d87d558 100644 (file)
 AT_SETUP([execute mode])
 AT_KEYWORDS([libtool])
 
+eval `$LIBTOOL --config | $EGREP '^(FGREP)='`
+
 AT_DATA([foo],
 [[#! /bin/sh
 if test $# -gt 0; then
-  echo "$@"
+  for arg
+  do
+    printf %s\\n "$arg"
+  done
 else
   :
 fi
@@ -50,7 +55,10 @@ fi
 
 AT_DATA([lt-real],
 [[#! /bin/sh
-echo "$@"
+for arg
+do
+  printf %s\\n "$arg"
+done
 cat
 ]])
 
@@ -81,6 +89,45 @@ mkdir sub
 cp foo sub/foo
 chmod +x foo sub/foo lt-wrapper lt-real
 
+AT_DATA([liba.c],
+[[int a () { return 0; }
+]])
+
+AT_DATA([main.c],
+[[#include <stdio.h>
+extern int a ();
+int main (int argc, char **argv)
+{
+  int i;
+  for (i=1; i<argc; ++i)
+    {
+      if (i != 1)
+       fputc ('\n', stdout);
+      fputs (argv[i], stdout);
+    }
+  fputc ('\n', stdout);
+  return a ();
+}
+]])
+
+instdir=`pwd`/inst
+libdir=$instdir/lib
+
+AT_CHECK([$LIBTOOL --mode=compile $CC $CPPFLAGS $CFLAGS -c liba.c],
+        [], [ignore], [ignore])
+AT_CHECK([$LIBTOOL --mode=link $CC $CFLAGS $LDFLAGS -o liba.la -rpath $libdir liba.lo],
+        [], [ignore], [ignore])
+AT_CHECK([$CC $CPPFLAGS $CFLAGS -c main.c],
+        [], [ignore], [ignore])
+AT_CHECK([$LIBTOOL --mode=link $CC $CFLAGS $LDFLAGS -o main main.$OBJEXT liba.la],
+        [], [ignore], [ignore])
+
+# end of preparatory blurb.
+# Now, when doing the tests, we both try the fake wrappers plus the real one
+# (only the latter exposes the C wrappers used for w32 systems).
+# With the latter, however, we need to ignore additional output; esp. wine
+# may be rather noisy.
+
 AT_CHECK([$LIBTOOL --mode=execute ./foo])
 AT_CHECK([$LIBTOOL --mode=execute sub/foo])
 AT_CHECK([$LIBTOOL --mode=execute ./foo foo], [], [foo
@@ -91,7 +138,9 @@ AT_CHECK([cd sub && $LIBTOOL --mode=execute ./foo ../foo], [], [../foo
 ])
 # suppose that ./foo is gdb, and lt-wrapper is the wrapper script.
 AT_CHECK([$LIBTOOL --mode=execute ./foo lt-wrapper bar baz </dev/null], [],
-        [./lt-real bar baz
+        [./lt-real
+bar
+baz
 ])
 
 # check that stdin works even with -dlopen.
@@ -116,7 +165,41 @@ AT_CHECK([$LIBTOOL --mode=execute ./lt-wrapper "arg  with special chars: \$!&*\`
         [], [arg  with special chars: $!&*`'()
 ])
 AT_CHECK([$LIBTOOL --mode=execute ./foo lt-wrapper "arg  with special chars: \$!&*\`'()"],
-        [], [./lt-real arg  with special chars: $!&*`'()
+        [], [./lt-real
+arg  with special chars: $!&*`'()
 ])
 
+# We always pair two args.  The first one is never the empty string.
+arg1=
+for arg2 in \
+   'def ghi' '' \
+   'd"e' 'f"g' \
+   'd\"e' 'f\"g' \
+   'd\\"e' 'f\\"g' \
+   'd\\\"e' 'f\\\"g' \
+   'd\' '' \
+   'd\\' '' \
+   'd\\\' '' \
+   'd\\\\' '' \
+   '<' '>' \
+   '<def>' ''
+do
+  if test -z "$arg1"; then
+    arg1=$arg2; continue
+  fi
+  AT_CHECK([$LIBTOOL --mode=execute ./foo abc "$arg1" "$arg2" xyz], [], [stdout])
+  AT_CHECK([$FGREP "$arg1" stdout], [], [ignore])
+  AT_CHECK([$FGREP "$arg2" stdout], [], [ignore])
+  AT_CHECK([test `sed -n '/^abc$/,/^xyz$/p' stdout | wc -l` -eq 4])
+  AT_CHECK([$LIBTOOL --mode=execute ./lt-wrapper abc "$arg1" "$arg2" xyz </dev/null], [], [stdout])
+  AT_CHECK([$FGREP "$arg1" stdout], [], [ignore])
+  AT_CHECK([$FGREP "$arg2" stdout], [], [ignore])
+  AT_CHECK([test `sed -n '/^abc$/,/^xyz$/p' stdout | wc -l` -eq 4])
+  AT_CHECK([$LIBTOOL --mode=execute ./foo lt-wrapper abc "$arg1" "$arg2" xyz], [], [stdout])
+  AT_CHECK([$FGREP "$arg1" stdout], [], [ignore])
+  AT_CHECK([$FGREP "$arg2" stdout], [], [ignore])
+  AT_CHECK([test `sed -n '/^abc$/,/^xyz$/p' stdout | wc -l` -eq 4])
+  arg1=
+done
+
 AT_CLEANUP