git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 06/20] mingw: replace mingw_startup() hack

From
Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com>
Date
Jun 19, 2019, 21:05 UTC
Message-ID
<02f618464f85c0d61921f22948369f76b630e1e0.1560978354.git.gitgitgadget@gmail.com>
In-Reply-To
<pull.149.v2.git.gitgitgadget@gmail.com>
From: Johannes Schindelin <johannes.schindelin@gmx.de>

Git for Windows has special code to retrieve the command-line parameters (and even the environment) in UTF-16 encoding, so that they can be converted to UTF-8. This is necessary because Git for Windows wants to use UTF-8 encoded strings throughout its code, and the main() function does not get the parameters in that encoding.

To do that, we used the __wgetmainargs() function, which is not even a Win32 API function, but provided by the MINGW "runtime" instead.

Obviously, this method would not work with any compiler other than GCC, and in preparation for compiling with Visual C++, we would like to avoid precisely that.

Lucky us, there is a much more elegant way: we can simply implement the UTF-16 variant of `main()`: `wmain()`.

To make that work, we need to link with -municode. The command-line parameters are passed to `wmain()` encoded in UTF-16, as desired, and this method also works with GCC, and also with Visual C++ after adjusting the MSVC linker flags to force it to use `wmain()`.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 compat/mingw.c   | 53 +++++++++++++++++++++++++++++++-----------------
 compat/mingw.h   | 22 ++++++++++----------
 config.mak.uname |  3 ++-
 3 files changed, 47 insertions(+), 31 deletions(-)
diff --git a/compat/mingw.c b/compat/mingw.c
index 9b6d2400e1..0d8713e515 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -2301,18 +2301,13 @@ static void setup_windows_environment(void)
 		setenv("TERM", "cygwin", 1);
 }
 
+#if !defined(_MSC_VER)
 /*
  * Disable MSVCRT command line wildcard expansion (__getmainargs called from
  * mingw startup code, see init.c in mingw runtime).
  */
 int _CRT_glob = 0;
-
-typedef struct {
-	int newmode;
-} _startupinfo;
-
-extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
-		_startupinfo *si);
+#endif
 
 static NORETURN void die_startup(void)
 {
@@ -2390,22 +2385,25 @@ static void maybe_redirect_std_handles(void)
 				  GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
 }
 
-void mingw_startup(void)
+/*
+ * We implement wmain() and compile with -municode, which would
+ * normally ignore main(), but we call the latter from the former
+ * so that we can handle non-ASCII command-line parameters
+ * appropriately.
+ *
+ * To be more compatible with the core git code, we convert
+ * argv into UTF8 and pass them directly to main().
+ */
+int wmain(int argc, const wchar_t **wargv)
 {
-	int i, maxlen, argc;
-	char *buffer;
-	wchar_t **wenv, **wargv;
-	_startupinfo si;
+	int i, maxlen, exit_status;
+	char *buffer, **save;
+	const char **argv;
 
 	trace2_initialize_clock();
 
 	maybe_redirect_std_handles();
 
-	/* get wide char arguments and environment */
-	si.newmode = 0;
-	if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
-		die_startup();
-
 	/* determine size of argv and environ conversion buffer */
 	maxlen = wcslen(wargv[0]);
 	for (i = 1; i < argc; i++)
@@ -2415,9 +2413,16 @@ void mingw_startup(void)
 	maxlen = 3 * maxlen + 1;
 	buffer = malloc_startup(maxlen);
 
-	/* convert command line arguments and environment to UTF-8 */
+	/*
+	 * Create a UTF-8 version of w_argv. Also create a "save" copy
+	 * to remember all the string pointers because parse_options()
+	 * will remove claimed items from the argv that we pass down.
+	 */
+	ALLOC_ARRAY(argv, argc + 1);
+	ALLOC_ARRAY(save, argc + 1);
 	for (i = 0; i < argc; i++)
-		__argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+		argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+	argv[i] = save[i] = NULL;
 	free(buffer);
 
 	/* fix Windows specific environment settings */
@@ -2436,6 +2441,16 @@ void mingw_startup(void)
 
 	/* initialize Unicode console */
 	winansi_init();
+
+	/* invoke the real main() using our utf8 version of argv. */
+	exit_status = main(argc, argv);
+
+	for (i = 0; i < argc; i++)
+		free(save[i]);
+	free(save);
+	free(argv);
+
+	return exit_status;
 }
 
 int uname(struct utsname *buf)
