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 */

Reply via email to