Skip to content

Commit 396ff75

Browse files
dschogitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 96a0679 commit 396ff75

File tree

3 files changed

+47
-31
lines changed

3 files changed

+47
-31
lines changed

compat/mingw.c

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2301,18 +2301,13 @@ static void setup_windows_environment(void)
23012301
setenv("TERM", "cygwin", 1);
23022302
}
23032303

2304+
#if !defined(_MSC_VER)
23042305
/*
23052306
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
23062307
* mingw startup code, see init.c in mingw runtime).
23072308
*/
23082309
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
23162311

23172312
static NORETURN void die_startup(void)
23182313
{
@@ -2390,22 +2385,25 @@ static void maybe_redirect_std_handles(void)
23902385
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
23912386
}
23922387

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)
23942398
{
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;
23992402

24002403
trace2_initialize_clock();
24012404

24022405
maybe_redirect_std_handles();
24032406

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-
24092407
/* determine size of argv and environ conversion buffer */
24102408
maxlen = wcslen(wargv[0]);
24112409
for (i = 1; i < argc; i++)
@@ -2415,9 +2413,16 @@ void mingw_startup(void)
24152413
maxlen = 3 * maxlen + 1;
24162414
buffer = malloc_startup(maxlen);
24172415

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);
24192423
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;
24212426
free(buffer);
24222427

24232428
/* fix Windows specific environment settings */
@@ -2436,6 +2441,16 @@ void mingw_startup(void)
24362441

24372442
/* initialize Unicode console */
24382443
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;
24392454
}
24402455

24412456
int uname(struct utsname *buf)

compat/mingw.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -562,18 +562,18 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
562562
extern CRITICAL_SECTION pinfo_cs;
563563

564564
/*
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.
566574
*/
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);
577577

578578
/*
579579
* Used by Pthread API implementation for Windows

config.mak.uname

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ ifeq ($(uname_S),Windows)
401401
compat/win32/trace2_win32_process_info.o \
402402
compat/win32/dirent.o
403403
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
405405
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
406406
PTHREAD_LIBS =
407407
lib =
@@ -548,6 +548,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
548548
ETAGS_TARGET = ETAGS
549549
NO_POSIX_GOODIES = UnfortunatelyYes
550550
DEFAULT_HELP_FORMAT = html
551+
BASIC_LDFLAGS += -municode
551552
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
552553
COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
553554
COMPAT_OBJS += compat/mingw.o compat/winansi.o \

0 commit comments

Comments
 (0)