C99 allows to pass NULL path into freopen() function which just changes the
mode of FILE*. Both msvcrt and UCRT returns error when NULL path is passed.

The primary use case of this C99 feature is to change stdin / stdout mode
from text to binary and so allow processing of binary data on stdin or
stdout.

Implement support for NULL path by renaming existing freopen symbol to
_freopen and then providing mingw-w64 wrapper function freopen() which will
either call renamed _freopen() or just changes the mode on existing FILE*
stream.

Because FILE struct has different offsets to members between msvcrt and
UCRT builds, it is needed to have separate compilation unit for msvcrt and
UCRT builds.
---
 mingw-w64-crt/Makefile.am                     |   2 +
 mingw-w64-crt/def-include/crt-aliases.def.in  |   2 +-
 .../api-ms-win-crt-stdio-l1-1-0.def           |   4 +-
 mingw-w64-crt/lib-common/msvcr120_app.def.in  |   2 +-
 mingw-w64-crt/lib-common/msvcrt.def.in        |   2 +-
 .../lib-common/ucrtbase-common.def.in         |   2 +-
 mingw-w64-crt/lib32/crtdll.def.in             |   2 +-
 mingw-w64-crt/lib32/msvcr100.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr100d.def.in          |   2 +-
 mingw-w64-crt/lib32/msvcr110.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr110d.def.in          |   2 +-
 mingw-w64-crt/lib32/msvcr120.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr120d.def.in          |   2 +-
 mingw-w64-crt/lib32/msvcr40d.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr70.def.in            |   2 +-
 mingw-w64-crt/lib32/msvcr70d.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr71.def.in            |   2 +-
 mingw-w64-crt/lib32/msvcr71d.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr80.def.in            |   2 +-
 mingw-w64-crt/lib32/msvcr80d.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcr90.def.in            |   2 +-
 mingw-w64-crt/lib32/msvcr90d.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcrt10.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcrt20.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcrt40.def.in           |   2 +-
 mingw-w64-crt/lib32/msvcrtd.def.in            |   2 +-
 mingw-w64-crt/lib64/msvcr100.def.in           |   2 +-
 mingw-w64-crt/lib64/msvcr100d.def.in          |   2 +-
 mingw-w64-crt/lib64/msvcr110.def.in           |   2 +-
 mingw-w64-crt/lib64/msvcr110d.def.in          |   2 +-
 mingw-w64-crt/lib64/msvcr120.def.in           |   2 +-
 mingw-w64-crt/lib64/msvcr120d.def.in          |   2 +-
 mingw-w64-crt/lib64/msvcr80.def.in            |   2 +-
 mingw-w64-crt/lib64/msvcr80d.def.in           |   2 +-
 mingw-w64-crt/lib64/msvcr90.def.in            |   2 +-
 mingw-w64-crt/lib64/msvcr90d.def.in           |   2 +-
 mingw-w64-crt/libarm32/msvcr110.def.in        |   2 +-
 mingw-w64-crt/libarm32/msvcr110d.def.in       |   2 +-
 mingw-w64-crt/libarm32/msvcr120.def.in        |   2 +-
 mingw-w64-crt/libarm32/msvcr120d.def.in       |   2 +-
 mingw-w64-crt/stdio/freopen.c                 | 315 ++++++++++++++++++
 mingw-w64-crt/stdio/ucrt_freopen.c            |   3 +
 mingw-w64-crt/testcases/Makefile.am           |   1 +
 mingw-w64-crt/testcases/t_freopen.c           |   9 +
 44 files changed, 370 insertions(+), 40 deletions(-)
 create mode 100644 mingw-w64-crt/stdio/freopen.c
 create mode 100644 mingw-w64-crt/stdio/ucrt_freopen.c
 create mode 100644 mingw-w64-crt/testcases/t_freopen.c

diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index afb8aa10ed73..1ca8520ea119 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -185,6 +185,7 @@ src_msvcrt_common=\
   stdio/_strtof_l.c \
   stdio/_wcstof_l.c \
   stdio/acrt_iob_func.c \
+  stdio/freopen.c \
   stdio/strtof.c \
   stdio/snprintf_alias.c \
   stdio/snwprintf_alias.c \
@@ -443,6 +444,7 @@ src_ucrtbase=\
   stdio/ucrt__vsnwprintf.c \
   stdio/ucrt__vswprintf.c \
   stdio/ucrt_fprintf.c \
+  stdio/ucrt_freopen.c \
   stdio/ucrt_fscanf.c \
   stdio/ucrt_fwprintf.c \
   stdio/ucrt_fwscanf.c \