diff --git a/compat/mingw.h b/compat/mingw.h
index 593bdbffe6..210f1b01a8 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -562,18 +562,18 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
 extern CRITICAL_SECTION pinfo_cs;
 
 /*
- * A replacement of main() that adds win32 specific initialization.
+ * Git, like most portable C applications, implements a main() function. On
+ * Windows, this main() function would receive parameters encoded in the
+ * current locale, but Git for Windows would prefer UTF-8 encoded  parameters.
+ *
+ * To make that happen, we still declare main() here, and then declare and
+ * implement wmain() (which is the Unicode variant of main()) and compile with
+ * -municode. This wmain() function reencodes the parameters from UTF-16 to
+ * UTF-8 format, sets up a couple of other things as required on Windows, and
+ * then hands off to the main() function.
  */
-
-void mingw_startup(void);
-#define main(c,v) dummy_decl_mingw_main(void); \
-static int mingw_main(c,v); \
-int main(int argc, const char **argv) \
-{ \
-	mingw_startup(); \
-	return mingw_main(__argc, (void *)__argv); \
-} \
-static int mingw_main(c,v)
+int wmain(int argc, const wchar_t **w_argv);
+int main(int argc, const char **argv);
 
 /*
  * Used by Pthread API implementation for Windows
diff --git a/config.mak.uname b/config.mak.uname
index 9fc053cac0..6ddece0350 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -401,7 +401,7 @@ ifeq ($(uname_S),Windows)
 		compat/win32/trace2_win32_process_info.o \
 		compat/win32/dirent.o
 	COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
-	BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
+	BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE
 	EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
 	PTHREAD_LIBS =
 	lib =
@@ -548,6 +548,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
 	ETAGS_TARGET = ETAGS
 	NO_POSIX_GOODIES = UnfortunatelyYes
 	DEFAULT_HELP_FORMAT = html
+	BASIC_LDFLAGS += -municode
 	COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
 	COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
 	COMPAT_OBJS += compat/mingw.o compat/winansi.o \
-- 
gitgitgadget
Previous: Johannes Schindelin via GitGitGadgetNext: Johannes Schindelin via GitGitGadget
Message 44 of 83 in “Fix MSVC support, at long last”
  1. 00/17 Fix MSVC support, at long lastJohannes Schindelin via GitGitGadget, Jun 18, 2019
  2. 01/17 Mark .bat files as requiring CR/LF endingsJohannes Schindelin via GitGitGadget, Jun 18, 2019
  3. 05/17 mingw: replace mingw_startup() hackJohannes Schindelin via GitGitGadget, Jun 18, 2019
  4. 03/17 cache-tree.c: avoid reusing the DEBUG constantJeff Hostetler via GitGitGadget, Jun 18, 2019
  5. Eric SunshineJun 18, 2019
  6. Johannes SchindelinJun 19, 2019
  7. Junio C HamanoJun 19, 2019
  8. Carlo ArenasJun 19, 2019
  9. Johannes SchindelinJun 19, 2019
  10. Carlo ArenasJun 19, 2019
  11. Johannes SchindelinJun 19, 2019
  12. 02/17 t0001 (mingw): do not expect a specific order of stdout/stderrJohannes Schindelin via GitGitGadget, Jun 18, 2019
  13. Eric SunshineJun 18, 2019
  14. Johannes SixtJun 19, 2019
  15. Eric SunshineJun 19, 2019
  16. Johannes SchindelinJun 19, 2019
  17. Johannes SchindelinJun 19, 2019
  18. Johannes SixtJun 19, 2019
  19. Johannes SchindelinJun 19, 2019
  20. 09/17 msvc: mark a variable as non-constJeff Hostetler via GitGitGadget, Jun 18, 2019
  21. 10/17 msvc: do not re-declare the timespec structJeff Hostetler via GitGitGadget, Jun 18, 2019
  22. 06/17 msvc: fix dependencies of compat/msvc.cJohannes Schindelin via GitGitGadget, Jun 18, 2019
  23. 07/17 msvc: include sigset_t definitionPhilip Oakley via GitGitGadget, Jun 18, 2019
  24. 08/17 msvc: define O_ACCMODEPhilip Oakley via GitGitGadget, Jun 18, 2019
  25. 04/17 obstack: fix compiler warningJohannes Schindelin via GitGitGadget, Jun 18, 2019
  26. 15/17 msvc: do not pretend to support all signalsJeff Hostetler via GitGitGadget, Jun 18, 2019
  27. Eric SunshineJun 19, 2019
  28. Johannes SchindelinJun 19, 2019
  29. 16/17 msvc: avoid debug assertion windows in Debug ModeJohannes Schindelin via GitGitGadget, Jun 18, 2019
  30. 13/17 msvc: support building Git using MS Visual C++Jeff Hostetler via GitGitGadget, Jun 18, 2019
  31. Carlo ArenasJun 19, 2019
  32. Johannes SchindelinJun 19, 2019
  33. Eric SunshineJun 19, 2019
  34. Johannes SchindelinJun 19, 2019
  35. 14/17 msvc: add pragmas for common warningsPhilip Oakley via GitGitGadget, Jun 18, 2019
  36. 12/17 msvc: fix detect_msys_tty()Jeff Hostetler via GitGitGadget, Jun 18, 2019
  37. 17/17 msvc: ignore .dll and incremental compile outputJeff Hostetler via GitGitGadget, Jun 18, 2019
  38. 11/17 msvc: define ftello()Jeff Hostetler via GitGitGadget, Jun 18, 2019
  39. 00/20 Fix MSVC support, at long lastJohannes Schindelin via GitGitGadget, Jun 19, 2019
  40. 01/20 mingw: fix a typo in the msysGit-specific sectionJohannes Schindelin via GitGitGadget, Jun 19, 2019
  41. 02/20 Mark .bat files as requiring CR/LF endingsJohannes Schindelin via GitGitGadget, Jun 19, 2019
  42. 03/20 t0001 (mingw): do not expect a specific order of stdout/stderrJohannes Schindelin via GitGitGadget, Jun 19, 2019
  43. 07/20 msvc: fix dependencies of compat/msvc.cJohannes Schindelin via GitGitGadget, Jun 19, 2019
  44. 06/20 mingw: replace mingw_startup() hackJohannes Schindelin via GitGitGadget, Jun 19, 2019
  45. 05/20 obstack: fix compiler warningJohannes Schindelin via GitGitGadget, Jun 19, 2019
  46. 09/20 msvc: define O_ACCMODEPhilip Oakley via GitGitGadget, Jun 19, 2019
  47. 10/20 msvc: mark a variable as non-constJeff Hostetler via GitGitGadget, Jun 19, 2019
  48. 12/20 msvc: define ftello()Jeff Hostetler via GitGitGadget, Jun 19, 2019
  49. 16/20 msvc: add a compile-time flag to allow detailed heap debuggingJeff Hostetler via GitGitGadget, Jun 19, 2019
  50. 17/20 msvc: add pragmas for common warningsPhilip Oakley via GitGitGadget, Jun 19, 2019
  51. 15/20 msvc: support building Git using MS Visual C++Jeff Hostetler via GitGitGadget, Jun 19, 2019
  52. Johannes SchindelinJun 21, 2019
  53. Junio C HamanoJun 21, 2019
  54. 19/20 msvc: avoid debug assertion windows in Debug ModeJohannes Schindelin via GitGitGadget, Jun 19, 2019
  55. 20/20 msvc: ignore .dll and incremental compile outputJeff Hostetler via GitGitGadget, Jun 19, 2019
  56. 13/20 msvc: fix detect_msys_tty()Jeff Hostetler via GitGitGadget, Jun 19, 2019
  57. 18/20 msvc: do not pretend to support all signalsJeff Hostetler via GitGitGadget, Jun 19, 2019
  58. 14/20 msvc: update Makefile to allow for spaces in the compiler pathJeff Hostetler via GitGitGadget, Jun 19, 2019
  59. 04/20 cache-tree/blame: avoid reusing the DEBUG constantJeff Hostetler via GitGitGadget, Jun 19, 2019
  60. 11/20 msvc: do not re-declare the timespec structJeff Hostetler via GitGitGadget, Jun 19, 2019
  61. 08/20 msvc: include sigset_t definitionPhilip Oakley via GitGitGadget, Jun 19, 2019
  62. 00/20 Fix MSVC support, at long lastJohannes Schindelin via GitGitGadget, Jun 25, 2019
  63. 01/20 mingw: fix a typo in the msysGit-specific sectionJohannes Schindelin via GitGitGadget, Jun 25, 2019
  64. 02/20 Mark .bat files as requiring CR/LF endingsJohannes Schindelin via GitGitGadget, Jun 25, 2019
  65. 03/20 t0001 (mingw): do not expect a specific order of stdout/stderrJohannes Schindelin via GitGitGadget, Jun 25, 2019
  66. 04/20 cache-tree/blame: avoid reusing the DEBUG constantJeff Hostetler via GitGitGadget, Jun 25, 2019
  67. 07/20 msvc: fix dependencies of compat/msvc.cJohannes Schindelin via GitGitGadget, Jun 25, 2019
  68. 11/20 msvc: do not re-declare the timespec structJeff Hostetler via GitGitGadget, Jun 25, 2019
  69. 10/20 msvc: mark a variable as non-constJeff Hostetler via GitGitGadget, Jun 25, 2019
  70. 13/20 msvc: fix detect_msys_tty()Jeff Hostetler via GitGitGadget, Jun 25, 2019
  71. 16/20 msvc: add a compile-time flag to allow detailed heap debuggingJeff Hostetler via GitGitGadget, Jun 25, 2019
  72. 14/20 msvc: update Makefile to allow for spaces in the compiler pathJeff Hostetler via GitGitGadget, Jun 25, 2019
  73. 18/20 msvc: do not pretend to support all signalsJeff Hostetler via GitGitGadget, Jun 25, 2019
  74. 15/20 msvc: support building Git using MS Visual C++Jeff Hostetler via GitGitGadget, Jun 25, 2019
  75. 17/20 msvc: add pragmas for common warningsPhilip Oakley via GitGitGadget, Jun 25, 2019
  76. 19/20 msvc: avoid debug assertion windows in Debug ModeJohannes Schindelin via GitGitGadget, Jun 25, 2019
  77. 20/20 msvc: ignore .dll and incremental compile outputJeff Hostetler via GitGitGadget, Jun 25, 2019
  78. 12/20 msvc: define ftello()Jeff Hostetler via GitGitGadget, Jun 25, 2019
  79. 09/20 msvc: define O_ACCMODEPhilip Oakley via GitGitGadget, Jun 25, 2019
  80. 05/20 obstack: fix compiler warningJohannes Schindelin via GitGitGadget, Jun 25, 2019
  81. 08/20 msvc: include sigset_t definitionPhilip Oakley via GitGitGadget, Jun 25, 2019
  82. 06/20 mingw: replace mingw_startup() hackJohannes Schindelin via GitGitGadget, Jun 25, 2019
  83. Junio C HamanoJun 25, 2019

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.