On 2025-03-20 Pali Rohár wrote:
> On Wednesday 12 March 2025 16:33:42 Lasse Collin wrote:
> > stat.h has "struct stat *" and stat64i32.c has "struct _stat64i32
> > *". On Linux and "clang -fsanitize=function", this kind of argument
> > type mismatch doesn't work if the function is called indirectly
> > using a function pointer. It might not be common to take the
> > address of stat, but it should still work. (I didn't test how this
> > works on Windows, sorry.)  
> 
> __MINGW_ASM_CALL is just a function symbol redirection. I used
> __MINGW_ASM_CALL specially for making aliases of those functions as
> they have same ABI.
> 
> Basically whatever function you call (on 64-bit) the result should be
> exactly same and I think that also pointers for those functions should
> be same.
> 
> Is clang really complaining?

I verified in MSYS2's CLANG64 environment that -fsanitize=function and
-fsanitize=undefined do complain on Windows too.

file1.c:

    struct foo { int x; };
    int foo(struct foo *f);

    int main(void)
    {
        struct foo f = { 5 };
        int (*cb)(struct foo *f) = &foo;
        return cb(&f);
    }

file2.c:

    struct wrongname { int x; };

    int foo(struct wrongname *f)
    {
        return f->x;
    }

Testing:

   $ clang -O2 -fsanitize=undefined file1.c file2.c
   $ ./a
   file1.c:8:12: runtime error: call to function foo through pointer
   to incorrect function type 'int (*)(struct foo *)'
   (C:\msys64\home\....): note: foo defined here SUMMARY:
   UndefinedBehaviorSanitizer: undefined-behavior file1.c:8:12

If I build with "clang -O2 -fsanitize=cfi -flto" then the program
terminates with an illegal instruction instead.

I don't know if the above needs to work with the functions provided by
mingw-w64, so me mentioning this may have been a distraction. If only
file1.c is built with -fsanitize=undefined, there's no problem (file1.c
could be user's app being sanitized and file2.c could represent
mingw-w64's stat function).

> > Otherwise I suspect that extra code is needed. For example, stat32.c
> > could use memcpy to convert struct _stat32 to struct stat like
> > mingw-w64-crt/stdio/_stat.c does in the current mingw-w64 code.
> > These wrappers could set _TIME_BITS and _FILE_OFFSET_BITS to get
> > the correct copy of struct stat.  
> 
> I think that it is stupid to use memcpy for cases which are 1:1 ABI
> binary compatible.

I agree it's stupid. As long as the type name mismatch is at a
translation unit boundary, strict aliasing shouldn't cause issues,
at least without -flto. Maybe I'm overcautious with this kind of
things (sometimes a trick has worked at first and trouble has appeared
much later).

Since you feel OK with the current method, maybe proceed with the
current patches and fix things later if problems actually appear in
reality. I made the ftw patch with this assumption.

> Also those aliases in def files are doing same thing. Just marking
> symbols as are ABI binary compatible.

-flto's hand doesn't reach into the DLLs. Thus, I don't think those
redirects can cause strict aliasing problems.

> We had already start to use __MINGW_ASM_CALL for symbol redirection
> instead of conditional #ifdef or inline functions, because of C++23
> exports and C++ inline mechanism (which differs from C). It is needed
> that emitted symbol in every object file has same meaning. And so,
> the __MINGW_ASM_CALL is doing it.

OK. :-) I did <ftw.h> similarly to your <sys/stat.h> changes.

> > (3)
> > fstat, stat, and wstat aren't declared in <sys/stat.h> when
> > __CRT__NO_INLINE is defined, thus those declarations are missing if
> > compiling without optimizations. I changed those to _CRTBLD, but
> > this broke ftw.c (not ftw64.c). <ftw.h> uses "struct stat" and
> > "struct stat64". I almost fixed the ftw issue, but I didn't finish
> > because figuring out the stat redirects should be finished first
> > (*if* they need to be changed).  
> 
> I know about this issue and that is why I defined __CRT__NO_INLINE. As
> this problem is already there, it could be fixed later.

OK, sorry. I had thought that the misuse of __CRT__NO_INLINE in
<sys/stat.h> was a somewhat severe bug, and fixing it as a side effect
could have been easy enough here.

In <ftw.h> I used __CRT_BUILDING_FTW. It can be renamed, of course. A
separate macro for <ftw.h> is convenient because building ftw*.c
depends on the seeing the correct stat struct and function from
<sys/stat.h>.

> I will include your first change directly into my patches as it is
> basically fixup of one of my change.

The attached patches are on top of your new patches. Differences to the
previous round:

  - No longer fix function name in __mingw_fix_wstat_path.c. Only fix
    const correctness.

  - Drop __CRT__NO_INLINE changes.

  - Add stdio/__mingw_fix_stat.h so that the internal function
    declarations don't need to be copied to many files.

  - In "Don't remove a trailing '\' if it is a DBCS trail byte", move
    the loop so that it's only run when there is a trailing (back)slash.

  - Add the ftw patch.

  - Drop S_IFBLK patch because an equivalent patch was just merged. :-)