diff --git a/mingw-w64-crt/def-include/crt-aliases.def.in 
b/mingw-w64-crt/def-include/crt-aliases.def.in
index 65675ecbc1c9..d952c96f0248 100644
--- a/mingw-w64-crt/def-include/crt-aliases.def.in
+++ b/mingw-w64-crt/def-include/crt-aliases.def.in
@@ -301,7 +301,7 @@ ftello == ftell
 creat64 == _creat
 open64 == _open
 fopen64 == fopen
-freopen64 == freopen
+; freopen64 function is provided by stdio/freopen.c
 tmpfile64 == tmpfile
 #ifndef NO_FPOS64_ALIASES
 ; fgetpos and fsetpos are already 64-bit
diff --git a/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def 
b/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def
index 2f31884e8009..d4858eb55d55 100644
--- a/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def
+++ b/mingw-w64-crt/lib-common/api-ms-win-crt-stdio-l1-1-0.def
@@ -173,8 +173,8 @@ fputwc
 fputws
 fread
 fread_s
-freopen
-freopen64 == freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
+; freopen64 function is provided by stdio/freopen.c
 freopen_s
 fseek
 fseeko == fseek
diff --git a/mingw-w64-crt/lib-common/msvcr120_app.def.in 
b/mingw-w64-crt/lib-common/msvcr120_app.def.in
index 32a85cde7913..e853e761d9dd 100644
--- a/mingw-w64-crt/lib-common/msvcr120_app.def.in
+++ b/mingw-w64-crt/lib-common/msvcr120_app.def.in
@@ -2028,7 +2028,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fscanf
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in 
b/mingw-w64-crt/lib-common/msvcrt.def.in
index a86e5680c3d9..16b422976585 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -975,7 +975,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp F_X86_NATIVE(DATA)
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib-common/ucrtbase-common.def.in 
b/mingw-w64-crt/lib-common/ucrtbase-common.def.in
index c0a273e0f280..47b96b489aca 100644
--- a/mingw-w64-crt/lib-common/ucrtbase-common.def.in
+++ b/mingw-w64-crt/lib-common/ucrtbase-common.def.in
@@ -2385,7 +2385,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fseek
diff --git a/mingw-w64-crt/lib32/crtdll.def.in 
b/mingw-w64-crt/lib32/crtdll.def.in
index 8ffaa3adf385..bcc0ee7eb8ba 100644
--- a/mingw-w64-crt/lib32/crtdll.def.in
+++ b/mingw-w64-crt/lib32/crtdll.def.in
@@ -467,7 +467,7 @@ fputs
 fputwc
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr100.def.in 
b/mingw-w64-crt/lib32/msvcr100.def.in
index 2d00f87f0d07..b07af4a991dc 100644
--- a/mingw-w64-crt/lib32/msvcr100.def.in
+++ b/mingw-w64-crt/lib32/msvcr100.def.in
@@ -1697,7 +1697,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr100d.def.in 
b/mingw-w64-crt/lib32/msvcr100d.def.in
index e1f6b4770127..6f6f2a0a5a47 100644
--- a/mingw-w64-crt/lib32/msvcr100d.def.in
+++ b/mingw-w64-crt/lib32/msvcr100d.def.in
@@ -1763,7 +1763,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr110.def.in 
b/mingw-w64-crt/lib32/msvcr110.def.in
index be2491f6fd85..bebd0aa6558b 100644
--- a/mingw-w64-crt/lib32/msvcr110.def.in
+++ b/mingw-w64-crt/lib32/msvcr110.def.in
@@ -1829,7 +1829,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr110d.def.in 
b/mingw-w64-crt/lib32/msvcr110d.def.in
index 16d06696c574..ed917c9ca08a 100644
--- a/mingw-w64-crt/lib32/msvcr110d.def.in
+++ b/mingw-w64-crt/lib32/msvcr110d.def.in
@@ -1896,7 +1896,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr120.def.in 
b/mingw-w64-crt/lib32/msvcr120.def.in
index aaa965780e67..03cc0002fb2b 100644
--- a/mingw-w64-crt/lib32/msvcr120.def.in
+++ b/mingw-w64-crt/lib32/msvcr120.def.in
@@ -1997,7 +1997,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr120d.def.in 
b/mingw-w64-crt/lib32/msvcr120d.def.in
index f670d0bbffa8..7173d07dce7a 100644
--- a/mingw-w64-crt/lib32/msvcr120d.def.in
+++ b/mingw-w64-crt/lib32/msvcr120d.def.in
@@ -2064,7 +2064,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr40d.def.in 
b/mingw-w64-crt/lib32/msvcr40d.def.in
index 1590e659077b..345b4c9fdb85 100644
--- a/mingw-w64-crt/lib32/msvcr40d.def.in
+++ b/mingw-w64-crt/lib32/msvcr40d.def.in
@@ -1497,7 +1497,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr70.def.in 
b/mingw-w64-crt/lib32/msvcr70.def.in
index ec64df727c37..9d16adf7207d 100644
--- a/mingw-w64-crt/lib32/msvcr70.def.in
+++ b/mingw-w64-crt/lib32/msvcr70.def.in
@@ -747,7 +747,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr70d.def.in 
b/mingw-w64-crt/lib32/msvcr70d.def.in
index 408f1e9194ce..ee0c89f6926d 100644
--- a/mingw-w64-crt/lib32/msvcr70d.def.in
+++ b/mingw-w64-crt/lib32/msvcr70d.def.in
@@ -791,7 +791,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr71.def.in 
b/mingw-w64-crt/lib32/msvcr71.def.in
index 6e3f54069999..c35494696887 100644
--- a/mingw-w64-crt/lib32/msvcr71.def.in
+++ b/mingw-w64-crt/lib32/msvcr71.def.in
@@ -741,7 +741,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr71d.def.in 
b/mingw-w64-crt/lib32/msvcr71d.def.in
index 7f70c52095a2..801ce1b2bc24 100644
--- a/mingw-w64-crt/lib32/msvcr71d.def.in
+++ b/mingw-w64-crt/lib32/msvcr71d.def.in
@@ -785,7 +785,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcr80.def.in 
b/mingw-w64-crt/lib32/msvcr80.def.in
index 981870755ae8..5b37f1e50107 100644
--- a/mingw-w64-crt/lib32/msvcr80.def.in
+++ b/mingw-w64-crt/lib32/msvcr80.def.in
@@ -1336,7 +1336,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr80d.def.in 
b/mingw-w64-crt/lib32/msvcr80d.def.in
index a98c5dd36dc1..a65513dcf922 100644
--- a/mingw-w64-crt/lib32/msvcr80d.def.in
+++ b/mingw-w64-crt/lib32/msvcr80d.def.in
@@ -1419,7 +1419,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr90.def.in 
b/mingw-w64-crt/lib32/msvcr90.def.in
index 6f626415f7fc..a4c1eddb3acc 100644
--- a/mingw-w64-crt/lib32/msvcr90.def.in
+++ b/mingw-w64-crt/lib32/msvcr90.def.in
@@ -1331,7 +1331,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcr90d.def.in 
b/mingw-w64-crt/lib32/msvcr90d.def.in
index 8bca8f485a4a..6c1d7dfd50a0 100644
--- a/mingw-w64-crt/lib32/msvcr90d.def.in
+++ b/mingw-w64-crt/lib32/msvcr90d.def.in
@@ -1403,7 +1403,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib32/msvcrt10.def.in 
b/mingw-w64-crt/lib32/msvcrt10.def.in
index 9d4db9f811e7..7576391c9ea6 100644
--- a/mingw-w64-crt/lib32/msvcrt10.def.in
+++ b/mingw-w64-crt/lib32/msvcrt10.def.in
@@ -1146,7 +1146,7 @@ fputs
 fputwc
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcrt20.def.in 
b/mingw-w64-crt/lib32/msvcrt20.def.in
index 94dcdb2fab49..ad1962864163 100644
--- a/mingw-w64-crt/lib32/msvcrt20.def.in
+++ b/mingw-w64-crt/lib32/msvcrt20.def.in
@@ -1365,7 +1365,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcrt40.def.in 
b/mingw-w64-crt/lib32/msvcrt40.def.in
index a061b15a8a65..1bfac1a19857 100644
--- a/mingw-w64-crt/lib32/msvcrt40.def.in
+++ b/mingw-w64-crt/lib32/msvcrt40.def.in
@@ -1465,7 +1465,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib32/msvcrtd.def.in 
b/mingw-w64-crt/lib32/msvcrtd.def.in
index 36e9fa45eda0..5274e7d49582 100644
--- a/mingw-w64-crt/lib32/msvcrtd.def.in
+++ b/mingw-w64-crt/lib32/msvcrtd.def.in
@@ -691,7 +691,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 frexp
 fscanf
 fseek
