+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.
- 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:
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
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)
{
}
}
+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
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
AT_DATA([lt-real],
[[#! /bin/sh
-echo "$@"
+for arg
+do
+ printf %s\\n "$arg"
+done
cat
]])
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
])
# 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.
[], [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