Things I noticed about your patches but I didn't make any changes:

  - With MSVCRT, _stat32 sets timestamps to -1 if the time doesn't fit
    into 32-bit time_t. The 32i64 wrappers truncate the timestamps
    instead. Maybe this doesn't matter, but I mention it in case you
    think it does. With file size it's different anyway: _stat32 uses a
    truncated st_size if file is too large.

  - The new file mingw-w64-crt/include/filetime_to_time64.h isn't listed
    in mingw-w64-crt/Makefile.am. It should be if "make dist" needs to
    work.

  - Commit message typos:

      * "Additionaly define struct stat64 for LFS"
         Additionally

      * "Function _time64 is avaulable since msvcr70.dll."
                             available

Thanks!

-- 
Lasse Collin
>From bd751dacc32ed92d21dc8e4919c8cf753298be3d Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 1/9] crt: __mingw_fix_wstat_path: Fix const correctness

---
 mingw-w64-crt/stdio/__mingw_fix_wstat_path.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
index 0984cace4..aa0c2e1a5 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
@@ -18,8 +18,8 @@
  *   to free it.
  */
 
-wchar_t* __mingw_fix_wstat_path (wchar_t* _path);
-wchar_t* __mingw_fix_wstat_path (wchar_t* _path)
+wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
+wchar_t* __mingw_fix_wstat_path (const wchar_t* _path)
 {
   int len;
   wchar_t *p;
-- 
2.49.0

>From fc3a5bc1954b365c7fa8c4e903835474961b009b Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 2/9] crt: stat: Put internal function declarations to
 stdio/__mingw_fix_stat.h

---
 mingw-w64-crt/Makefile.am                    | 1 +
 mingw-w64-crt/stdio/__mingw_fix_stat.h       | 8 ++++++++
 mingw-w64-crt/stdio/__mingw_fix_stat_path.c  | 2 +-
 mingw-w64-crt/stdio/__mingw_fix_wstat_path.c | 2 +-
 mingw-w64-crt/stdio/stat32.c                 | 3 +--
 mingw-w64-crt/stdio/stat32i64.c              | 3 +--
 mingw-w64-crt/stdio/stat64.c                 | 3 +--
 mingw-w64-crt/stdio/stat64i32.c              | 3 +--
 mingw-w64-crt/stdio/wstat32.c                | 3 +--
 mingw-w64-crt/stdio/wstat32i64.c             | 3 +--
 mingw-w64-crt/stdio/wstat64.c                | 3 +--
 mingw-w64-crt/stdio/wstat64i32.c             | 3 +--
 12 files changed, 19 insertions(+), 18 deletions(-)
 create mode 100644 mingw-w64-crt/stdio/__mingw_fix_stat.h

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 4355aac6f..b373d5e24 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1045,6 +1045,7 @@ src_libmingwex=\
   stdio/asprintf.c \
   stdio/fopen64.c          stdio/fseeko32.c          stdio/fseeko64.c          
                         stdio/ftello.c          \
   stdio/ftello64.c         stdio/ftruncate64.c       stdio/lltoa.c            
stdio/lltow.c             stdio/lseek64.c         \
+  stdio/__mingw_fix_stat.h \
   stdio/__mingw_fix_stat_path.c stdio/__mingw_fix_wstat_path.c \
   \
   stdio/mingw_pformat.h    mingw_sformat.h           mingw_swformat.h \
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h 
b/mingw-w64-crt/stdio/__mingw_fix_stat.h
new file mode 100644
index 000000000..05d7552ab
--- /dev/null
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h
@@ -0,0 +1,8 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+char* __mingw_fix_stat_path (const char* _path);
+wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
index 1efb3fea3..dfa44874e 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
@@ -7,6 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
+#include "__mingw_fix_stat.h"
 
 /**
  * Returns _path without trailing slash if any
@@ -18,7 +19,6 @@
  *   to free it.
  */
 
-char* __mingw_fix_stat_path (const char* _path);
 char* __mingw_fix_stat_path (const char* _path)
 {
   int len;
diff --git a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
index aa0c2e1a5..372a1878d 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
@@ -7,6 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
+#include "__mingw_fix_stat.h"
 
 /**
  * Returns _path without trailing slash if any
@@ -18,7 +19,6 @@
  *   to free it.
  */
 
-wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
 wchar_t* __mingw_fix_wstat_path (const wchar_t* _path)
 {
   int len;
diff --git a/mingw-w64-crt/stdio/stat32.c b/mingw-w64-crt/stdio/stat32.c
index be3e4694b..b2d9f3d50 100644
--- a/mingw-w64-crt/stdio/stat32.c
+++ b/mingw-w64-crt/stdio/stat32.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-char *__mingw_fix_stat_path(const char *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat);
 int __cdecl stat32(const char *_Filename, struct _stat32 *_Stat)
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
index f853d7a3f..fa81c39ac 100644
--- a/mingw-w64-crt/stdio/stat32i64.c
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-char *__mingw_fix_stat_path(const char *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat);
 int __cdecl stat32i64(const char *_Filename, struct _stat32i64 *_Stat)
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
index 096bae132..7e2446aeb 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-char *__mingw_fix_stat_path(const char *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl stat64(const char *_Filename, struct stat64 *_Stat)
 {
diff --git a/mingw-w64-crt/stdio/stat64i32.c b/mingw-w64-crt/stdio/stat64i32.c
index 66f069722..3e49542de 100644
--- a/mingw-w64-crt/stdio/stat64i32.c
+++ b/mingw-w64-crt/stdio/stat64i32.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-char *__mingw_fix_stat_path(const char *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat);
 int __cdecl stat64i32(const char *_Filename, struct _stat64i32 *_Stat)
diff --git a/mingw-w64-crt/stdio/wstat32.c b/mingw-w64-crt/stdio/wstat32.c
index 41e3b1c85..f1af85e4a 100644
--- a/mingw-w64-crt/stdio/wstat32.c
+++ b/mingw-w64-crt/stdio/wstat32.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat);
 int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 *_Stat)
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
index 15acacb53..e90d824d0 100644
--- a/mingw-w64-crt/stdio/wstat32i64.c
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat);
 int __cdecl wstat32i64(const wchar_t *_Filename, struct _stat32i64 *_Stat)
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
index 399efcf72..2faf21fd2 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat)
 {
diff --git a/mingw-w64-crt/stdio/wstat64i32.c b/mingw-w64-crt/stdio/wstat64i32.c
index 5821cc464..ed419fdd2 100644
--- a/mingw-w64-crt/stdio/wstat64i32.c
+++ b/mingw-w64-crt/stdio/wstat64i32.c
@@ -7,8 +7,7 @@
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
-
-wchar_t *__mingw_fix_wstat_path(const wchar_t *_path);
+#include "__mingw_fix_stat.h"
 
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat);
 int __cdecl wstat64i32(const wchar_t *_Filename, struct _stat64i32 *_Stat)
