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>
This commit is contained in:
Johannes Schindelin 2019-06-19 14:05:59 -07:00 committed by Junio C Hamano
parent 96a0679441
commit 396ff7547d
3 changed files with 47 additions and 31 deletions

View File

@ -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)

View File

@ -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

View File

@ -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 \