The fixincl executable uses system function to call applyfix or to direcly patch a header file, with parameters enclosed in single quotes. The problem is that MinGW system function just calls cmd.exe, which doesn't strip quotes from parameters and completely ignores quotes for embedding spaces in parameters. The MinGW system function also doesn't allow embedding newlines in executed command parameters. As a result fixincludes doesn't work at all when trying to build a cross compiler with mingw as host.
On MinGW host, this patch adds system_with_shell function, which transforms command passed to system function is following way: * Enclose entire command in double quotes * Prepend shell executable name, taken from environment variable SHELL * Replace each occurence of newline with '$'\n'' sequence which is understood by bash and ksh * Escape special characters Second attempt: Moved system_with_shell to fixlib.c. Escape some special characters located between single quotes as enclosing single-quoted string in double quotes nullifies single quotes. Fixed passing file name to apply_fix. Fixed "test" tests and "shell" fixes, also for DJGPP. Convert line all endings to unix in check.sh to avoid false positives. Followed Jeff's and Bruce's advices: Fixed formatting. Call system_with_shell explicitly. If it's not needed it just expands to "system". On MinGW (mingw64 actually) all fixinclude tests pass. On DJGPP all but two tests pass - if you manage to get it to compile at all. 2016-09-30 Tadek Kijkowski <tkijkow...@gmail.com> * check.tpl: Convert line endings to unix on test outputs * fixfixes.c: Fixed passing file name to apply_fix when SEPARATE_FIX_PROC is defined * fixincl.c: Use system_with_shell, fixes for MinGW and DJGPP * fixlib.c, fixlib.h: Added system_with_shell and fix_path_separators Index: fixincludes/check.tpl =================================================================== --- fixincludes/check.tpl (revision 240643) +++ fixincludes/check.tpl (working copy) @@ -123,6 +123,11 @@ exec < ${TESTDIR}/LIST while read f do + if [ -n "$MSYSTEM" -o -n "$DJGPP" ] + then + # On MinGW and DJGPP convert line endings to avoid false positives + mv $f $f.dos; tr -d '\r' < $f.dos > $f; rm $f.dos + fi if [ ! -f ${TESTBASE}/$f ] then echo "Newly fixed header: $f" >&2 Index: fixincludes/fixfixes.c =================================================================== --- fixincludes/fixfixes.c (revision 240643) +++ fixincludes/fixfixes.c (working copy) @@ -790,7 +790,8 @@ return EXIT_FAILURE; } - apply_fix (pFix, argv[1]); + /* Second parameter of apply_fix is file name */ + apply_fix (pFix, argv[2]); fclose (stdout); fclose (stdin); unlink (argv[4]); Index: fixincludes/fixincl.c =================================================================== --- fixincludes/fixincl.c (revision 240643) +++ fixincludes/fixincl.c (working copy) @@ -74,9 +74,12 @@ #endif /* DO_STATS */ const char incl_quote_pat[] = "^[ \t]*#[ \t]*include[ \t]*\"[^/]"; -tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n"; regex_t incl_quote_re; +#ifndef SEPARATE_FIX_PROC +tSCC z_fork_err[] = "Error %d (%s) starting filter process for %s\n"; +#endif + static void do_version (void) ATTRIBUTE_NORETURN; char *load_file (const char *); void run_compiles (void); @@ -188,7 +191,7 @@ puts (zBuf + 5); exit (strcmp (run_shell (zBuf), program_id)); #else - exit (system (zBuf)); + exit (system_with_shell (zBuf)); #endif } @@ -275,6 +278,11 @@ /* NULL as the first argument to `tempnam' causes it to DTRT wrt the temporary directory where the file will be created. */ pz_temp_file = tempnam( NULL, "fxinc" ); + +#if defined(__MINGW32__) + fix_path_separators (pz_temp_file); +#endif + # endif signal (SIGQUIT, SIG_IGN); @@ -566,7 +574,27 @@ free ((void *) pz_res); return res; } +#elif defined(__MINGW32__) || defined(__DJGPP__) +static int +test_test (tTestDesc* p_test, char* pz_test_file) +{ + tSCC cmd_fmt[] = +#if defined(__DJGPP__) + "file=%s; test %s >/dev/null 2>/dev/null"; #else + "file=%s; test %s > /dev/null 2>&1"; +#endif + int res; + + char *cmd_buf = XNEWVEC (char, strlen(cmd_fmt) + strlen(pz_test_file) + strlen(p_test->pz_test_text)); + + sprintf (cmd_buf, cmd_fmt, pz_test_file, p_test->pz_test_text); + res = system_with_shell (cmd_buf); + + free (cmd_buf); + return res ? SKIP_FIX : APPLY_FIX; +} +#else /* * IF we are in MS-DOS land, then whatever shell-type test is required * will, by definition, fail @@ -887,7 +915,7 @@ else /* NOT an "internal" fix: */ { size_t parg_size; -#ifdef __MSDOS__ +#if defined(__MSDOS__) && !defined(__DJGPP__) /* Don't use the "src > dstX; rm -f dst; mv -f dstX dst" trick: dst is a temporary file anyway, so we know there's no other file by that name; and DOS's system(3) doesn't mind to @@ -906,6 +934,8 @@ implementations cannot cope :-(. */ tSCC z_cmd_fmt[] = " %s > %sX ; rm -f %s; mv -f %sX %s"; #endif + tSCC z_subshell_start[] = "( "; + tSCC z_subshell_end[] = " ) < "; tCC** ppArgs = p_fixd->patch_args; argsize = sizeof( z_cmd_fmt ) + strlen( pz_temp_file ) @@ -912,6 +942,10 @@ + strlen( pz_file_source ); parg_size = argsize; + if (p_fixd->fd_flags & FD_SHELL_SCRIPT) + { + argsize += strlen( z_subshell_start ) + strlen ( z_subshell_end ); + } /* * Compute the size of the command line. Add lotsa extra space @@ -937,6 +971,16 @@ ppArgs = p_fixd->patch_args; /* + * If it's shell script, enclose it in parentheses and skip "sh -c". + */ + if (p_fixd->fd_flags & FD_SHELL_SCRIPT) + { + strcpy (pz_scan, z_subshell_start); + pz_scan += strlen (z_subshell_start); + ppArgs += 2; + } + + /* * Copy the program name, unquoted */ { @@ -978,9 +1022,18 @@ } /* + * Close parenthesis if it's shell script. + */ + if (p_fixd->fd_flags & FD_SHELL_SCRIPT) + { + strcpy (pz_scan, z_subshell_end); + pz_scan += strlen (z_subshell_end); + } + + /* * add the file machinations. */ -#ifdef __MSDOS__ +#if defined(__MSDOS__) && !defined(__DJGPP__) sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file ); #else sprintf (pz_scan, z_cmd_fmt, pz_file_source, pz_temp_file, @@ -987,8 +1040,8 @@ pz_temp_file, pz_temp_file, pz_temp_file); #endif } - system( pz_cmd ); - free( (void*)pz_cmd ); + system_with_shell (pz_cmd); + free (pz_cmd); } /* * * * * * * * * * * * * @@ -1090,7 +1143,7 @@ t_bool saw_sum_test = BOOL_FALSE; t_bool one_sum_passed = BOOL_FALSE; -#ifdef SEPARATE_FIX_PROC +#if defined(__MSDOS__) && !defined(__DJGPP__) /* * There is only one fix that uses a shell script as of this writing. * I hope to nuke it anyway, it does not apply to DOS and it would Index: fixincludes/fixlib.c =================================================================== --- fixincludes/fixlib.c (revision 240643) +++ fixincludes/fixlib.c (working copy) @@ -278,3 +278,141 @@ } #endif + +#if defined(__MINGW32__) +void +fix_path_separators (char* p) +{ + while (p != NULL) + { + p = strchr (p, '\\'); + if (p != NULL) + { + *p = '/'; + ++p; + } + } +} + +/* Count number of needle character ocurrences in str */ +static int +count_occurrences_of_char (char* str, char needle) +{ + int cnt = 0; + + while (str) + { + str = strchr (str, needle); + if (str) + { + ++str; + ++cnt; + } + } + + return cnt; +} + +/* On Mingw32, system function will just start cmd by default. + Call system function, but prepend ${CONFIG_SHELL} or ${SHELL} -c to the command, + replace newlines with '$'\n'', enclose command with double quotes + and escape special characters which were originally enclosed in single quotes. + */ +int +system_with_shell (char* s) +{ + static const char z_shell_start_args[] = " -c \""; + static const char z_shell_end_args[] = "\""; + static const char z_shell_newline[] = "'$'\\n''"; + + /* Use configured shell if present */ + char *env_shell = getenv ("CONFIG_SHELL"); + int newline_cnt = count_occurrences_of_char (s, '\n'); + int escapes_cnt = count_occurrences_of_char( s, '\\') + + count_occurrences_of_char (s, '"') + + count_occurrences_of_char (s, '`'); + char *long_cmd; + char *cmd_endp; + int sys_result; + char *s_scan; + int in_quotes; + + if (env_shell == NULL) + env_shell = getenv ("SHELL"); + + /* If neither CONFIGURED_SHELL nor SHELL is set, just call standard system function */ + if (env_shell == NULL) + return system (s); + + /* Allocate enough memory to fit newly created command string */ + long_cmd = XNEWVEC (char, strlen (env_shell) + + strlen (z_shell_start_args) + + strlen (s) + + newline_cnt * (strlen (z_shell_newline) - 1) + + escapes_cnt + + strlen (z_shell_end_args) + + 1); + + /* Start with ${SHELL} */ + strcpy (long_cmd, env_shell); + cmd_endp = long_cmd + strlen (long_cmd); + + /* Opening quote */ + strcpy (cmd_endp, z_shell_start_args); + cmd_endp += strlen (z_shell_start_args); + + /* Replace newlines and escape special chars */ + in_quotes = 0; + for (s_scan = s; *s_scan; ++s_scan) + { + switch (*s_scan) + { + case '\n': + if (in_quotes) + { + /* Replace newline inside quotes with '$'\n'' */ + strcpy (cmd_endp, z_shell_newline); + cmd_endp += strlen (z_shell_newline); + } + else + { + /* Replace newlines outside quotes with ; and merge subsequent newlines */ + *(cmd_endp++) = ';'; + *(cmd_endp++) = ' '; + while (*(s_scan + 1) == '\n' || *(s_scan + 1) == ' ' || *(s_scan + 1) == '\t') + ++s_scan; + } + break; + case '\'': + /* Escape single quote and toggle in_quotes flag */ + in_quotes = !in_quotes; + *(cmd_endp++) = *s_scan; + break; + case '\\': + case '`': + /* Escape backslash and backtick inside quotes */ + if (in_quotes) + *(cmd_endp++) = '\\'; + *(cmd_endp++) = *s_scan; + break; + case '"': + /* Escape double quotes always */ + *(cmd_endp++) = '\\'; + *(cmd_endp++) = *s_scan; + break; + default: + *(cmd_endp++) = *s_scan; + } + } + + /* Closing quote */ + strcpy (cmd_endp, z_shell_end_args); + + sys_result = system (long_cmd); + + free (long_cmd); + + return sys_result; +} + +#endif /* defined(__MINGW32__) */ Index: fixincludes/fixlib.h =================================================================== --- fixincludes/fixlib.h (revision 240643) +++ fixincludes/fixlib.h (working copy) @@ -269,4 +269,19 @@ t_bool mn_get_regexps ( regex_t** label_re, regex_t** name_re, tCC *who ); void initialize_opts ( void ); + +#if defined(__MINGW32__) + +void fix_path_separators ( char* p ); + +/* prepend shell name to command passed to system call */ +int system_with_shell ( char* s ); + +#else + +/* normal call */ +#define system_with_shell system + +#endif /* defined(__MINGW32__) */ + #endif /* ! GCC_FIXLIB_H */