-- 
2.49.0

>From 0e0bd56fd57af92c05e2e09f6e7a88a7c3d99ec0 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 3/9] crt: __mingw_fix_stat_path: Use size_t instead of int

---
 mingw-w64-crt/stdio/__mingw_fix_stat_path.c  | 2 +-
 mingw-w64-crt/stdio/__mingw_fix_wstat_path.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
index dfa44874e..11e456504 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
@@ -21,7 +21,7 @@
 
 char* __mingw_fix_stat_path (const char* _path)
 {
-  int len;
+  size_t len;
   char *p;
 
   p = (char*)_path;
diff --git a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
index 372a1878d..e497fedb6 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
@@ -21,7 +21,7 @@
 
 wchar_t* __mingw_fix_wstat_path (const wchar_t* _path)
 {
-  int len;
+  size_t len;
   wchar_t *p;
 
   p = (wchar_t*)_path;
-- 
2.49.0

>From 13e7d5f85d9af259a53934f0201c08cc1a52dc10 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 4/9] crt: stat: Check for malloc failure

---
 mingw-w64-crt/stdio/__mingw_fix_stat_path.c  | 2 ++
 mingw-w64-crt/stdio/__mingw_fix_wstat_path.c | 2 ++
 mingw-w64-crt/stdio/stat32.c                 | 2 ++
 mingw-w64-crt/stdio/stat32i64.c              | 2 ++
 mingw-w64-crt/stdio/stat64.c                 | 2 ++
 mingw-w64-crt/stdio/stat64i32.c              | 2 ++
 mingw-w64-crt/stdio/wstat32.c                | 2 ++
 mingw-w64-crt/stdio/wstat32i64.c             | 2 ++
 mingw-w64-crt/stdio/wstat64.c                | 2 ++
 mingw-w64-crt/stdio/wstat64i32.c             | 2 ++
 10 files changed, 20 insertions(+)

diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
index 11e456504..516f1a98d 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
@@ -56,6 +56,8 @@ char* __mingw_fix_stat_path (const char* _path)
     if (_path[len - 1] == '/' || _path[len - 1] == '\\')
       {
        p = (char*)malloc (len);
+       if (p == NULL)
+         return NULL; /* malloc has set errno. */
        memcpy (p, _path, len - 1);
        p[len - 1] = '\0';
       }
diff --git a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
index e497fedb6..fb7408f42 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_wstat_path.c
@@ -56,6 +56,8 @@ wchar_t* __mingw_fix_wstat_path (const wchar_t* _path)
     if (_path[len - 1] == L'/' || _path[len - 1] == L'\\')
       {
        p = (wchar_t*)malloc (len * sizeof(wchar_t));
+       if (p == NULL)
+         return NULL; /* malloc has set errno. */
        memcpy (p, _path, (len - 1) * sizeof(wchar_t));
        p[len - 1] = L'\0';
       }
diff --git a/mingw-w64-crt/stdio/stat32.c b/mingw-w64-crt/stdio/stat32.c
index b2d9f3d50..156f5f6bd 100644
--- a/mingw-w64-crt/stdio/stat32.c
+++ b/mingw-w64-crt/stdio/stat32.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _stat32(_path, _Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
index fa81c39ac..2ebc61a5d 100644
--- a/mingw-w64-crt/stdio/stat32i64.c
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _stat32i64(_path, _Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/stat64.c b/mingw-w64-crt/stdio/stat64.c
index 7e2446aeb..c150ff87b 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -12,6 +12,8 @@
 int __cdecl stat64(const char *_Filename, struct stat64 *_Stat)
 {
   char *_path = __mingw_fix_stat_path(_Filename);
+  if (_path == NULL)
+    return -1;
   int ret = _stat64(_path, (struct _stat64 *)_Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/stat64i32.c b/mingw-w64-crt/stdio/stat64i32.c
index 3e49542de..9a6838680 100644
--- a/mingw-w64-crt/stdio/stat64i32.c
+++ b/mingw-w64-crt/stdio/stat64i32.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _stat64i32(_path, _Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/wstat32.c b/mingw-w64-crt/stdio/wstat32.c
index f1af85e4a..6ef7a325f 100644
--- a/mingw-w64-crt/stdio/wstat32.c
+++ b/mingw-w64-crt/stdio/wstat32.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _wstat32(_path, _Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
index e90d824d0..e7ee5b0b8 100644
--- a/mingw-w64-crt/stdio/wstat32i64.c
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _wstat32i64(_path, _Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/wstat64.c b/mingw-w64-crt/stdio/wstat64.c
index 2faf21fd2..03dea9856 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -12,6 +12,8 @@
 int __cdecl wstat64(const wchar_t *_Filename, struct stat64 *_Stat)
 {
   wchar_t *_path = __mingw_fix_wstat_path(_Filename);
+  if (_path == NULL)
+    return -1;
   int ret = _wstat64(_path, (struct _stat64 *)_Stat);
   if (_path != _Filename)
     free(_path);
diff --git a/mingw-w64-crt/stdio/wstat64i32.c b/mingw-w64-crt/stdio/wstat64i32.c
index ed419fdd2..813d96a03 100644
--- a/mingw-w64-crt/stdio/wstat64i32.c
+++ b/mingw-w64-crt/stdio/wstat64i32.c
@@ -13,6 +13,8 @@ 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)
+    return -1;
   int ret = _wstat64i32(_path, _Stat);
   if (_path != _Filename)
     free(_path);
-- 
2.49.0

>From 78bf50659af78adb1aabd50e1214ade1a61059d3 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 5/9] crt: stat: Fail if the pathname ends with a slash and
 isn't a directory

POSIX requires stat("foo/", &st) to fail if foo isn't a directory or
a symbolic link to a directory.

Preserve errno when calling free().
---
 mingw-w64-crt/Makefile.am                     |  2 +-
 mingw-w64-crt/stdio/__mingw_fix_stat.h        |  2 ++
 mingw-w64-crt/stdio/__mingw_fix_stat_common.c | 34 +++++++++++++++++++
 mingw-w64-crt/stdio/stat32.c                  |  4 +--
 mingw-w64-crt/stdio/stat32i64.c               |  4 +--
 mingw-w64-crt/stdio/stat64.c                  |  4 +--
 mingw-w64-crt/stdio/stat64i32.c               |  4 +--
 mingw-w64-crt/stdio/wstat32.c                 |  4 +--
 mingw-w64-crt/stdio/wstat32i64.c              |  4 +--
 mingw-w64-crt/stdio/wstat64.c                 |  4 +--
 mingw-w64-crt/stdio/wstat64i32.c              |  4 +--
 11 files changed, 45 insertions(+), 25 deletions(-)
 create mode 100644 mingw-w64-crt/stdio/__mingw_fix_stat_common.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index b373d5e24..8c66a691e 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1045,7 +1045,7 @@ src_libmingwex=\
   stdio/asprintf.c \
   stdio/fopen64.c          stdio/fseeko32.c          stdio/fseeko64.c          
                         stdio/ftello.c          \
   stdio/ftello64.c         stdio/ftruncate64.c       stdio/lltoa.c            
stdio/lltow.c             stdio/lseek64.c         \
-  stdio/__mingw_fix_stat.h \
+  stdio/__mingw_fix_stat.h stdio/__mingw_fix_stat_common.c \
   stdio/__mingw_fix_stat_path.c stdio/__mingw_fix_wstat_path.c \
   \
   stdio/mingw_pformat.h    mingw_sformat.h           mingw_swformat.h \
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat.h 
b/mingw-w64-crt/stdio/__mingw_fix_stat.h
index 05d7552ab..23efd4f93 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat.h
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat.h
@@ -4,5 +4,7 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
+int __mingw_fix_stat_common(int ret, const void *orig_path, void *used_path,
+                            unsigned short mode);
 char* __mingw_fix_stat_path (const char* _path);
 wchar_t* __mingw_fix_wstat_path (const wchar_t* _path);
diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_common.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_common.c
new file mode 100644
index 000000000..6a4382e7e
--- /dev/null
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_common.c
@@ -0,0 +1,34 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "__mingw_fix_stat.h"
+
+int __mingw_fix_stat_common(int ret, const void *orig_path, void *used_path,
+                            unsigned short mode)
+{
+    // If the original pathname and used pathname differ, it means that
+    // __mingw_fix_stat_path or __mingw_fix_wstat_path had to allocate
+    // a temporary buffer and remove a trailing directory separator.
+    // In this case the temporary allocation has to be freed, and the
+    // stat function succeeds only if the pathname was a directory.
+    if (orig_path != used_path) {
+        // Save errno because we call free.
+        int saved_errno = errno;
+        free(used_path);
+
+        if (ret == 0 && !S_ISDIR(mode)) {
+            ret = -1;
+            saved_errno = ENOTDIR;
+        }
+
+        errno = saved_errno;
+    }
+
+    return ret;
+}
diff --git a/mingw-w64-crt/stdio/stat32.c b/mingw-w64-crt/stdio/stat32.c
index 156f5f6bd..2e8f68c7d 100644
--- a/mingw-w64-crt/stdio/stat32.c
+++ b/mingw-w64-crt/stdio/stat32.c
@@ -16,9 +16,7 @@ int __cdecl stat32(const char *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL)
     return -1;
   int ret = _stat32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat32))(const char *, struct _stat32 *) = 
stat32;
 
diff --git a/mingw-w64-crt/stdio/stat32i64.c b/mingw-w64-crt/stdio/stat32i64.c
index 2ebc61a5d..5ece01cea 100644
--- a/mingw-w64-crt/stdio/stat32i64.c
+++ b/mingw-w64-crt/stdio/stat32i64.c
@@ -16,8 +16,6 @@ int __cdecl stat32i64(const char *_Filename, struct 
_stat32i64 *_Stat)
   if (_path == NULL)
     return -1;
   int ret = _stat32i64(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 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 c150ff87b..50f05cba3 100644
--- a/mingw-w64-crt/stdio/stat64.c
+++ b/mingw-w64-crt/stdio/stat64.c
@@ -15,8 +15,6 @@ int __cdecl stat64(const char *_Filename, struct stat64 
*_Stat)
   if (_path == NULL)
     return -1;
   int ret = _stat64(_path, (struct _stat64 *)_Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64))(const char *, struct stat64 *) = 
stat64;
diff --git a/mingw-w64-crt/stdio/stat64i32.c b/mingw-w64-crt/stdio/stat64i32.c
index 9a6838680..8786b2164 100644
--- a/mingw-w64-crt/stdio/stat64i32.c
+++ b/mingw-w64-crt/stdio/stat64i32.c
@@ -16,9 +16,7 @@ int __cdecl stat64i32(const char *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL)
     return -1;
   int ret = _stat64i32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(stat64i32))(const char *, struct _stat64i32 
*) = stat64i32;
 
diff --git a/mingw-w64-crt/stdio/wstat32.c b/mingw-w64-crt/stdio/wstat32.c
index 6ef7a325f..a0ce12661 100644
--- a/mingw-w64-crt/stdio/wstat32.c
+++ b/mingw-w64-crt/stdio/wstat32.c
@@ -16,9 +16,7 @@ int __cdecl wstat32(const wchar_t *_Filename, struct _stat32 
*_Stat)
   if (_path == NULL)
     return -1;
   int ret = _wstat32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat32))(const wchar_t *, struct _stat32 *) 
= wstat32;
 
diff --git a/mingw-w64-crt/stdio/wstat32i64.c b/mingw-w64-crt/stdio/wstat32i64.c
index e7ee5b0b8..9fd4e6a2e 100644
--- a/mingw-w64-crt/stdio/wstat32i64.c
+++ b/mingw-w64-crt/stdio/wstat32i64.c
@@ -16,8 +16,6 @@ int __cdecl wstat32i64(const wchar_t *_Filename, struct 
_stat32i64 *_Stat)
   if (_path == NULL)
     return -1;
   int ret = _wstat32i64(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 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 03dea9856..d4ef5d7eb 100644
--- a/mingw-w64-crt/stdio/wstat64.c
+++ b/mingw-w64-crt/stdio/wstat64.c
@@ -15,8 +15,6 @@ int __cdecl wstat64(const wchar_t *_Filename, struct stat64 
*_Stat)
   if (_path == NULL)
     return -1;
   int ret = _wstat64(_path, (struct _stat64 *)_Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64))(const wchar_t *, struct stat64 *) = 
wstat64;
diff --git a/mingw-w64-crt/stdio/wstat64i32.c b/mingw-w64-crt/stdio/wstat64i32.c
index 813d96a03..08307cc28 100644
--- a/mingw-w64-crt/stdio/wstat64i32.c
+++ b/mingw-w64-crt/stdio/wstat64i32.c
@@ -16,9 +16,7 @@ int __cdecl wstat64i32(const wchar_t *_Filename, struct 
_stat64i32 *_Stat)
   if (_path == NULL)
     return -1;
   int ret = _wstat64i32(_path, _Stat);
-  if (_path != _Filename)
-    free(_path);
-  return ret;
+  return __mingw_fix_stat_common(ret, _Filename, _path, _Stat->st_mode);
 }
 int (__cdecl *__MINGW_IMP_SYMBOL(wstat64i32))(const wchar_t *, struct 
_stat64i32 *) = wstat64i32;
 
-- 
2.49.0

>From 073ac0bdddfc7a41d54ef977bc3e30fd5ca6b4fa Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 6/9] crt: Add __mingw_filename_cp()