diff --git a/mingw-w64-crt/lib64/msvcr100.def.in 
b/mingw-w64-crt/lib64/msvcr100.def.in
index df860c09d27c..291718638f98 100644
--- a/mingw-w64-crt/lib64/msvcr100.def.in
+++ b/mingw-w64-crt/lib64/msvcr100.def.in
@@ -1652,7 +1652,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr100d.def.in 
b/mingw-w64-crt/lib64/msvcr100d.def.in
index 6fd09a5886e4..185c173580b9 100644
--- a/mingw-w64-crt/lib64/msvcr100d.def.in
+++ b/mingw-w64-crt/lib64/msvcr100d.def.in
@@ -1717,7 +1717,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr110.def.in 
b/mingw-w64-crt/lib64/msvcr110.def.in
index 80f9fa554c71..810b4cb22e6d 100644
--- a/mingw-w64-crt/lib64/msvcr110.def.in
+++ b/mingw-w64-crt/lib64/msvcr110.def.in
@@ -1776,7 +1776,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr110d.def.in 
b/mingw-w64-crt/lib64/msvcr110d.def.in
index 3bb047b4cd3c..01c5650cde64 100644
--- a/mingw-w64-crt/lib64/msvcr110d.def.in
+++ b/mingw-w64-crt/lib64/msvcr110d.def.in
@@ -1841,7 +1841,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr120.def.in 
b/mingw-w64-crt/lib64/msvcr120.def.in
index 4f19699b1771..383aea6910fa 100644
--- a/mingw-w64-crt/lib64/msvcr120.def.in
+++ b/mingw-w64-crt/lib64/msvcr120.def.in
@@ -1943,7 +1943,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr120d.def.in 
b/mingw-w64-crt/lib64/msvcr120d.def.in
index f4a1d6b24a68..2c8ef5498ae4 100644
--- a/mingw-w64-crt/lib64/msvcr120d.def.in
+++ b/mingw-w64-crt/lib64/msvcr120d.def.in
@@ -2008,7 +2008,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr80.def.in 
b/mingw-w64-crt/lib64/msvcr80.def.in
index 362ca3efe409..13f81391f280 100644
--- a/mingw-w64-crt/lib64/msvcr80.def.in
+++ b/mingw-w64-crt/lib64/msvcr80.def.in
@@ -1274,7 +1274,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr80d.def.in 
b/mingw-w64-crt/lib64/msvcr80d.def.in
index 66ff58201d8f..72cc448e5792 100644
--- a/mingw-w64-crt/lib64/msvcr80d.def.in
+++ b/mingw-w64-crt/lib64/msvcr80d.def.in
@@ -1351,7 +1351,7 @@ fputwc
 fputws
 fread
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA ; overwritten
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr90.def.in 
b/mingw-w64-crt/lib64/msvcr90.def.in
index d04136ed004a..fe07c215e220 100644
--- a/mingw-w64-crt/lib64/msvcr90.def.in
+++ b/mingw-w64-crt/lib64/msvcr90.def.in
@@ -1272,7 +1272,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/lib64/msvcr90d.def.in 
b/mingw-w64-crt/lib64/msvcr90d.def.in
index 1a25264437f1..e5369f325b51 100644
--- a/mingw-w64-crt/lib64/msvcr90d.def.in
+++ b/mingw-w64-crt/lib64/msvcr90d.def.in
@@ -1338,7 +1338,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp DATA
 fscanf
