mingw: replace mingw_startup() hack

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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Johannes Schindelin committed Jun 19, 2019 at 14:05 UTC 396ff7547d8b3f6a933069f048d09380d515a10b
3 files changed +47 -31
compat/mingw.c
+34 -19
@@ -2301,18 +2301,13 @@ static void setup_windows_environment(void)
2301 setenv("TERM", "cygwin", 1);
2302 }
2303
2304 +#if !defined(_MSC_VER)
2305 /*
2306 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
2307 * mingw startup code, see init.c in mingw runtime).
2308 */
2309 int _CRT_glob = 0;
2309 -
2310 -typedef struct {
2311 - int newmode;
2312 -} _startupinfo;
2313 -
2314 -extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
2315 - _startupinfo *si);
2310 +#endif
2311
2312 static NORETURN void die_startup(void)
2313 {
@@ -2390,22 +2385,25 @@ static void maybe_redirect_std_handles(void)
2385 GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2386 }
2387
2393 -void mingw_startup(void)
2388 +/*
2389 + * We implement wmain() and compile with -municode, which would
2390 + * normally ignore main(), but we call the latter from the former
2391 + * so that we can handle non-ASCII command-line parameters
2392 + * appropriately.
2393 + *
2394 + * To be more compatible with the core git code, we convert
2395 + * argv into UTF8 and pass them directly to main().
2396 + */
2397 +int wmain(int argc, const wchar_t **wargv)
2398 {
2395 - int i, maxlen, argc;
2396 - char *buffer;
2397 - wchar_t **wenv, **wargv;
2398 - _startupinfo si;
2399 + int i, maxlen, exit_status;
2400 + char *buffer, **save;
2401 + const char **argv;
2402
2403 trace2_initialize_clock();
2404
2405 maybe_redirect_std_handles();
2406
2404 - /* get wide char arguments and environment */
2405 - si.newmode = 0;
2406 - if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
2407 - die_startup();
2408 -
2407 /* determine size of argv and environ conversion buffer */
2408 maxlen = wcslen(wargv[0]);
2409 for (i = 1; i < argc; i++)
@@ -2415,9 +2413,16 @@ void mingw_startup(void)
2413 maxlen = 3 * maxlen + 1;
2414 buffer = malloc_startup(maxlen);
2415
2418 - /* convert command line arguments and environment to UTF-8 */
2416 + /*
2417 + * Create a UTF-8 version of w_argv. Also create a "save" copy
2418 + * to remember all the string pointers because parse_options()
2419 + * will remove claimed items from the argv that we pass down.
2420 + */
2421 + ALLOC_ARRAY(argv, argc + 1);
2422 + ALLOC_ARRAY(save, argc + 1);
2423 for (i = 0; i < argc; i++)
2420 - __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
2424 + argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
2425 + argv[i] = save[i] = NULL;
2426 free(buffer);
2427
2428 /* fix Windows specific environment settings */
@@ -2436,6 +2441,16 @@ void mingw_startup(void)
2441
2442 /* initialize Unicode console */
2443 winansi_init();
2444 +
2445 + /* invoke the real main() using our utf8 version of argv. */
2446 + exit_status = main(argc, argv);
2447 +
2448 + for (i = 0; i < argc; i++)
2449 + free(save[i]);
2450 + free(save);
2451 + free(argv);
2452 +
2453 + return exit_status;
2454 }
2455
2456 int uname(struct utsname *buf)
compat/mingw.h
+11 -11
@@ -562,18 +562,18 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
562 extern CRITICAL_SECTION pinfo_cs;
563
564 /*
565 - * A replacement of main() that adds win32 specific initialization.
565 + * Git, like most portable C applications, implements a main() function. On
566 + * Windows, this main() function would receive parameters encoded in the
567 + * current locale, but Git for Windows would prefer UTF-8 encoded parameters.
568 + *
569 + * To make that happen, we still declare main() here, and then declare and
570 + * implement wmain() (which is the Unicode variant of main()) and compile with
571 + * -municode. This wmain() function reencodes the parameters from UTF-16 to
572 + * UTF-8 format, sets up a couple of other things as required on Windows, and
573 + * then hands off to the main() function.
574 */
567 -
568 -void mingw_startup(void);
569 -#define main(c,v) dummy_decl_mingw_main(void); \
570 -static int mingw_main(c,v); \
571 -int main(int argc, const char **argv) \
572 -{ \
573 - mingw_startup(); \
574 - return mingw_main(__argc, (void *)__argv); \
575 -} \
576 -static int mingw_main(c,v)
575 +int wmain(int argc, const wchar_t **w_argv);
576 +int main(int argc, const char **argv);
577
578 /*
579 * Used by Pthread API implementation for Windows
config.mak.uname
+2 -1
@@ -401,7 +401,7 @@ ifeq ($(uname_S),Windows)
401 compat/win32/trace2_win32_process_info.o \
402 compat/win32/dirent.o
403 COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -Icompat -Icompat/regex -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
404 - BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE
404 + BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -ENTRY:wmainCRTStartup -SUBSYSTEM:CONSOLE
405 EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
406 PTHREAD_LIBS =
407 lib =
@@ -548,6 +548,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
548 ETAGS_TARGET = ETAGS
549 NO_POSIX_GOODIES = UnfortunatelyYes
550 DEFAULT_HELP_FORMAT = html
551 + BASIC_LDFLAGS += -municode
552 COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
553 COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
554 COMPAT_OBJS += compat/mingw.o compat/winansi.o \