It returns the code page that the CRT currently uses for filenames.
With UCRT, setlocale(LC_ALL, ".UTF-8") makes the CRT use UTF-8
for filenames, ignoring CP_ACP and CP_OEMCP. Such a setlocale() call
can make the WinAPI filename code page differ from the code page
used by the CRT.

Otherwise the CRT and WinAPI use the same code page which is either
CP_ACP or CP_OEMCP. The latter is used if SetFileApisToOEM has been
called.
---
 mingw-w64-crt/Makefile.am                |  1 +
 mingw-w64-crt/misc/__mingw_filename_cp.c | 18 ++++++++++++++++++
 mingw-w64-headers/crt/locale.h           |  3 +++
 3 files changed, 22 insertions(+)
 create mode 100644 mingw-w64-crt/misc/__mingw_filename_cp.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 8c66a691e..5fcde4a6d 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1034,6 +1034,7 @@ src_libmingwex=\
   misc/wdirent.c         misc/winbs_uint64.c        misc/winbs_ulong.c      
misc/winbs_ushort.c    \
   misc/wmemchr.c         misc/wmemcmp.c             misc/wmemcpy.c          
misc/wmemmove.c              misc/wmempcpy.c        \
   misc/wmemset.c         misc/ftw.c                 misc/ftw64.c            