diff --git a/mingw-w64-crt/libarm32/msvcr110.def.in 
b/mingw-w64-crt/libarm32/msvcr110.def.in
index d863b64340a5..0be317154872 100644
--- a/mingw-w64-crt/libarm32/msvcr110.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110.def.in
@@ -1763,7 +1763,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fscanf
diff --git a/mingw-w64-crt/libarm32/msvcr110d.def.in 
b/mingw-w64-crt/libarm32/msvcr110d.def.in
index 699f0e8de058..560beb37cd26 100644
--- a/mingw-w64-crt/libarm32/msvcr110d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110d.def.in
@@ -1828,7 +1828,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fscanf
diff --git a/mingw-w64-crt/libarm32/msvcr120.def.in 
b/mingw-w64-crt/libarm32/msvcr120.def.in
index 3218c4897b44..29f351e1b876 100644
--- a/mingw-w64-crt/libarm32/msvcr120.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120.def.in
@@ -1911,7 +1911,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fscanf
diff --git a/mingw-w64-crt/libarm32/msvcr120d.def.in 
b/mingw-w64-crt/libarm32/msvcr120d.def.in
index 096f4d17d9ca..4ba59334d6c9 100644
--- a/mingw-w64-crt/libarm32/msvcr120d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120d.def.in
@@ -1976,7 +1976,7 @@ fputws
 fread
 fread_s
 free
