Commit 4de585a5 authored by Tamar Christina's avatar Tamar Christina

Remove MAX_PATH restrictions from RTS, I/O manager and various utilities

Summary:
This shims out fopen and sopen so that they use modern APIs under the hood
along with namespaced paths.

This lifts the MAX_PATH restrictions from Haskell programs and makes the new
limit ~32k.

There are only some slight caveats that have been documented.

Some utilities have not been upgraded such as lndir, since all these things are
different cabal packages I have been forced to copy the source in different places
which is less than ideal. But it's the only way to keep sdist working.

Test Plan: ./validate

Reviewers: hvr, bgamari, erikd, simonmar

Reviewed By: bgamari

Subscribers: rwbarton, thomie, carter

GHC Trac Issues: #10822

Differential Revision: https://phabricator.haskell.org/D4416
parent afb686a8
......@@ -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
......
......@@ -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?
......
......@@ -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
------------------
......
......@@ -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))
......
......@@ -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
......
......@@ -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) {
......
......@@ -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);
}
......
......@@ -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
......
......@@ -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
......
......@@ -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;
......
......@@ -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;
......
......@@ -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);
......
......@@ -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)
......
......@@ -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);
......
/* -----------------------------------------------------------------------------
*
* (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"
......@@ -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
......
......@@ -22,6 +22,7 @@
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include <fs_rts.h>
#define FAIL(...) do {\
errorBelch("loadArchive: "__VA_ARGS__); \
......
......@@ -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)
......
......@@ -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)
......
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.
/* -----------------------------------------------------------------------------
*
* (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
/* -----------------------------------------------------------------------------
*
* (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
......@@ -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'