misc/mingw-access.c          \
+  misc/__mingw_filename_cp.c \
   \
   ssp/chk_fail.c         ssp/gets_chk.c             ssp/memcpy_chk.c        
ssp/memmove_chk.c \
   ssp/mempcpy_chk.c \
diff --git a/mingw-w64-crt/misc/__mingw_filename_cp.c 
b/mingw-w64-crt/misc/__mingw_filename_cp.c
new file mode 100644
index 000000000..2be1a4c31
--- /dev/null
+++ b/mingw-w64-crt/misc/__mingw_filename_cp.c
@@ -0,0 +1,18 @@
+/**
+ * This file has no copyright assigned and is placed in the Public Domain.
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER.PD within this package.
+ */
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+#include <locale.h>
+
+unsigned int __cdecl __mingw_filename_cp(void)
+{
+    return (___lc_codepage_func() == CP_UTF8)
+           ? CP_UTF8
+           : AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+}
diff --git a/mingw-w64-headers/crt/locale.h b/mingw-w64-headers/crt/locale.h
index 9b695e333..66a1cd9ac 100644
--- a/mingw-w64-headers/crt/locale.h
+++ b/mingw-w64-headers/crt/locale.h
@@ -98,6 +98,9 @@ extern "C" {
 
   _CRTIMP unsigned int __cdecl ___lc_codepage_func(void);
 
+  /* Get the code page that the CRT currently uses for filenames. */
+  unsigned int __cdecl __mingw_filename_cp(void);
+
 #ifndef _WLOCALE_DEFINED
 #define _WLOCALE_DEFINED
   _CRTIMP wchar_t *__cdecl _wsetlocale(int _Category,const wchar_t *_Locale);
-- 
2.49.0

>From 635a9ddab918d470698f1eebb862c7e8f84d7fc7 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 7/9] crt: stat: Don't remove a trailing '\' if it is a DBCS
 trail byte

