crt: Deduplicate mingw-w64 stat code into __MINGW_FIXED_STAT macro

Create new function-like macro __MINGW_FIXED_STAT with same pattern as
existing __MINGW_FIXED_FSTAT macro, and extract repeated stat and wstat
code into that new __MINGW_FIXED_STAT macro.

Use gcc compound statement extension to create function-like macro and
__builtin_choose_expr to choose correct narrow or wide function based on
the passed type (char* or wchar_t*). With this patterns, the whole stat and
wstat code is deduplicated to macro functions.

This allows to extend the __MINGW_FIXED_STAT code by followup changes
without duplicating the functionality again.

Signed-off-by: LIU Hao <lh_mouse@126.com>
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h b/mingw-w64-crt/stdio/__mingw_fix_stat.h
index 1317dbf..13e4bf6 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat.h
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h
@@ -19,4 +19,27 @@
    _fstat_ret; \
 })
 
+#define __MINGW_CHOOSE_CHAR_WCHART_EXPR(var, char_expr, wchart_expr, other_expr) \
+  __builtin_choose_expr(__builtin_types_compatible_p(typeof(var), char), char_expr, \
+    __builtin_choose_expr(__builtin_types_compatible_p(typeof(var), wchar_t), wchart_expr, \
+      other_expr))
+
+#define __MINGW_PATH_PTR_TYPE(path) \
+  typeof(__MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], (char*)0, (wchar_t*)0, (void)0))
+
+#define __MINGW_FIX_STAT_PATH(path) \
+  __MINGW_CHOOSE_CHAR_WCHART_EXPR((path)[0], __mingw_fix_stat_path, __mingw_fix_wstat_path, NULL)(path)
+
+#define __MINGW_FIXED_STAT(stat_func, filename, obj) ({ \
+  int _stat_ret; \
+  __MINGW_PATH_PTR_TYPE(filename) path = __MINGW_FIX_STAT_PATH(filename); \
+  if (path == NULL && (filename) != NULL) { \
+    _stat_ret = -1; \
+  } else { \
+    _stat_ret = (stat_func)(path, (obj)); \
+    _stat_ret = __mingw_fix_stat_finish(_stat_ret, (filename), path, (obj)->st_mode); \
+  } \
+  _stat_ret; \
+})
+
 #endif
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat32.c b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
index f936ef9..400ac1f 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat32.c
@@ -11,11 +11,7 @@
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat);
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat)
 {
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat32(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_stat32, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = stat32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
index 89c3e05..299abd8 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_stat64i32.c
@@ -11,11 +11,7 @@
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat);
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat)
 {
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat64i32(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_stat64i32, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 *) = stat64i32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
index 50e183c..85291d9 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat32.c
@@ -11,11 +11,7 @@
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat);
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat)
 {
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat32(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_wstat32, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) = wstat32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
index caf5ca7..baff6a0 100644
--- a/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110plus_wstat64i32.c
@@ -11,11 +11,7 @@
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat);
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat)
 {
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat64i32(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_wstat64i32, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct _stat64i32 *) = wstat64i32;
 
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat32.c b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
index 196c2de..8650db2 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat32.c
@@ -19,11 +19,7 @@
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat)
 {
   struct _stat32i64 st;
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat32i64(_path, &st);
-  ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
+  int ret = __MINGW_FIXED_STAT(_stat32i64, _Filename, &st);
   if (ret != 0)
     return ret;
   if (st.st_size > INT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
index 2a6cc9a..d929a4f 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_stat64i32.c
@@ -19,11 +19,7 @@
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat)
 {
   struct _stat64 st;
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat64(_path, &st);
-  ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
+  int ret = __MINGW_FIXED_STAT(_stat64, _Filename, &st);
   if (ret != 0)
     return ret;
   if (st.st_size > INT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
index b9848ff..308c7f6 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat32.c
@@ -19,11 +19,7 @@
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat)
 {
   struct _stat32i64 st;
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat32i64(_path, &st);
-  ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
+  int ret = __MINGW_FIXED_STAT(_wstat32i64, _Filename, &st);
   if (ret != 0)
     return ret;
   if (st.st_size > INT32_MAX) {
diff --git a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
index a7611bf..f8ed493 100644
--- a/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
+++ b/mingw-w64-crt/stdio/msvcr110pre_wstat64i32.c
@@ -19,11 +19,7 @@
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat)
 {
   struct _stat64 st;
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat64(_path, &st);
-  ret = __mingw_fix_stat_finish(ret, _Filename, _path, st.st_mode);
+  int ret = __MINGW_FIXED_STAT(_wstat64, _Filename, &st);
   if (ret != 0)
     return ret;
   if (st.st_size > INT32_MAX) {
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
index 0b0c5cd..f8e84bf 100644
--- a/mingw-w64-crt/stdio/stat32i64.c
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -11,10 +11,6 @@
 int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat);
 int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat)
 {
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat32i64(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_stat32i64, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32i64))(const char *, struct _stat32i64 *) = stat32i64;
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
index b1f5b43..0cef610 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -10,10 +10,6 @@
 
 int __cdecl stat64(const char *_Filename, struct stat64 *_Stat)
 {
-  char *_path = __mingw_fix_stat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _stat64(_path, (struct _stat64 *)_Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_stat64, _Filename, (struct _stat64 *)_Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = stat64;
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
index 98dcaf1..d4bb87e 100644
--- a/mingw-w64-crt/stdio/wstat32i64.c
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -11,10 +11,6 @@
 int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat);
 int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat)
 {
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat32i64(_path, _Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_wstat32i64, _Filename, _Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32i64))(const wchar_t *, struct _stat32i64 *) = wstat32i64;
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
index 76d5474..0f00a4e 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -10,10 +10,6 @@
 
 int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat)
 {
-  wchar_t *_path = __mingw_fix_wstat_path(_Filename);
-  if (_path == NULL && _Filename != NULL)
-    return -1;
-  int ret = _wstat64(_path, (struct _stat64 *)_Stat);
-  return __mingw_fix_stat_finish(ret, _Filename, _path, _Stat->st_mode);
+  return __MINGW_FIXED_STAT(_wstat64, _Filename, (struct _stat64 *)_Stat);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = wstat64;