summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--configure.ac9
-rw-r--r--docs/users_guide/8.6.1-notes.rst48
-rw-r--r--ghc.mk10
-rw-r--r--libraries/base/base.cabal2
-rw-r--r--libraries/base/include/HsBase.h16
-rw-r--r--rts/Hpc.c5
-rw-r--r--rts/Linker.c3
-rw-r--r--rts/PathUtils.h2
-rw-r--r--rts/ProfHeap.c3
-rw-r--r--rts/Profiling.c5
-rw-r--r--rts/RtsFlags.c6
-rw-r--r--rts/RtsSymbols.c1
-rw-r--r--rts/eventlog/EventLogWriter.c3
-rw-r--r--rts/fs_rts.h15
-rw-r--r--rts/ghc.mk2
-rw-r--r--rts/linker/LoadArchive.c1
-rw-r--r--rts/linker/PEi386.c3
-rw-r--r--rts/rts.cabal.in3
-rw-r--r--utils/fs/README4
-rw-r--r--utils/fs/fs.c293
-rw-r--r--utils/fs/fs.h36
-rw-r--r--utils/lndir/lndir.c17
-rw-r--r--utils/unlit/ghc.mk2
-rw-r--r--utils/unlit/unlit.c19
-rw-r--r--utils/unlit/unlit.cabal3
26 files changed, 482 insertions, 34 deletions
diff --git a/.gitignore b/.gitignore
index efc7a89ea4..631d12f8fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,6 +180,11 @@ _darcs/
/utils/mkUserGuidePart/mkUserGuidePart.cabal
/utils/runghc/runghc.cabal
/utils/gen-dll/gen-dll.cabal
+utils/lndir/fs.*
+utils/unlit/fs.*
+rts/fs.*
+libraries/base/include/fs.h
+libraries/base/cbits/fs.c
/extra-gcc-opts
/sdistprep
diff --git a/configure.ac b/configure.ac
index 111def2cda..062004d904 100644
--- a/configure.ac
+++ b/configure.ac
@@ -670,6 +670,15 @@ dnl --------------------------------------------------------------
dnl End of configure script option section
dnl --------------------------------------------------------------
+dnl ** Copy the files from the "fs" utility into the right folders.
+dnl --------------------------------------------------------------
+AC_MSG_NOTICE([Creating links for in-tree file handling routines.])
+ln -f -v utils/fs/fs.* utils/lndir/
+ln -f -v utils/fs/fs.* utils/unlit/
+ln -f -v utils/fs/fs.* rts/
+ln -f -v utils/fs/fs.h libraries/base/include/
+ln -f -v utils/fs/fs.c libraries/base/cbits/
+AC_MSG_NOTICE([Routines in place. Packages can now be build normally.])
dnl --------------------------------------------------------------
dnl ** Can the unix package be built?
diff --git a/docs/users_guide/8.6.1-notes.rst b/docs/users_guide/8.6.1-notes.rst
index 548702159c..6300a3f3e4 100644
--- a/docs/users_guide/8.6.1-notes.rst
+++ b/docs/users_guide/8.6.1-notes.rst
@@ -82,8 +82,10 @@ Runtime system
else. This fixes ``iuuc`` on Windows given the proper search directories (e.g
``-L/mingw64/lib``).
- - The GHC runtime linker now uses ``LIBRARY_PATH`` and the runtime loader now also
- searches ``LD_LIBRARY_PATH``.
+- The GHC runtime linker now uses ``LIBRARY_PATH`` and the runtime loader now also
+ searches ``LD_LIBRARY_PATH``.
+
+- The GHC runtime on Windows is no longer constrained by MAX_PATH.
Template Haskell
~~~~~~~~~~~~~~~~
@@ -107,6 +109,48 @@ Template Haskell
Build system
~~~~~~~~~~~~
+Windows Paths
+~~~~~~~~~~~~~
+
+Windows paths are not all the same. The different kinds of paths each have
+different meanings. The MAX_PATH limitation is not a limitation of the Operating
+System nor the File System. It is a limitation of the default namespace enforced
+by the Win32 API for backwards compatibility.
+
+The NT Kernel however allows you ways to opt out of this path preprocessing by
+the Win32 APIs. This is done by explicitly using the desired namespace in the
+PATH.
+
+The namespaces are:
+
+ - file namespace: \\?\
+ - device namespace: \\.\
+ - nt namespace: \
+
+Each of these turn off Path processing completely by the Win32 API and the paths
+are passed untouched to the filesystem.
+
+Paths with a drive letter are `legacy` paths. The drive letters are actually
+meaningless to the kernel. Just like Unix operating systems, drive letters are
+just a mount point. You can view your mount points by using the `mountvol`
+command.
+
+The Haskell I/O manager will now automatically promote paths in the legacy
+format to Win32 file namespace. By default the I/O manager will do two things to
+your paths:
+
+ - replace / with \\
+ - expand relative paths to absolute paths
+
+If you want to opt out of all preprocessing just expliticly use namespaces in
+your paths. Due to this change, if you need to open raw devices (e.g. COM ports)
+you need to use the device namespace explicitly. (e.g. `\\.\COM1`). GHC and
+Haskell programs in general no longer support opening devices in the `legacy`
+format.
+
+See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx for
+more details.
+
Included libraries
------------------
diff --git a/ghc.mk b/ghc.mk
index 38c165d261..3573b7575b 100644
--- a/ghc.mk
+++ b/ghc.mk
@@ -1450,6 +1450,16 @@ distclean : clean
$(call removeTrees,inplace/mingw)
$(call removeTrees,inplace/perl)
+# Remove the fs utilities.
+ $(call removeFiles,utils/lndir/fs.h)
+ $(call removeFiles,utils/lndir/fs.c)
+ $(call removeFiles,utils/unlit/fs.h)
+ $(call removeFiles,utils/unlit/fs.c)
+ $(call removeFiles,rts/fs.h)
+ $(call removeFiles,rts/fs.c)
+ $(call removeFiles,libraries/base/include/fs.h)
+ $(call removeFiles,libraries/base/cbits/fs.c)
+
maintainer-clean : distclean
$(call removeFiles,configure mk/config.h.in)
$(call removeTrees,autom4te.cache $(wildcard libraries/*/autom4te.cache))
diff --git a/libraries/base/base.cabal b/libraries/base/base.cabal
index c7075b3280..0a91315a61 100644
--- a/libraries/base/base.cabal
+++ b/libraries/base/base.cabal
@@ -36,6 +36,7 @@ extra-source-files:
include/HsBaseConfig.h.in
include/ieee-flpt.h
include/md5.h
+ include/fs.h
install-sh
source-repository head
@@ -339,6 +340,7 @@ Library
cbits/md5.c
cbits/primFloat.c
cbits/sysconf.c
+ cbits/fs.c
cmm-sources:
cbits/CastFloatWord.cmm
diff --git a/libraries/base/include/HsBase.h b/libraries/base/include/HsBase.h
index 13640c590e..d5884473ca 100644
--- a/libraries/base/include/HsBase.h
+++ b/libraries/base/include/HsBase.h
@@ -520,13 +520,25 @@ extern void* __hscore_get_saved_termios(int fd);
extern void __hscore_set_saved_termios(int fd, void* ts);
#if defined(_WIN32)
+/* Defined in fs.c. */
+extern int __hs_swopen (const wchar_t* filename, int oflag, int shflag,
+ int pmode);
+
INLINE int __hscore_open(wchar_t *file, int how, mode_t mode) {
+ int result = -1;
if ((how & O_WRONLY) || (how & O_RDWR) || (how & O_APPEND))
- return _wsopen(file,how | _O_NOINHERIT,_SH_DENYNO,mode);
+ result = __hs_swopen(file,how | _O_NOINHERIT,_SH_DENYNO,mode);
// _O_NOINHERIT: see #2650
else
- return _wsopen(file,how | _O_NOINHERIT,_SH_DENYNO,mode);
+ result = __hs_swopen(file,how | _O_NOINHERIT,_SH_DENYNO,mode);
// _O_NOINHERIT: see #2650
+
+ /* This call is very important, otherwise the I/O system will not propagate
+ the correct error for why it failed. */
+ if (result == -1)
+ maperrno ();
+
+ return result;
}
#else
INLINE int __hscore_open(char *file, int how, mode_t mode) {
diff --git a/rts/Hpc.c b/rts/Hpc.c
index 7575e34ce0..9ba9b04b61 100644
--- a/rts/Hpc.c
+++ b/rts/Hpc.c
@@ -13,6 +13,7 @@
#include <ctype.h>
#include <string.h>
#include <assert.h>
+#include <fs_rts.h>
#if defined(HAVE_SYS_TYPES_H)
#include <sys/types.h>
@@ -233,7 +234,7 @@ startupHpc(void)
sprintf(tixFilename, "%s.tix", prog_name);
}
- if (init_open(fopen(tixFilename,"r"))) {
+ if (init_open(__rts_fopen(tixFilename,"r"))) {
readTix();
}
}
@@ -387,7 +388,7 @@ exitHpc(void) {
// not clober the .tix file.
if (hpc_pid == getpid()) {
- FILE *f = fopen(tixFilename,"w");
+ FILE *f = __rts_fopen(tixFilename,"w");
writeTix(f);
}
diff --git a/rts/Linker.c b/rts/Linker.c
index 8f55c296a6..aa6ec7fe7a 100644
--- a/rts/Linker.c
+++ b/rts/Linker.c
@@ -48,6 +48,7 @@
#include <string.h>
#include <stdio.h>
#include <assert.h>
+#include <fs_rts.h>
#if defined(HAVE_SYS_STAT_H)
#include <sys/stat.h>
@@ -711,7 +712,7 @@ addDLL( pathchar *dll_name )
strncpy(line, (errmsg+(match[1].rm_so)),match_length);
line[match_length] = '\0'; // make sure string is null-terminated
IF_DEBUG(linker, debugBelch ("file name = '%s'\n", line));
- if ((fp = fopen(line, "r")) == NULL) {
+ if ((fp = __rts_fopen(line, "r")) == NULL) {
return errmsg; // return original error if open fails
}
// try to find a GROUP or INPUT ( ... ) command
diff --git a/rts/PathUtils.h b/rts/PathUtils.h
index 152606a7b0..0b35b214e0 100644
--- a/rts/PathUtils.h
+++ b/rts/PathUtils.h
@@ -14,7 +14,7 @@
#if defined(mingw32_HOST_OS)
#define pathcmp wcscmp
#define pathlen wcslen
-#define pathopen _wfopen
+#define pathopen __rts_fwopen
#define pathstat _wstat
#define struct_stat struct _stat
#define open wopen
diff --git a/rts/ProfHeap.c b/rts/ProfHeap.c
index cb6a13c897..4a7b6d38a8 100644
--- a/rts/ProfHeap.c
+++ b/rts/ProfHeap.c
@@ -23,6 +23,7 @@
#include "Trace.h"
#include "sm/GCThread.h"
+#include <fs_rts.h>
#include <string.h>
/* -----------------------------------------------------------------------------
@@ -340,7 +341,7 @@ void initProfiling (void)
sprintf(hp_filename, "%s.hp", prog);
/* open the log file */
- if ((hp_file = fopen(hp_filename, "w")) == NULL) {
+ if ((hp_file = __rts_fopen(hp_filename, "w")) == NULL) {
debugBelch("Can't open profiling report file %s\n",
hp_filename);
RtsFlags.ProfFlags.doHeapProfile = 0;
diff --git a/rts/Profiling.c b/rts/Profiling.c
index 803f86befc..9f1a442951 100644
--- a/rts/Profiling.c
+++ b/rts/Profiling.c
@@ -22,6 +22,7 @@
#include "Printer.h"
#include "Capability.h"
+#include <fs_rts.h>
#include <string.h>
#if defined(DEBUG)
@@ -264,7 +265,7 @@ initProfilingLogFile(void)
sprintf(prof_filename, "%s.prof", stem);
/* open the log file */
- if ((prof_file = fopen(prof_filename, "w")) == NULL) {
+ if ((prof_file = __rts_fopen(prof_filename, "w")) == NULL) {
debugBelch("Can't open profiling report file %s\n", prof_filename);
RtsFlags.CcFlags.doCostCentres = 0;
// Retainer profiling (`-hr` or `-hr<cc> -h<x>`) writes to
@@ -281,7 +282,7 @@ initProfilingLogFile(void)
sprintf(hp_filename, "%s.hp", stem);
/* open the log file */
- if ((hp_file = fopen(hp_filename, "w")) == NULL) {
+ if ((hp_file = __rts_fopen(hp_filename, "w")) == NULL) {
debugBelch("Can't open profiling report file %s\n",
hp_filename);
RtsFlags.ProfFlags.doHeapProfile = 0;
diff --git a/rts/RtsFlags.c b/rts/RtsFlags.c
index b674e9b685..abb980039d 100644
--- a/rts/RtsFlags.c
+++ b/rts/RtsFlags.c
@@ -31,6 +31,8 @@
#include <sys/types.h>
#endif
+#include <fs_rts.h>
+
// Flag Structure
RTS_FLAGS RtsFlags;
@@ -1697,7 +1699,7 @@ openStatsFile (char *filename, // filename, or NULL
f = NULL; /* NULL means use debugBelch */
} else {
if (*filename != '\0') { /* stats file specified */
- f = fopen(filename,"w");
+ f = __rts_fopen (filename,"w");
} else {
if (filename_fmt == NULL) {
errorBelch("Invalid stats filename format (NULL)\n");
@@ -1707,7 +1709,7 @@ openStatsFile (char *filename, // filename, or NULL
char stats_filename[STATS_FILENAME_MAXLEN];
snprintf(stats_filename, STATS_FILENAME_MAXLEN, filename_fmt,
prog_name);
- f = fopen(stats_filename,"w");
+ f = __rts_fopen (stats_filename,"w");
}
if (f == NULL) {
errorBelch("Can't open stats file %s\n", filename);
diff --git a/rts/RtsSymbols.c b/rts/RtsSymbols.c
index d5800fd336..aa95660945 100644
--- a/rts/RtsSymbols.c
+++ b/rts/RtsSymbols.c
@@ -106,6 +106,7 @@
RTS_WIN64_ONLY(SymI_HasProto(__imp__environ)) \
RTS_WIN32_ONLY(SymI_HasProto(_imp___iob)) \
RTS_WIN64_ONLY(SymI_HasProto(__iob_func)) \
+ RTS_WIN64_ONLY(SymI_HasProto(__mingw_vsnwprintf)) \
/* see Note [Symbols for MinGW's printf] */ \
SymI_HasProto(_lock_file) \
SymI_HasProto(_unlock_file)
diff --git a/rts/eventlog/EventLogWriter.c b/rts/eventlog/EventLogWriter.c
index d8e5a44192..e6f560fc24 100644
--- a/rts/eventlog/EventLogWriter.c
+++ b/rts/eventlog/EventLogWriter.c
@@ -14,6 +14,7 @@
#include <string.h>
#include <stdio.h>
+#include <fs_rts.h>
#if defined(HAVE_SYS_TYPES_H)
#include <sys/types.h>
#endif
@@ -71,7 +72,7 @@ initEventLogFileWriter(void)
stgFree(prog);
/* Open event log file for writing. */
- if ((event_log_file = fopen(event_log_filename, "wb")) == NULL) {
+ if ((event_log_file = __rts_fopen(event_log_filename, "wb")) == NULL) {
sysErrorBelch(
"initEventLogFileWriter: can't open %s", event_log_filename);
stg_exit(EXIT_FAILURE);
diff --git a/rts/fs_rts.h b/rts/fs_rts.h
new file mode 100644
index 0000000000..12c27ccc8c
--- /dev/null
+++ b/rts/fs_rts.h
@@ -0,0 +1,15 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) Tamar Christina 2018
+ *
+ * Hack to get around linkewhole issues on linux. The FS utilities need to be in
+ * a different namespace to allow the linking.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#pragma once
+
+#undef FS_NAMESPACE
+#define FS_NAMESPACE rts
+
+#include "fs.h"
diff --git a/rts/ghc.mk b/rts/ghc.mk
index 761cc43b8f..9d1c2a50f6 100644
--- a/rts/ghc.mk
+++ b/rts/ghc.mk
@@ -332,7 +332,7 @@ endif
STANDARD_OPTS += $(addprefix -I,$(GHC_INCLUDE_DIRS)) -Irts -Irts/dist/build
# COMPILING_RTS is only used when building Win32 DLL support.
-STANDARD_OPTS += -DCOMPILING_RTS
+STANDARD_OPTS += -DCOMPILING_RTS -DFS_NAMESPACE=rts
# HC_OPTS is included in both .c and .cmm compilations, whereas CC_OPTS is
# only included in .c compilations. HC_OPTS included the WAY_* opts, which
diff --git a/rts/linker/LoadArchive.c b/rts/linker/LoadArchive.c
index 3c4bd44a28..7f00edbed8 100644
--- a/rts/linker/LoadArchive.c
+++ b/rts/linker/LoadArchive.c
@@ -22,6 +22,7 @@
#include <string.h>
#include <stddef.h>
#include <ctype.h>
+#include <fs_rts.h>
#define FAIL(...) do {\
errorBelch("loadArchive: "__VA_ARGS__); \
diff --git a/rts/linker/PEi386.c b/rts/linker/PEi386.c
index 364f7780b2..49aa16d3e3 100644
--- a/rts/linker/PEi386.c
+++ b/rts/linker/PEi386.c
@@ -2025,6 +2025,9 @@ SymbolAddr *lookupSymbol_PEi386(SymbolName *lbl)
zapTrailingAtSign ( (unsigned char*)lbl );
#endif
sym = lookupSymbolInDLLs((unsigned char*)lbl);
+ /* TODO: We should really cache this symbol now that we've loaded it.
+ The system loader is fast, but not fast enough to keep wasting
+ cycles like this. */
return sym; // might be NULL if not found
} else {
#if defined(mingw32_HOST_OS)
diff --git a/rts/rts.cabal.in b/rts/rts.cabal.in
index 67fb9c5d92..0b9a8c4efd 100644
--- a/rts/rts.cabal.in
+++ b/rts/rts.cabal.in
@@ -129,6 +129,8 @@ library
rts/Timer.h
rts/Types.h
rts/Utils.h
+ rts/fs.h
+ rts/fs_rts.h
rts/prof/CCS.h
rts/prof/LDV.h
rts/storage/Block.h
@@ -429,6 +431,7 @@ library
sm/Storage.c
sm/Sweep.c
xxhash.c
+ fs.c
-- I wish we had wildcards..., this would be:
-- *.c hooks/**/*.c sm/**/*.c eventlog/**/*.c linker/**/*.c
if os(windows)
diff --git a/utils/fs/README b/utils/fs/README
new file mode 100644
index 0000000000..5011939a38
--- /dev/null
+++ b/utils/fs/README
@@ -0,0 +1,4 @@
+This "fs" library, used by various ghc utilities is used to share some common
+I/O filesystem functions with different packages.
+
+This file is copied across the build-system by configure.
diff --git a/utils/fs/fs.c b/utils/fs/fs.c
new file mode 100644
index 0000000000..24bf3a3632
--- /dev/null
+++ b/utils/fs/fs.c
@@ -0,0 +1,293 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) Tamar Christina 2018
+ *
+ * Windows I/O routines for file opening.
+ *
+ * NOTE: Only modify this file in utils/fs/ and rerun configure. Do not edit
+ * this file in any other directory as it will be overwritten.
+ *
+ * ---------------------------------------------------------------------------*/
+#include "fs.h"
+#include <stdio.h>
+
+#if defined(_WIN32)
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#include <wchar.h>
+#include <sys\stat.h>
+#include <sys\types.h>
+#include <share.h>
+
+/* This function converts Windows paths between namespaces. More specifically
+ It converts an explorer style path into a NT or Win32 namespace.
+ This has several caveats but they are caviats that are native to Windows and
+ not POSIX. See
+ https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247.aspx.
+ Anything else such as raw device paths we leave untouched. The main benefit
+ of doing any of this is that we can break the MAX_PATH restriction and also
+ access raw handles that we couldn't before. */
+static wchar_t* __hs_create_device_name (const wchar_t* filename) {
+ const wchar_t* win32_dev_namespace = L"\\\\.\\";
+ const wchar_t* win32_file_namespace = L"\\\\?\\";
+ const wchar_t* nt_device_namespace = L"\\Device\\";
+ const wchar_t* unc_prefix = L"UNC\\";
+ const wchar_t* network_share = L"\\\\";
+
+ wchar_t* result = _wcsdup (filename);
+ wchar_t ns[10] = {0};
+
+ /* If the file is already in a native namespace don't change it. */
+ if ( wcsncmp (win32_dev_namespace , filename, 4) == 0
+ || wcsncmp (win32_file_namespace, filename, 4) == 0
+ || wcsncmp (nt_device_namespace , filename, 8) == 0)
+ return result;
+
+ /* Since we're using the lower level APIs we must normalize slashes now. The
+ Win32 API layer will no longer convert '/' into '\\' for us. */
+ for (size_t i = 0; i < wcslen (result); i++)
+ {
+ if (result[i] == L'/')
+ result[i] = L'\\';
+ }
+
+ /* Now resolve any . and .. in the path or subsequent API calls may fail since
+ Win32 will no longer resolve them. */
+ DWORD nResult = GetFullPathNameW (result, 0, NULL, NULL) + 1;
+ wchar_t *temp = _wcsdup (result);
+ result = malloc (nResult * sizeof (wchar_t));
+ if (GetFullPathNameW (temp, nResult, result, NULL) == 0)
+ {
+ goto cleanup;
+ }
+
+ free (temp);
+
+ if (wcsncmp (network_share, result, 2) == 0)
+ {
+ if (swprintf (ns, 10, L"%ls%ls", win32_file_namespace, unc_prefix) <= 0)
+ {
+ goto cleanup;
+ }
+ }
+ else if (swprintf (ns, 10, L"%ls", win32_file_namespace) <= 0)
+ {
+ goto cleanup;
+ }
+
+ /* Create new string. */
+ int bLen = wcslen (result) + wcslen (ns) + 1;
+ temp = _wcsdup (result);
+ result = malloc (bLen * sizeof (wchar_t));
+ if (swprintf (result, bLen, L"%ls%ls", ns, temp) <= 0)
+ {
+ goto cleanup;
+ }
+
+ free (temp);
+
+ return result;
+
+cleanup:
+ free (temp);
+ free (result);
+ return NULL;
+}
+
+#define HAS_FLAG(a,b) ((a & b) == b)
+
+int FS(swopen) (const wchar_t* filename, int oflag, int shflag, int pmode)
+{
+ /* Construct access mode. */
+ DWORD dwDesiredAccess = 0;
+ if (HAS_FLAG (oflag, _O_RDONLY))
+ dwDesiredAccess |= GENERIC_READ | FILE_READ_DATA | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_ATTRIBUTES;;
+ if (HAS_FLAG (oflag, _O_RDWR))
+ dwDesiredAccess |= GENERIC_WRITE | GENERIC_READ | FILE_READ_DATA |
+ FILE_WRITE_DATA | FILE_READ_ATTRIBUTES |
+ FILE_WRITE_ATTRIBUTES;
+ if (HAS_FLAG (oflag, _O_WRONLY))
+ dwDesiredAccess|= GENERIC_WRITE | FILE_WRITE_DATA |
+ FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
+
+ /* Construct shared mode. */
+ DWORD dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;
+ if (HAS_FLAG (shflag, _SH_DENYRW))
+ dwShareMode &= ~(FILE_SHARE_READ | FILE_SHARE_WRITE);
+ if (HAS_FLAG (shflag, _SH_DENYWR))
+ dwShareMode &= ~FILE_SHARE_WRITE;
+ if (HAS_FLAG (shflag, _SH_DENYRD))
+ dwShareMode &= ~FILE_SHARE_READ;
+ if (HAS_FLAG (pmode, _S_IWRITE))
+ dwShareMode |= FILE_SHARE_READ | FILE_SHARE_WRITE;
+ if (HAS_FLAG (pmode, _S_IREAD))
+ dwShareMode |= FILE_SHARE_READ;
+
+ /* Override access mode with pmode if creating file. */
+ if (HAS_FLAG (oflag, _O_CREAT))
+ {
+ if (HAS_FLAG (pmode, _S_IWRITE))
+ dwDesiredAccess |= FILE_GENERIC_WRITE;
+ if (HAS_FLAG (pmode, _S_IREAD))
+ dwDesiredAccess |= FILE_GENERIC_READ;
+ }
+
+ /* Create file disposition. */
+ DWORD dwCreationDisposition = OPEN_EXISTING;
+ if (HAS_FLAG (oflag, _O_CREAT))
+ dwCreationDisposition = OPEN_ALWAYS;
+ if (HAS_FLAG (oflag, (_O_CREAT | _O_EXCL)))
+ dwCreationDisposition = CREATE_NEW;
+ if (HAS_FLAG (oflag, _O_TRUNC) && !HAS_FLAG (oflag, _O_CREAT))
+ dwCreationDisposition = TRUNCATE_EXISTING;
+
+ /* Set file access attributes. */
+ DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+ if (HAS_FLAG (oflag, _O_RDONLY))
+ dwFlagsAndAttributes |= 0; /* No special attribute. */
+ if (HAS_FLAG (oflag, (_O_CREAT | _O_TEMPORARY)))
+ dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (HAS_FLAG (oflag, (_O_CREAT | _O_SHORT_LIVED)))
+ dwFlagsAndAttributes |= FILE_ATTRIBUTE_TEMPORARY;
+ if (HAS_FLAG (oflag, _O_RANDOM))
+ dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
+ if (HAS_FLAG (oflag, _O_SEQUENTIAL))
+ dwFlagsAndAttributes |= FILE_FLAG_SEQUENTIAL_SCAN;
+ /* Flag is only valid on it's own. */
+ if (dwFlagsAndAttributes != FILE_ATTRIBUTE_NORMAL)
+ dwFlagsAndAttributes &= ~FILE_ATTRIBUTE_NORMAL;
+
+ /* Set security attributes. */
+ SECURITY_ATTRIBUTES securityAttributes;
+ ZeroMemory (&securityAttributes, sizeof(SECURITY_ATTRIBUTES));
+ securityAttributes.bInheritHandle = !(oflag & _O_NOINHERIT);
+ securityAttributes.lpSecurityDescriptor = NULL;
+ securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
+
+ wchar_t* _filename = __hs_create_device_name (filename);
+ if (!_filename)
+ return -1;
+
+ HANDLE hResult
+ = CreateFileW (_filename, dwDesiredAccess, dwShareMode, &securityAttributes,
+ dwCreationDisposition, dwFlagsAndAttributes, NULL);
+ free (_filename);
+ if (INVALID_HANDLE_VALUE == hResult)
+ return -1;
+
+ /* Now we have a Windows handle, we have to convert it to an FD and apply
+ the remaining flags. */
+ const int flag_mask = _O_APPEND | _O_RDONLY | _O_TEXT | _O_WTEXT;
+ int fd = _open_osfhandle ((intptr_t)hResult, oflag & flag_mask);
+ if (-1 == fd)
+ return -1;
+
+ /* Finally we can change the mode to the requested one. */
+ const int mode_mask = _O_TEXT | _O_BINARY | _O_U16TEXT | _O_U8TEXT | _O_WTEXT;
+ if ((oflag & mode_mask) && (-1 == _setmode (fd, oflag & mode_mask)))
+ return -1;
+
+ return fd;
+}
+
+FILE *FS(fwopen) (const wchar_t* filename, const wchar_t* mode)
+{
+ int shflag = 0;
+ int pmode = 0;
+ int oflag = 0;
+
+ int len = wcslen (mode);
+ int i;
+ #define IS_EXT(X) ((i < (len - 1)) && mode[i] == X)
+
+ for (i = 0; i < len; i++)
+ {
+ switch (mode[i])
+ {
+ case L'a':
+ if (IS_EXT (L'+'))
+ oflag |= _O_RDWR | _O_CREAT | _O_APPEND;
+ else
+ oflag |= _O_WRONLY | _O_CREAT | _O_APPEND;
+ break;
+ case L'r':
+ if (IS_EXT (L'+'))
+ oflag |= _O_RDWR;
+ else
+ oflag |= _O_RDONLY;
+ break;
+ case L'w':
+ if (IS_EXT (L'+'))
+ oflag |= _O_RDWR | _O_CREAT | _O_TRUNC;
+ else
+ oflag |= _O_WRONLY | _O_CREAT | _O_TRUNC;
+ break;
+ case L'b':
+ oflag |= _O_BINARY;
+ break;
+ case L't':
+ oflag |= _O_TEXT;
+ break;
+ case L'c':
+ case L'n':
+ oflag |= 0;
+ break;
+ case L'S':
+ oflag |= _O_SEQUENTIAL;
+ break;
+ case L'R':
+ oflag |= _O_RANDOM;
+ break;
+ case L'T':
+ oflag |= _O_SHORT_LIVED;
+ break;
+ case L'D':
+ oflag |= _O_TEMPORARY;
+ break;
+ default:
+ if (wcsncmp (mode, L"ccs=UNICODE", 11) == 0)
+ oflag |= _O_WTEXT;
+ else if (wcsncmp (mode, L"ccs=UTF-8", 9) == 0)
+ oflag |= _O_U8TEXT;
+ else if (wcsncmp (mode, L"ccs=UTF-16LE", 12) == 0)
+ oflag |= _O_U16TEXT;
+ else continue;
+ }
+ }
+ #undef IS_EXT
+
+ int fd = FS(swopen) (filename, oflag, shflag, pmode);
+ FILE* file = _wfdopen (fd, mode);
+ return file;
+}
+
+FILE *FS(fopen) (const char* filename, const char* mode)
+{
+ size_t len = mbstowcs (NULL, filename, 0);
+ wchar_t *w_filename = malloc (sizeof (wchar_t) * (len + 1));
+ mbstowcs (w_filename, filename, len);
+ w_filename[len] = L'\0';
+
+ len = mbstowcs (NULL, mode, 0);
+ wchar_t *w_mode = malloc (sizeof (wchar_t) * (len + 1));
+ mbstowcs (w_mode, mode, len);
+ w_mode[len] = L'\0';
+
+ FILE *result = FS(fwopen) (w_filename, w_mode);
+ free (w_filename);
+ free (w_mode);
+ return result;
+}
+#else
+FILE *FS(fopen) (const char* filename, const char* mode)
+{
+ return fopen (filename, mode);
+}
+#endif
diff --git a/utils/fs/fs.h b/utils/fs/fs.h
new file mode 100644
index 0000000000..ab2eded2a1
--- /dev/null
+++ b/utils/fs/fs.h
@@ -0,0 +1,36 @@
+/* -----------------------------------------------------------------------------
+ *
+ * (c) Tamar Christina 2018
+ *
+ * Windows I/O routines for file opening.
+ *
+ * NOTE: Only modify this file in utils/fs/ and rerun configure. Do not edit
+ * this file in any other directory as it will be overwritten.
+ *
+ * ---------------------------------------------------------------------------*/
+
+#pragma once
+
+#include <stdio.h>
+
+#if !defined(FS_NAMESPACE)
+#define FS_NAMESPACE hs
+#endif
+
+/* Play some dirty tricks to get CPP to expand correctly. */
+#define FS_FULL(ns, name) __##ns##_##name
+#define prefix FS_NAMESPACE
+#define FS_L(p, n) FS_FULL(p, n)
+#define FS(name) FS_L(prefix, name)
+
+#if defined(_WIN32)
+#include <wchar.h>
+
+int FS(swopen) (const wchar_t* filename, int oflag,
+ int shflag, int pmode);
+FILE *FS(fwopen) (const wchar_t* filename, const wchar_t* mode);
+FILE *FS(fopen) (const char* filename, const char* mode);
+#else
+
+FILE *FS(fopen) (const char* filename, const char* mode);
+#endif
diff --git a/utils/lndir/lndir.c b/utils/lndir/lndir.c
index 87f2824166..8ea5ab2ab4 100644
--- a/utils/lndir/lndir.c
+++ b/utils/lndir/lndir.c
@@ -2,7 +2,7 @@
/* Create shadow link tree (after X11R4 script of the same name)
Mark Reinhold (mbr@lcs.mit.edu)/3 January 1990 */
-/*
+/*
Copyright (c) 1990, X Consortium
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -46,6 +46,7 @@ in this Software without prior written authorization from the X Consortium.
#define NeedVarargsPrototypes 1
#include "lndir-Xos.h"
+#include "fs.h"
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
@@ -182,11 +183,11 @@ int copyfile(const char *oldpath, const char *newpath) {
return symlink(oldpath, newpath);
} else {
#endif
- f_old = fopen(oldpath, "rb");
+ f_old = __hs_fopen(oldpath, "rb");
if (f_old == NULL) {
return -1;
}
- f_new = fopen(newpath, "wbx");
+ f_new = __hs_fopen(newpath, "wbx");
if (f_new == NULL) {
e = errno;
fclose(f_old);
@@ -272,7 +273,7 @@ int rel; /* if true, prepend "../" to fn before using */
else
buf[0] = '\0';
strcat (buf, fn);
-
+
if (!(df = opendir (buf))) {
msg ("%s: Cannot opendir", buf);
return 1;
@@ -305,7 +306,7 @@ int rel; /* if true, prepend "../" to fn before using */
#if defined(S_ISDIR)
if(S_ISDIR(sb.st_mode))
#else
- if (sb.st_mode & S_IFDIR)
+ if (sb.st_mode & S_IFDIR)
#endif
{
/* directory */
@@ -397,7 +398,7 @@ int rel; /* if true, prepend "../" to fn before using */
mperror (dp->d_name);
}
}
-
+
closedir (df);
return 0;
}
@@ -410,7 +411,7 @@ char **av;
char* tn;
struct stat fs, ts;
#if defined(__CYGWIN32__)
- /*
+ /*
The lndir code assumes unix-style paths to work. cygwin
lets you get away with using dos'ish paths (e.g., "f:/oo")
in most contexts. Using them with 'lndir' will seriously
@@ -457,7 +458,7 @@ char **av;
if (stat (tn, &ts) < 0) {
if (force && (tn[0] != '.' || tn[1] != '\0') ) {
mymkdir(tn, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH );
- }
+ }
else {
quiterr (1, tn);
#if defined(S_ISDIR)
diff --git a/utils/unlit/ghc.mk b/utils/unlit/ghc.mk
index 8911f4e856..0560aa57b8 100644
--- a/utils/unlit/ghc.mk
+++ b/utils/unlit/ghc.mk
@@ -11,7 +11,7 @@
# -----------------------------------------------------------------------------
# built by ghc-stage0
-utils/unlit_dist_C_SRCS = unlit.c
+utils/unlit_dist_C_SRCS = unlit.c fs.c
utils/unlit_dist_PROGNAME = unlit
utils/unlit_dist_TOPDIR = YES
utils/unlit_dist_INSTALL_INPLACE = YES
diff --git a/utils/unlit/unlit.c b/utils/unlit/unlit.c
index 4eb91d71be..97f853b268 100644
--- a/utils/unlit/unlit.c
+++ b/utils/unlit/unlit.c
@@ -7,7 +7,7 @@
* column on each line. It is hoped that this style of programming will
* encourage the writing of accurate and clearly documented programs
* in which the writer may include motivating arguments, examples
- * and explanations.
+ * and explanations.
*
* Unlit is a filter that can be used to strip all of the comment lines
* out of a literate script file. The command format for unlit is:
@@ -40,6 +40,7 @@
* And \begin{pseudocode} ... \end{pseudocode}. -- LA
*/
+#include "fs.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
@@ -115,7 +116,7 @@ static void myputc(char c, FILE *ostream)
{
if (putc(c,ostream) == EOF) {
writeerror();
- }
+ }
}
#define TABPOS 8
@@ -179,7 +180,7 @@ static line readline(FILE *istream, FILE *ostream) {
if (c==EOF)
return ENDFILE;
-
+
if ( c == '#' ) {
if ( ignore_shebang ) {
c1 = egetc(istream);
@@ -335,10 +336,10 @@ int main(int argc,char **argv)
else if (strcmp(*argv,"-h")==0) {
if (argc > 1) {
argc--; argv++;
- if (prefix_str)
+ if (prefix_str)
free(prefix_str);
prefix_str = (char*)malloc(sizeof(char)*(1+strlen(*argv)));
- if (prefix_str)
+ if (prefix_str)
strcpy(prefix_str, *argv);
}
} else if (strcmp(*argv,"-#")==0)
@@ -362,16 +363,16 @@ int main(int argc,char **argv)
file = "stdin";
}
else
- if ((istream=fopen(argv[0], "r")) == NULL) {
+ if ((istream=__hs_fopen(argv[0], "r")) == NULL) {
fprintf(stderr, CANNOTOPEN, argv[0]);
exit(1);
}
ofilename=argv[1];
- if (strcmp(argv[1], "-")==0)
- ostream = stdout;
+ if (strcmp(argv[1], "-")==0)
+ ostream = stdout;
else
- if ((ostream=fopen(argv[1], "w")) == NULL) {
+ if ((ostream=__hs_fopen(argv[1], "w")) == NULL) {
fprintf(stderr, CANNOTOPEN, argv[1]);
exit(1);
}
diff --git a/utils/unlit/unlit.cabal b/utils/unlit/unlit.cabal
index a621f04bc7..622a55934d 100644
--- a/utils/unlit/unlit.cabal
+++ b/utils/unlit/unlit.cabal
@@ -13,4 +13,5 @@ build-type: Simple
Executable unlit
Default-Language: Haskell2010
Main-Is: unlit.c
- C-Sources: unlit.c
+ C-Sources: unlit.c, fs.c
+ Includes: fs.h