In double-byte character sets, the trail byte of a two-byte character
can be a backslash. If such a two-byte character was at the end of
the pathname, the trailing backslash was incorrectly removed.

The code still removes only one trailing directory separator and thus
stat("directory//", &st) still incorrectly fails with MSVCRT. This
commit only fixes the DBCS issue.
---
 mingw-w64-crt/stdio/__mingw_fix_stat_path.c | 36 ++++++++++++++++++---
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c 
b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
index 516f1a98d..9c79dd586 100644
--- a/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
+++ b/mingw-w64-crt/stdio/__mingw_fix_stat_path.c
@@ -4,11 +4,24 @@
  * No warranty is given; refer to the file DISCLAIMER.PD within this package.
  */
 
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
 #define __CRT__NO_INLINE
 #include <sys/stat.h>
 #include <stdlib.h>
+#include <locale.h>
+#include <windows.h>
 #include "__mingw_fix_stat.h"
 
+static const char *
+next_char(unsigned int cp, const char *p)
+{
+  /* If it is a lead byte, skip the next byte except if it is \0.
+   * If it is \0, it's not a valid DBCS string. */
+  return (IsDBCSLeadByteEx(cp, *p) && p[1] != '\0') ? p + 2 : p + 1;
+}
+
 /**
  * Returns _path without trailing slash if any
  *
@@ -21,6 +34,7 @@
 
 char* __mingw_fix_stat_path (const char* _path)
 {
+  const unsigned int cp = __mingw_filename_cp();
   size_t len;
   char *p;
 
@@ -29,24 +43,27 @@ char* __mingw_fix_stat_path (const char* _path)
   if (_path && *_path) {
     len = strlen (_path);
 
-    /* Ignore X:\ */
-
+    /* Ignore X:\
+     * No ANSI or OEM code page uses ':' as a trail byte. (The code page 1361
+     * cannot be used as ANSI or OEM code page.) */
     if (len <= 1 || ((len == 2 || len == 3) && _path[1] == ':'))
       return p;
 