-freopen
+_freopen == freopen ; freopen replaced by emu because is not C99 compatible
 freopen_s
 frexp
 fscanf
diff --git a/mingw-w64-crt/stdio/freopen.c b/mingw-w64-crt/stdio/freopen.c
new file mode 100644
index 000000000000..237c295de0d0
--- /dev/null
+++ b/mingw-w64-crt/stdio/freopen.c
@@ -0,0 +1,315 @@
+/**
+ * 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 <stdio.h>
+#include <errno.h>
+#include <io.h>
+#include <fcntl.h>
+#include <internal.h>
+#include <windows.h>
+#include <winternl.h>
+#include <ntstatus.h>
+
+static NTSTATUS NTAPI MyNtQueryInformationFile(HANDLE FileHandle, 
PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, 
FILE_INFORMATION_CLASS FileInformationClass)
+{
+  static NTSTATUS (NTAPI *NtQueryInformationFilePtr)(HANDLE, PIO_STATUS_BLOCK, 
PVOID, ULONG, FILE_INFORMATION_CLASS);
+  static volatile LONG init = 0;
+
+  if (!init) {
+    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
+    FARPROC func = ntdll ? GetProcAddress(ntdll, "NtQueryInformationFile") : 
NULL;
+    (void)InterlockedExchangePointer((PVOID volatile 
*)&NtQueryInformationFilePtr, func);
+    (void)InterlockedExchange(&init, 1);
+  }
+
+  if (!NtQueryInformationFilePtr)
+    return STATUS_NOT_IMPLEMENTED;
+  else
+    return NtQueryInformationFilePtr(FileHandle, IoStatusBlock, 
FileInformation, Length, FileInformationClass);
+}
+#define NtQueryInformationFile MyNtQueryInformationFile
+
+static NTSTATUS NTAPI MyNtSetInformationFile(HANDLE FileHandle, 
PIO_STATUS_BLOCK IoStatusBlock, PVOID FileInformation, ULONG Length, 
FILE_INFORMATION_CLASS FileInformationClass)
+{
+  static NTSTATUS (NTAPI *NtSetInformationFilePtr)(HANDLE, PIO_STATUS_BLOCK, 
PVOID, ULONG, FILE_INFORMATION_CLASS);
+  static volatile LONG init = 0;
+
+  if (!init) {
+    HMODULE ntdll = GetModuleHandleA("ntdll.dll");
+    FARPROC func = ntdll ? GetProcAddress(ntdll, "NtSetInformationFile") : 
NULL;
+    (void)InterlockedExchangePointer((PVOID volatile 
*)&NtSetInformationFilePtr, func);
+    (void)InterlockedExchange(&init, 1);
+  }
+
+  if (!NtSetInformationFilePtr)
+    return STATUS_NOT_IMPLEMENTED;
+  else
+    return NtSetInformationFilePtr(FileHandle, IoStatusBlock, FileInformation, 
Length, FileInformationClass);
+}
+#define NtSetInformationFile MyNtSetInformationFile
+
+
+_CRTIMP FILE *__cdecl _freopen(const char *restrict filename, const char 
*restrict mode, FILE *restrict file);
+
+FILE *__cdecl freopen(const char *restrict filename, const char *restrict 
mode, FILE *restrict file)
+{
+  int new_stream_flags;
+  int new_mode_flags;
+  int new_mode;
+  int change_iocommit;
+  int fd;
+  HANDLE handle;
+  FILE_MODE_INFORMATION file_mode_information;
+  IO_STATUS_BLOCK io_status_block;
+  NTSTATUS status;
+
+  /* This mingw-w64 function handles only case when the filename is NULL and
+   * both mode and file are specified. Forward all other cases to the original
+   * msvcrt freopen function.
+   */
+  if (!(!filename && mode && file))
+    return _freopen(filename, mode, file);
+
+
+  /* As a first step parse freopen mode and reject any invalid combinations
+   * in the same way and same behavior as the original msvcrt freopen function.
+   */
+
+  while (*mode == ' ')
+    mode++;
+
+  new_stream_flags = _commode; /* default stream flags are in global variable 
_commode */
+  new_mode_flags = 0;
+  change_iocommit = 0;
+
+  if (*mode == 'r')
+    new_stream_flags |= _IOREAD;
+  else if (*mode == 'w' || *mode == 'a') /* FIXME: changing between 'w' and 
'a' is ignored and does not trigger any error */
+    new_stream_flags |= _IOWRT;
+  else
+    goto err_inval;
+
+  while (*++mode) {
+    if (*mode == ' ')
+      continue;
+
+    if (*mode == '+') {
+      if (new_stream_flags & _IORW)
+        goto err_inval;
+      new_stream_flags |= _IORW;
+      new_stream_flags &= ~(_IOREAD | _IOWRT);
+    } else if (*mode == 'b') {
+      if (new_mode_flags & (_O_TEXT | _O_BINARY))
+        goto err_inval;
+      new_mode_flags |= _O_BINARY;
+    } else if (*mode == 't') {
+      if (new_mode_flags & (_O_TEXT | _O_BINARY))
+        goto err_inval;
+      new_mode_flags |= _O_TEXT;
+    } else if (*mode == 'c') {
+      if (change_iocommit)
+        goto err_inval;
+      new_stream_flags |= _IOCOMMIT;
+      change_iocommit = 1;
+    } else if (*mode == 'n') {
+      if (change_iocommit)
+        goto err_inval;
+      new_stream_flags &= ~_IOCOMMIT;
+      change_iocommit = 1;
+    } else if (*mode == 'S') {
+      if (new_mode_flags & (_O_SEQUENTIAL | _O_RANDOM))
+        goto err_inval;
+      new_mode_flags |= _O_SEQUENTIAL;
+    } else if (*mode == 'R') {
+      if (new_mode_flags & (_O_SEQUENTIAL | _O_RANDOM))
+        goto err_inval;
+      new_mode_flags |= _O_RANDOM;
+    } else if (*mode == 'T') {
+      if (new_mode_flags & _O_SHORT_LIVED)
+        goto err_inval;
+      new_mode_flags |= _O_SHORT_LIVED;
+    } else if (*mode == 'D') {
+      if (new_mode_flags & _O_TEMPORARY)
+        goto err_inval;
+      new_mode_flags |= _O_TEMPORARY;
+    } else if (*mode == 'N') {
+      /* msvcrt does not check for duplicate N characters */
+      new_mode_flags |= _O_NOINHERIT;
+    } else if (*mode == ',') {
+      while (*mode == ' ')
+        mode++;
+      if (strncmp(mode, "ccs", 3) != 0)
+        goto err_inval;
+      mode += 3;
+      while (*mode == ' ')
+        mode++;
+      if (*mode != '=')
+        goto err_inval;
+      while (*mode == ' ')
+        mode++;
+      if (stricmp(mode, "UTF-8") == 0)
+        new_mode_flags |= _O_U8TEXT;
+      else if (stricmp(mode, "UTF-16LE") == 0)
+        new_mode_flags |= _O_U16TEXT;
+      else if (stricmp(mode, "UNICODE") == 0)
+        new_mode_flags |= _O_WTEXT;
+      else
+        goto err_inval;
+      /* after ",css=" there is no other mode specifier, stricmp checks for 
this */
+    } else {
+      goto err_inval;
+    }
+  }
+
+  /* If mode is not specified then the default one from the global variable 
_fmode is used */
+  if (!(new_mode_flags & (_O_BINARY | _O_TEXT | _O_U8TEXT | _O_U16TEXT | 
_O_WTEXT)))
+    new_mode_flags |= _fmode & (_O_BINARY | _O_TEXT | _O_U8TEXT | _O_U16TEXT | 
_O_WTEXT);
+
+  /* If mode is not specified neither in _fmode then the fallback value is 
always _O_TEXT */
+  if (!(new_mode_flags & (_O_BINARY | _O_TEXT | _O_U8TEXT | _O_U16TEXT | 
_O_WTEXT)))
+    new_mode_flags |= _O_TEXT;
+
+  /* Changing of _IOREAD, _IOWRT and _IORW is not possible */
+  if ((new_stream_flags & (_IOREAD | _IOWRT | _IORW)) != (file->_flags & 
(_IOREAD | _IOWRT | _IORW)))
+    goto err_inval;
+
+
+  /* As a second step take CRT file descriptor and WINAPI handle */
+
+  fd = fileno(file);
+  if (fd < 0)
+    return NULL;
+
+  handle = (HANDLE)_get_osfhandle(fd);
+  if (handle == INVALID_HANDLE_VALUE)
+    return NULL;
+
+
+  /* As a third step before changing any FILE*, fd or HANDLE characteristics, 
flush all buffers */
+  /* This ensures that data in buffer are processed by existing settings */
+  fflush(file);
+
+
+  /* As a fourth step process _O_SEQUENTIAL, _O_TEMPORARY, _O_NOINHERIT, 
_O_SHORT_LIVED and _O_RANDOM flags */
+
+  /* CRT _O_SEQUENTIAL flag is mapped to WINAPI FILE_FLAG_SEQUENTIAL_SCAN flag
+   * which is mapped to NT FILE_SEQUENTIAL_ONLY flag. WINAPI does not provide
+   * API to query or change this flag on already opened file handle. NT API
+   * provides functions NtQueryInformationFile() and NtSetInformationFile()
+   * with class FileModeInformation which allows that. WINAPI functions
+   * GetFileInformationByHandleEx() and SetFileInformationByHandle() do not
+   * support FileModeInfo level yet.
+   * Our NtQueryInformationFile wrapper returns STATUS_NOT_IMPLEMENTED when
+   * the NtQueryInformationFile function is not supported.
+   * For console handle: wine returns STATUS_OBJECT_TYPE_MISMATCH,
+   * Windows XP returns STATUS_INVALID_HANDLE and Windows 10 returns
+   * STATUS_SUCCESS.
+   * Treat STATUS_NOT_IMPLEMENTED, STATUS_OBJECT_TYPE_MISMATCH and
+   * STATUS_INVALID_HANDLE codes as if the FILE_SEQUENTIAL_ONLY was not set
+   * and setting it is not supported.
+   */
+  status = NtQueryInformationFile(handle, &io_status_block, 
&file_mode_information, sizeof(file_mode_information), FileModeInformation);
+  if (status == STATUS_NOT_IMPLEMENTED || status == 
STATUS_OBJECT_TYPE_MISMATCH || status == STATUS_INVALID_HANDLE) {
+    if (new_mode_flags & _O_SEQUENTIAL)
+      goto err_inval;
+  } else if (status == STATUS_SUCCESS) {
+    if (!!(new_mode_flags & _O_SEQUENTIAL) != !!(file_mode_information.Mode & 
FILE_SEQUENTIAL_ONLY)) {
+      if (new_mode_flags & _O_SEQUENTIAL)
+        file_mode_information.Mode |= FILE_SEQUENTIAL_ONLY;
+      else
+        file_mode_information.Mode &= ~FILE_SEQUENTIAL_ONLY;
+      status = NtSetInformationFile(handle, &io_status_block, 
&file_mode_information, sizeof(file_mode_information), FileModeInformation);
+      if (status != STATUS_SUCCESS)
+        goto err_inval;
+    }
+  } else {
+    goto err_inval;
+  }
+
+  /* CRT _O_TEMPORARY flag is mapped to WINAPI FILE_FLAG_DELETE_ON_CLOSE flag
+   * which is mapped to NT FILE_DELETE_ON_CLOSE flag. There is no API which
+   * allows to query that flag. Function GetFileInformationByHandleEx() does
+   * not support levels FileDispositionInfo and FileDispositionInfoEx. And
+   * function NtQueryInformationFile() does not support classes
+   * FileDispositionInformation and FileDispositionInformationEx.
+   * Setting or clearing this flag on already opened file handle is possible
+   * only on filesystem which supports FileDispositionInformationEx
+   * FILE_DISPOSITION_ON_CLOSE flag (e.g. on NTFS since Windows 10 1607)
+   * via either WINAPI SetFileInformationByHandle() function with level
+   * FileDispositionInfoEx or via NT NtSetInformationFile() function with class
+   * FileDispositionInformationEx, together with specifying
+   * FILE_DISPOSITION_ON_CLOSE flag. According to MS-FSCC documentation,
+   * setting of this flag will fail with STATUS_NOT_SUPPORTED if the
+   * NT FILE_DELETE_ON_CLOSE flag was not specified during opening the file.
+   * So there is no reliable way to query, clear or set FILE_DELETE_ON_CLOSE
+   * flag. Therefore ignore the CRT _O_TEMPORARY flag.
+   */
+
+  /* CRT _O_NOINHERIT flag is mapped to CRT ioinfo osfile flag FNOINHERIT and
+   * also to WINAPI SecurityAttributes bInheritHandle. WINAPI inheritance can
+   * be queried or changed by GetHandleInformation() and SetHandleInformation()
+   * functions with HANDLE_FLAG_INHERIT mask.
+   * Changing of CRT ioinfo osfile flag FNOINHERIT is not possible without
+   * touching CRT internals. So ignore the CRT _O_NOINHERIT flag.
+   */
+
+
+  /* CRT _O_SHORT_LIVED flag is mapped to WINAPI FILE_ATTRIBUTE_TEMPORARY flag
+   * which applies only when creating a new file. msvcrt and UCRT accepts the
+   * _O_SHORT_LIVED also when opening an existing file and in this case the
+   * flag is ignored. freopen with NULL filename re-opens an existing file,
+   * so always ignore the CRT _O_SHORT_LIVED flag.
+   */
+
+  /* CRT _O_RANDOM flag is mapped to WINAPI FILE_FLAG_RANDOM_ACCESS flag which
+   * is mapped to NT FILE_RANDOM_ACCESS flag. There is no WINAPI or NT function
+   * which allows to query, clear or set this flag. So ignore any attempt to 
set
+   * or to clear this CRT flag without throwing any error.
+   */
+
+
+  /* As a fifth step process _O_BINARY, _O_TEXT, _O_WTEXT, _O_U16TEXT, 
_O_U8TEXT flags */
+
+  /* Choose the correct new mode. If more mode flags are specified then msvcrt 
chooses the first in this order. */
+  if (new_mode_flags & _O_BINARY)
+    new_mode = _O_BINARY;
+  else if (new_mode_flags & _O_TEXT)
+    new_mode = _O_TEXT;
+  else if (new_mode_flags & _O_WTEXT)
+    new_mode = _O_WTEXT;
+  else if (new_mode_flags & _O_U16TEXT)
+    new_mode = _O_U16TEXT;
+  else if (new_mode_flags & _O_U8TEXT)
+    new_mode = _O_U8TEXT;
+  else
+    goto err_inval; /* should not happen */
+
+  if (_setmode(fd, new_mode) < 0)
+    return NULL;
+
+
+  /* As a sixth step process _IOCOMMIT flag */
+
+  /* CRT _IOCOMMIT flag is set only in the FILE* structure. So atomically set 
or clear it. */
+  if (change_iocommit) {
+    if (new_stream_flags & _IOCOMMIT)
+      InterlockedOr((LONG volatile *)&file->_flags, _IOCOMMIT);
+    else
+      InterlockedAnd((LONG volatile *)&file->_flags, ~_IOCOMMIT);
+  }
+
+
+  /* All done */
+  return file;
+
+err_inval:
+  errno = EINVAL;
+  return NULL;
+}
+FILE * (__cdecl *__MINGW_IMP_SYMBOL(freopen))(const char *restrict, const char 
*restrict, FILE *restrict) = freopen;
+
+FILE * __attribute__((alias("freopen"))) __cdecl freopen64(const char 
*restrict filename, const char *restrict mode, FILE *restrict file);
+extern FILE * (__cdecl * __attribute__((alias 
(__MINGW64_STRINGIFY(__MINGW_IMP_SYMBOL(freopen))))) 
__MINGW_IMP_SYMBOL(freopen64))(const char *restrict, const char *restrict, FILE 
*restrict);
diff --git a/mingw-w64-crt/stdio/ucrt_freopen.c 
b/mingw-w64-crt/stdio/ucrt_freopen.c
new file mode 100644
index 000000000000..d00ff8ed3c9d
--- /dev/null
+++ b/mingw-w64-crt/stdio/ucrt_freopen.c
@@ -0,0 +1,3 @@
+/* _flags member of FILE struct has different offset in UCRT builds */
+#define _UCRT
+#include "freopen.c"
diff --git a/mingw-w64-crt/testcases/Makefile.am 
b/mingw-w64-crt/testcases/Makefile.am
index 6ebb7f6babab..6e1248293419 100644
--- a/mingw-w64-crt/testcases/Makefile.am
+++ b/mingw-w64-crt/testcases/Makefile.am
@@ -21,6 +21,7 @@ testcase_progs = \
   t_btowc \
   t_findfirst \
   t_float  \
+  t_freopen \
   t_fstat \
   t_intrinc \
   t_imagebase \
diff --git a/mingw-w64-crt/testcases/t_freopen.c 
b/mingw-w64-crt/testcases/t_freopen.c
new file mode 100644
index 000000000000..8adb5f878a2d
--- /dev/null
+++ b/mingw-w64-crt/testcases/t_freopen.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+#include <assert.h>
+
+int main() {
+    assert(freopen(NULL, "rb", stdin) != NULL);
+    assert(freopen(NULL, "ab", stdout) != NULL);
+    assert(freopen(NULL, "ab", stderr) != NULL);
+    return 0;
+}
-- 
2.20.1



_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to