+    const char *r = _path;
+
     /* Check UNC \\abc\<name>\ */
     if ((_path[0] == '\\' || _path[0] == '/')
        && (_path[1] == '\\' || _path[1] == '/'))
       {
-       const char *r = &_path[2];
+       r = &_path[2];
        while (*r != 0 && *r != '\\' && *r != '/')
-         ++r;
+         r = next_char(cp, r);
        if (*r != 0)
          ++r;
        if (*r == 0)
          return p;
        while (*r != 0 && *r != '\\' && *r != '/')
-         ++r;
+         r = next_char(cp, r);
        if (*r != 0)
          ++r;
        if (*r == 0)
@@ -55,6 +72,15 @@ char* __mingw_fix_stat_path (const char* _path)
 
     if (_path[len - 1] == '/' || _path[len - 1] == '\\')
       {
+       /* Return if the last character is a double-byte character.
+        * Its trail byte could be a '\' which must not be interpret
+        * as a directory separator. */
+       while (r[1] != '\0') {
+         r = next_char(cp, r);
+         if (*r == '\0')
+           return p;
+       }
+
        p = (char*)malloc (len);
        if (p == NULL)
          return NULL; /* malloc has set errno. */
-- 
2.49.0

>From 84ac63bf8f47d8e34f8f8be6fe9ffab17478bf4b Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 8/9] crt: dirname: Use __mingw_filename_cp

This way it works even if setlocale(LC_ALL, ".UTF8") has been used
with UCRT. IsDBCSLeadByteEx(CP_UTF8, x) always returns FALSE, so
nothing else in dirname.c needs to be modified.
---
 mingw-w64-crt/misc/dirname.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mingw-w64-crt/misc/dirname.c b/mingw-w64-crt/misc/dirname.c
index 7e6c8d58a..239d5db8d 100644
--- a/mingw-w64-crt/misc/dirname.c
+++ b/mingw-w64-crt/misc/dirname.c
@@ -7,6 +7,7 @@
 #define WIN32_LEAN_AND_MEAN
 #endif
 #include <stdlib.h>
+#include <locale.h>
 #include <libgen.h>
 #include <windows.h>
 
@@ -91,7 +92,7 @@ do_get_path_info(struct path_info* info, char* path)
     int dbcs_tb, prev_dir_sep, dir_sep;
 
     /* Get the code page for paths in the same way as `fopen()`.  */
-    cp = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+    cp = __mingw_filename_cp();
 
     /* Set the structure to 'no data'.  */
     info->prefix_end = NULL;
-- 
2.49.0

>From aed782870873fe15b0c1d31a7fbfb7687f171985 Mon Sep 17 00:00:00 2001
From: Lasse Collin <lasse.col...@tukaani.org>
Date: Sat, 22 Mar 2025 14:42:12 +0200
Subject: [PATCH 9/9] crt: ftw: Support _FILE_OFFSET_BITS and _USE_32BIT_TIME_T

This is to match the changes in <sys/stat.h>.
---
 mingw-w64-crt/Makefile.am     |  4 +++-
 mingw-w64-crt/misc/ftw.c      | 11 +++++------
 mingw-w64-crt/misc/ftw32.c    | 27 +++++++++++++++++++++++++++
 mingw-w64-crt/misc/ftw32i64.c | 16 ++++++++++++++++
 mingw-w64-crt/misc/ftw64.c    |  6 +++++-
 mingw-w64-crt/misc/ftw64i32.c | 23 +++++++++++++++++++++++
 mingw-w64-headers/crt/ftw.h   | 23 ++++++++++++++++++++---
 7 files changed, 99 insertions(+), 11 deletions(-)
 create mode 100644 mingw-w64-crt/misc/ftw32.c
 create mode 100644 mingw-w64-crt/misc/ftw32i64.c
 create mode 100644 mingw-w64-crt/misc/ftw64i32.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index 5fcde4a6d..b999ad6b2 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1033,7 +1033,8 @@ src_libmingwex=\
   misc/wcstold.c \
   misc/wdirent.c         misc/winbs_uint64.c        misc/winbs_ulong.c      
misc/winbs_ushort.c    \
   misc/wmemchr.c         misc/wmemcmp.c             misc/wmemcpy.c          
misc/wmemmove.c              misc/wmempcpy.c        \
-  misc/wmemset.c         misc/ftw.c                 misc/ftw64.c            
misc/mingw-access.c          \
+  misc/wmemset.c         misc/mingw-access.c \
+  misc/ftw32.c           misc/ftw32i64.c            misc/ftw64.c            
misc/ftw64i32.c \
   misc/__mingw_filename_cp.c \
   \
   ssp/chk_fail.c         ssp/gets_chk.c             ssp/memcpy_chk.c        
ssp/memmove_chk.c \
@@ -4270,6 +4271,7 @@ EXTRA_DIST += revstamp.h \
   crt/CRT_noglob.c \
   crt/txtmode.c \
   crt/ucrtexe.c \
+  misc/ftw.c \
   profile/gcrt0.c \
   profile/COPYING \
   profile/CYGWIN_LICENSE \
diff --git a/mingw-w64-crt/misc/ftw.c b/mingw-w64-crt/misc/ftw.c
index 74d05aa71..54a6d8b88 100644
--- a/mingw-w64-crt/misc/ftw.c
+++ b/mingw-w64-crt/misc/ftw.c
@@ -3,6 +3,7 @@
  * No warranty is given; refer to the file DISCLAIMER within this package.
  */
 
+#define __CRT_BUILDING_FTW
 #include <stdlib.h>
 #include <unistd.h>
 #include <malloc.h>
@@ -15,12 +16,6 @@
 #include <dirent.h>
 #include <ftw.h>
 
-#ifdef IMPL_FTW64
-#define stat stat64
-#define nftw nftw64
-#define ftw ftw64
-#endif
-
 typedef struct dir_data_t {
   DIR *h;
   char *buf;
@@ -450,12 +445,16 @@ do_it (const char *dir, __UNUSED_PARAM(int is_nftw), void 
*fcb, int descriptors,
   return ret;
 }
 
+int
+ftw (const char *path, int (*fcb) (const char *, const struct stat *, int), 
int descriptors);
 int
 ftw (const char *path, int (*fcb) (const char *, const struct stat *, int), 
int descriptors)
 {
   return do_it (path, 0, fcb, descriptors, 0);
 }
 
+int
+nftw (const char *path, int (*fcb) (const char *, const struct stat *, int , 
struct FTW *), int descriptors, int flags);
 int
 nftw (const char *path, int (*fcb) (const char *, const struct stat *, int , 
struct FTW *), int descriptors, int flags)
 {
diff --git a/mingw-w64-crt/misc/ftw32.c b/mingw-w64-crt/misc/ftw32.c
new file mode 100644
index 000000000..dbdeb2d0a
--- /dev/null
+++ b/mingw-w64-crt/misc/ftw32.c
@@ -0,0 +1,27 @@
+/**
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within this package.
+ */
+
+#ifndef _WIN64
+
+#define _USE_32BIT_TIME_T
+
+#define nftw nftw32
+#define ftw ftw32
+
+#include "ftw.c"
+
+/* This is the default ABI in 32-bit non-UCRT builds. */
+#ifndef _UCRT
+#undef nftw
+#undef ftw
+
+int __attribute__ ((alias ("nftw32"))) __cdecl
+nftw (const char *, int (*) (const char *, const struct stat *, int, struct 
FTW *), int, int);
+
+int __attribute__ ((alias ("ftw32"))) __cdecl
+ftw (const char *, int (*) (const char *, const struct stat *, int), int);
+#endif
+
+#endif
diff --git a/mingw-w64-crt/misc/ftw32i64.c b/mingw-w64-crt/misc/ftw32i64.c
new file mode 100644
index 000000000..5b70e2f0e
--- /dev/null
+++ b/mingw-w64-crt/misc/ftw32i64.c
@@ -0,0 +1,16 @@
+/**
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within this package.
+ */
+
+#ifndef _WIN64
+
+#define _USE_32BIT_TIME_T
+#define _FILE_OFFSET_BITS 64
+
+#define nftw nftw32i64
+#define ftw ftw32i64
+
+#include "ftw.c"
+
+#endif
diff --git a/mingw-w64-crt/misc/ftw64.c b/mingw-w64-crt/misc/ftw64.c
index 3e45847ef..a2d8ded11 100644
--- a/mingw-w64-crt/misc/ftw64.c
+++ b/mingw-w64-crt/misc/ftw64.c
@@ -3,6 +3,10 @@
  * No warranty is given; refer to the file DISCLAIMER within this package.
  */
 
-#define IMPL_FTW64 1
+#define _TIME_BITS 64
+#define _FILE_OFFSET_BITS 64
+
+#define nftw nftw64
+#define ftw ftw64
 
 #include "ftw.c"
diff --git a/mingw-w64-crt/misc/ftw64i32.c b/mingw-w64-crt/misc/ftw64i32.c
new file mode 100644
index 000000000..b4fca4d2a
--- /dev/null
+++ b/mingw-w64-crt/misc/ftw64i32.c
@@ -0,0 +1,23 @@
+/**
+ * This file is part of the mingw-w64 runtime package.
+ * No warranty is given; refer to the file DISCLAIMER within this package.
+ */
+
+#define _TIME_BITS 64
+
+#define nftw nftw64i32
+#define ftw ftw64i32
+
+#include "ftw.c"
+
+/* This is the default ABI in all 64-bit builds and all UCRT builds. */
+#if defined(_WIN64) || defined(_UCRT)
+#undef nftw
+#undef ftw
+
+int __attribute__ ((alias ("nftw64i32"))) __cdecl
+nftw (const char *, int (*) (const char *, const struct stat *, int, struct 
FTW *), int, int);
+
+int __attribute__ ((alias ("ftw64i32"))) __cdecl
+ftw (const char *, int (*) (const char *, const struct stat *, int), int);
+#endif
diff --git a/mingw-w64-headers/crt/ftw.h b/mingw-w64-headers/crt/ftw.h
index a5e23e0a9..f491ca5a6 100644
--- a/mingw-w64-headers/crt/ftw.h
+++ b/mingw-w64-headers/crt/ftw.h
@@ -53,11 +53,28 @@ extern "C" {
   /* Continue with FTW_DP callback for current directory (if FTW_DEPTH) and 
then its siblings.  */
 #define FTW_SKIP_SIBLINGS 3
 
-  int ftw (const char *, int (*) (const char *, const struct stat *, int), 
int);
-  int ftw64 (const char *, int (*) (const char *, const struct stat64 *, int), 
int);
+#ifndef __CRT_BUILDING_FTW
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+  int ftw (const char *, int (*) (const char *, const struct stat *, int), 
int) __MINGW_ASM_CALL(ftw32i64);
+  int nftw (const char *, int (*) (const char *, const struct stat *, int , 
struct FTW *), int, int) __MINGW_ASM_CALL(nftw32i64);
+#else
+  int ftw (const char *, int (*) (const char *, const struct stat *, int), 
int) __MINGW_ASM_CALL(ftw64);
+  int nftw (const char *, int (*) (const char *, const struct stat *, int , 
struct FTW *), int, int) __MINGW_ASM_CALL(nftw64);
+#endif
+#else
+#ifdef _USE_32BIT_TIME_T
+  int ftw (const char *, int (*) (const char *, const struct stat *, int), 
int) __MINGW_ASM_CALL(ftw32);
+  int nftw (const char *, int (*) (const char *, const struct stat *, int , 
struct FTW *), int, int) __MINGW_ASM_CALL(nftw32);
+#else
+  int ftw (const char *, int (*) (const char *, const struct stat *, int), 
int) __MINGW_ASM_CALL(ftw64i32);
+  int nftw (const char *, int (*) (const char *, const struct stat *, int , 
struct FTW *), int, int) __MINGW_ASM_CALL(nftw64i32);
+#endif
+#endif
 
-  int nftw (const char *, int (*) (const char *, const struct stat *, int , 
struct FTW *), int, int);
+  int ftw64 (const char *, int (*) (const char *, const struct stat64 *, int), 
int);
   int nftw64 (const char *, int (*) (const char *, const struct stat64 *, int 
, struct FTW *), int, int);
+#endif
 
 #ifdef __cplusplus
 }
-- 
2.49.0

_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to