/* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
- Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
+
+Copyright (C) 1994-1995, 2000-2014 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include <sddl.h>
#include <sys/acl.h>
+#include <acl.h>
/* This is not in MinGW's sddl.h (but they are in MSVC headers), so we
define them by hand if not already defined. */
static BOOL g_b_init_get_sid_sub_authority;
static BOOL g_b_init_get_sid_sub_authority_count;
static BOOL g_b_init_get_security_info;
-static BOOL g_b_init_get_file_security;
+static BOOL g_b_init_get_file_security_w;
+static BOOL g_b_init_get_file_security_a;
static BOOL g_b_init_get_security_descriptor_owner;
static BOOL g_b_init_get_security_descriptor_group;
static BOOL g_b_init_is_valid_sid;
static BOOL g_b_init_copy_sid;
static BOOL g_b_init_get_native_system_info;
static BOOL g_b_init_get_system_times;
-static BOOL g_b_init_create_symbolic_link;
+static BOOL g_b_init_create_symbolic_link_w;
+static BOOL g_b_init_create_symbolic_link_a;
static BOOL g_b_init_get_security_descriptor_dacl;
static BOOL g_b_init_convert_sd_to_sddl;
static BOOL g_b_init_convert_sddl_to_sd;
static BOOL g_b_init_is_valid_security_descriptor;
-static BOOL g_b_init_set_file_security;
+static BOOL g_b_init_set_file_security_w;
+static BOOL g_b_init_set_file_security_a;
+static BOOL g_b_init_set_named_security_info_w;
+static BOOL g_b_init_set_named_security_info_a;
static BOOL g_b_init_get_adapters_info;
/*
#ifdef _UNICODE
const char * const LookupAccountSid_Name = "LookupAccountSidW";
-const char * const GetFileSecurity_Name = "GetFileSecurityW";
-const char * const SetFileSecurity_Name = "SetFileSecurityW";
#else
const char * const LookupAccountSid_Name = "LookupAccountSidA";
-const char * const GetFileSecurity_Name = "GetFileSecurityA";
-const char * const SetFileSecurity_Name = "SetFileSecurityA";
#endif
typedef BOOL (WINAPI * LookupAccountSid_Proc) (
LPCTSTR lpSystemName,
PACL *ppDacl,
PACL *ppSacl,
PSECURITY_DESCRIPTOR *ppSecurityDescriptor);
-typedef BOOL (WINAPI * GetFileSecurity_Proc) (
- LPCTSTR lpFileName,
+typedef BOOL (WINAPI * GetFileSecurityW_Proc) (
+ LPCWSTR lpFileName,
+ SECURITY_INFORMATION RequestedInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor,
+ DWORD nLength,
+ LPDWORD lpnLengthNeeded);
+typedef BOOL (WINAPI * GetFileSecurityA_Proc) (
+ LPCSTR lpFileName,
SECURITY_INFORMATION RequestedInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
DWORD nLength,
LPDWORD lpnLengthNeeded);
-typedef BOOL (WINAPI *SetFileSecurity_Proc) (
- LPCTSTR lpFileName,
+typedef BOOL (WINAPI *SetFileSecurityW_Proc) (
+ LPCWSTR lpFileName,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef BOOL (WINAPI *SetFileSecurityA_Proc) (
+ LPCSTR lpFileName,
+ SECURITY_INFORMATION SecurityInformation,
+ PSECURITY_DESCRIPTOR pSecurityDescriptor);
+typedef DWORD (WINAPI *SetNamedSecurityInfoW_Proc) (
+ LPCWSTR lpObjectName,
+ SE_OBJECT_TYPE ObjectType,
+ SECURITY_INFORMATION SecurityInformation,
+ PSID psidOwner,
+ PSID psidGroup,
+ PACL pDacl,
+ PACL pSacl);
+typedef DWORD (WINAPI *SetNamedSecurityInfoA_Proc) (
+ LPCSTR lpObjectName,
+ SE_OBJECT_TYPE ObjectType,
+ SECURITY_INFORMATION SecurityInformation,
+ PSID psidOwner,
+ PSID psidGroup,
+ PACL pDacl,
+ PACL pSacl);
typedef BOOL (WINAPI * GetSecurityDescriptorOwner_Proc) (
PSECURITY_DESCRIPTOR pSecurityDescriptor,
PSID *pOwner,
LPFILETIME lpIdleTime,
LPFILETIME lpKernelTime,
LPFILETIME lpUserTime);
-typedef BOOLEAN (WINAPI *CreateSymbolicLink_Proc) (
- LPTSTR lpSymlinkFileName,
- LPTSTR lpTargetFileName,
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkW_Proc) (
+ LPCWSTR lpSymlinkFileName,
+ LPCWSTR lpTargetFileName,
+ DWORD dwFlags);
+typedef BOOLEAN (WINAPI *CreateSymbolicLinkA_Proc) (
+ LPCSTR lpSymlinkFileName,
+ LPCSTR lpTargetFileName,
DWORD dwFlags);
typedef BOOL (WINAPI *ConvertStringSecurityDescriptorToSecurityDescriptor_Proc) (
LPCTSTR StringSecurityDescriptor,
}
static BOOL WINAPI
-get_file_security (LPCTSTR lpFileName,
+get_file_security (const char *lpFileName,
SECURITY_INFORMATION RequestedInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor,
DWORD nLength,
LPDWORD lpnLengthNeeded)
{
- static GetFileSecurity_Proc s_pfn_Get_File_Security = NULL;
+ static GetFileSecurityA_Proc s_pfn_Get_File_SecurityA = NULL;
+ static GetFileSecurityW_Proc s_pfn_Get_File_SecurityW = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
return FALSE;
}
- if (g_b_init_get_file_security == 0)
+ if (w32_unicode_filenames)
{
- g_b_init_get_file_security = 1;
- hm_advapi32 = LoadLibrary ("Advapi32.dll");
- s_pfn_Get_File_Security =
- (GetFileSecurity_Proc) GetProcAddress (
- hm_advapi32, GetFileSecurity_Name);
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_get_file_security_w == 0)
+ {
+ g_b_init_get_file_security_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Get_File_SecurityW =
+ (GetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+ "GetFileSecurityW");
+ }
+ if (s_pfn_Get_File_SecurityW == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_utf16 (lpFileName, filename_w);
+ return (s_pfn_Get_File_SecurityW (filename_w, RequestedInformation,
+ pSecurityDescriptor, nLength,
+ lpnLengthNeeded));
}
- if (s_pfn_Get_File_Security == NULL)
+ else
{
- errno = ENOTSUP;
- return FALSE;
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_get_file_security_a == 0)
+ {
+ g_b_init_get_file_security_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Get_File_SecurityA =
+ (GetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+ "GetFileSecurityA");
+ }
+ if (s_pfn_Get_File_SecurityA == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_ansi (lpFileName, filename_a);
+ return (s_pfn_Get_File_SecurityA (filename_a, RequestedInformation,
+ pSecurityDescriptor, nLength,
+ lpnLengthNeeded));
}
- return (s_pfn_Get_File_Security (lpFileName, RequestedInformation,
- pSecurityDescriptor, nLength,
- lpnLengthNeeded));
}
static BOOL WINAPI
-set_file_security (LPCTSTR lpFileName,
+set_file_security (const char *lpFileName,
SECURITY_INFORMATION SecurityInformation,
PSECURITY_DESCRIPTOR pSecurityDescriptor)
{
- static SetFileSecurity_Proc s_pfn_Set_File_Security = NULL;
+ static SetFileSecurityW_Proc s_pfn_Set_File_SecurityW = NULL;
+ static SetFileSecurityA_Proc s_pfn_Set_File_SecurityA = NULL;
HMODULE hm_advapi32 = NULL;
if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
return FALSE;
}
- if (g_b_init_set_file_security == 0)
+ if (w32_unicode_filenames)
+ {
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_set_file_security_w == 0)
+ {
+ g_b_init_set_file_security_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityW =
+ (SetFileSecurityW_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityW");
+ }
+ if (s_pfn_Set_File_SecurityW == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_utf16 (lpFileName, filename_w);
+ return (s_pfn_Set_File_SecurityW (filename_w, SecurityInformation,
+ pSecurityDescriptor));
+ }
+ else
{
- g_b_init_set_file_security = 1;
- hm_advapi32 = LoadLibrary ("Advapi32.dll");
- s_pfn_Set_File_Security =
- (SetFileSecurity_Proc) GetProcAddress (
- hm_advapi32, SetFileSecurity_Name);
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_set_file_security_a == 0)
+ {
+ g_b_init_set_file_security_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_File_SecurityA =
+ (SetFileSecurityA_Proc) GetProcAddress (hm_advapi32,
+ "SetFileSecurityA");
+ }
+ if (s_pfn_Set_File_SecurityA == NULL)
+ {
+ errno = ENOTSUP;
+ return FALSE;
+ }
+ filename_to_ansi (lpFileName, filename_a);
+ return (s_pfn_Set_File_SecurityA (filename_a, SecurityInformation,
+ pSecurityDescriptor));
}
- if (s_pfn_Set_File_Security == NULL)
+}
+
+static DWORD WINAPI
+set_named_security_info (LPCTSTR lpObjectName,
+ SE_OBJECT_TYPE ObjectType,
+ SECURITY_INFORMATION SecurityInformation,
+ PSID psidOwner,
+ PSID psidGroup,
+ PACL pDacl,
+ PACL pSacl)
+{
+ static SetNamedSecurityInfoW_Proc s_pfn_Set_Named_Security_InfoW = NULL;
+ static SetNamedSecurityInfoA_Proc s_pfn_Set_Named_Security_InfoA = NULL;
+ HMODULE hm_advapi32 = NULL;
+ if (is_windows_9x () == TRUE)
{
errno = ENOTSUP;
- return FALSE;
+ return ENOTSUP;
+ }
+ if (w32_unicode_filenames)
+ {
+ wchar_t filename_w[MAX_PATH];
+
+ if (g_b_init_set_named_security_info_w == 0)
+ {
+ g_b_init_set_named_security_info_w = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_Named_Security_InfoW =
+ (SetNamedSecurityInfoW_Proc) GetProcAddress (hm_advapi32,
+ "SetNamedSecurityInfoW");
+ }
+ if (s_pfn_Set_Named_Security_InfoW == NULL)
+ {
+ errno = ENOTSUP;
+ return ENOTSUP;
+ }
+ filename_to_utf16 (lpObjectName, filename_w);
+ return (s_pfn_Set_Named_Security_InfoW (filename_w, ObjectType,
+ SecurityInformation, psidOwner,
+ psidGroup, pDacl, pSacl));
+ }
+ else
+ {
+ char filename_a[MAX_PATH];
+
+ if (g_b_init_set_named_security_info_a == 0)
+ {
+ g_b_init_set_named_security_info_a = 1;
+ hm_advapi32 = LoadLibrary ("Advapi32.dll");
+ s_pfn_Set_Named_Security_InfoA =
+ (SetNamedSecurityInfoA_Proc) GetProcAddress (hm_advapi32,
+ "SetNamedSecurityInfoA");
+ }
+ if (s_pfn_Set_Named_Security_InfoA == NULL)
+ {
+ errno = ENOTSUP;
+ return ENOTSUP;
+ }
+ filename_to_ansi (lpObjectName, filename_a);
+ return (s_pfn_Set_Named_Security_InfoA (filename_a, ObjectType,
+ SecurityInformation, psidOwner,
+ psidGroup, pDacl, pSacl));
}
- return (s_pfn_Set_File_Security (lpFileName, SecurityInformation,
- pSecurityDescriptor));
}
static BOOL WINAPI
}
static BOOLEAN WINAPI
-create_symbolic_link (LPTSTR lpSymlinkFilename,
- LPTSTR lpTargetFileName,
+create_symbolic_link (LPCSTR lpSymlinkFilename,
+ LPCSTR lpTargetFileName,
DWORD dwFlags)
{
- static CreateSymbolicLink_Proc s_pfn_Create_Symbolic_Link = NULL;
+ static CreateSymbolicLinkW_Proc s_pfn_Create_Symbolic_LinkW = NULL;
+ static CreateSymbolicLinkA_Proc s_pfn_Create_Symbolic_LinkA = NULL;
BOOLEAN retval;
if (is_windows_9x () == TRUE)
errno = ENOSYS;
return 0;
}
- if (g_b_init_create_symbolic_link == 0)
+ if (w32_unicode_filenames)
{
- g_b_init_create_symbolic_link = 1;
-#ifdef _UNICODE
- s_pfn_Create_Symbolic_Link =
- (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
- "CreateSymbolicLinkW");
-#else
- s_pfn_Create_Symbolic_Link =
- (CreateSymbolicLink_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
- "CreateSymbolicLinkA");
-#endif
+ wchar_t symfn_w[MAX_PATH], tgtfn_w[MAX_PATH];
+
+ if (g_b_init_create_symbolic_link_w == 0)
+ {
+ g_b_init_create_symbolic_link_w = 1;
+ s_pfn_Create_Symbolic_LinkW =
+ (CreateSymbolicLinkW_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "CreateSymbolicLinkW");
+ }
+ if (s_pfn_Create_Symbolic_LinkW == NULL)
+ {
+ errno = ENOSYS;
+ return 0;
+ }
+
+ filename_to_utf16 (lpSymlinkFilename, symfn_w);
+ filename_to_utf16 (lpTargetFileName, tgtfn_w);
+ retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+ /* If we were denied creation of the symlink, try again after
+ enabling the SeCreateSymbolicLinkPrivilege for our process. */
+ if (!retval)
+ {
+ TOKEN_PRIVILEGES priv_current;
+
+ if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+ &priv_current))
+ {
+ retval = s_pfn_Create_Symbolic_LinkW (symfn_w, tgtfn_w, dwFlags);
+ restore_privilege (&priv_current);
+ revert_to_self ();
+ }
+ }
}
- if (s_pfn_Create_Symbolic_Link == NULL)
+ else
{
- errno = ENOSYS;
- return 0;
- }
+ char symfn_a[MAX_PATH], tgtfn_a[MAX_PATH];
- retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
- dwFlags);
- /* If we were denied creation of the symlink, try again after
- enabling the SeCreateSymbolicLinkPrivilege for our process. */
- if (!retval)
- {
- TOKEN_PRIVILEGES priv_current;
+ if (g_b_init_create_symbolic_link_a == 0)
+ {
+ g_b_init_create_symbolic_link_a = 1;
+ s_pfn_Create_Symbolic_LinkA =
+ (CreateSymbolicLinkA_Proc)GetProcAddress (GetModuleHandle ("kernel32.dll"),
+ "CreateSymbolicLinkA");
+ }
+ if (s_pfn_Create_Symbolic_LinkA == NULL)
+ {
+ errno = ENOSYS;
+ return 0;
+ }
- if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE, &priv_current))
+ filename_to_ansi (lpSymlinkFilename, symfn_a);
+ filename_to_ansi (lpTargetFileName, tgtfn_a);
+ retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+ /* If we were denied creation of the symlink, try again after
+ enabling the SeCreateSymbolicLinkPrivilege for our process. */
+ if (!retval)
{
- retval = s_pfn_Create_Symbolic_Link (lpSymlinkFilename, lpTargetFileName,
- dwFlags);
- restore_privilege (&priv_current);
- revert_to_self ();
+ TOKEN_PRIVILEGES priv_current;
+
+ if (enable_privilege (SE_CREATE_SYMBOLIC_LINK_NAME, TRUE,
+ &priv_current))
+ {
+ retval = s_pfn_Create_Symbolic_LinkA (symfn_a, tgtfn_a, dwFlags);
+ restore_privilege (&priv_current);
+ revert_to_self ();
+ }
}
}
return retval;
\f
+/* Here's an overview of how the Windows build supports file names
+ that cannot be encoded by the current system codepage.
+
+ From the POV of Lisp and layers of C code above the functions here,
+ Emacs on Windows pretends that its file names are encoded in UTF-8;
+ see encode_file and decode_file on coding.c. Any file name that is
+ passed as a unibyte string to C functions defined here is assumed
+ to be in UTF-8 encoding. Any file name returned by functions
+ defined here must be in UTF-8 encoding, with only a few exceptions
+ reserved for a couple of special cases. (Be sure to use
+ MAX_UTF8_PATH for char arrays that store UTF-8 encoded file names,
+ as they can be much longer than MAX_PATH!)
+
+ The UTF-8 encoded file names cannot be passed to system APIs, as
+ Windows does not support that. Therefore, they are converted
+ either to UTF-16 or to the ANSI codepage, depending on the value of
+ w32-unicode-filenames, before calling any system APIs or CRT library
+ functions. The default value of that variable is determined by the
+ OS on which Emacs runs: nil on Windows 9X and t otherwise, but the
+ user can change that default (although I don't see why would she
+ want to).
+
+ The 4 functions defined below, filename_to_utf16, filename_to_ansi,
+ filename_from_utf16, and filename_from_ansi, are the workhorses of
+ these conversions. They rely on Windows native APIs
+ MultiByteToWideChar and WideCharToMultiByte; we cannot use
+ functions from coding.c here, because they allocate memory, which
+ is a bad idea on the level of libc, which is what the functions
+ here emulate. (If you worry about performance due to constant
+ conversion back and forth from UTF-8 to UTF-16, then don't: first,
+ it was measured to take only a few microseconds on a not-so-fast
+ machine, and second, that's exactly what the ANSI APIs we used
+ before do anyway, because they are just thin wrappers around the
+ Unicode APIs.)
+
+ The variables file-name-coding-system and default-file-name-coding-system
+ still exist, but are actually used only when a file name needs to
+ be converted to the ANSI codepage. This happens all the time when
+ w32-unicode-filenames is nil, but can also happen from time to time
+ when it is t. Otherwise, these variables have no effect on file-name
+ encoding when w32-unicode-filenames is t; this is similar to
+ selection-coding-system.
+
+ This arrangement works very well, but it has a few gotchas and
+ limitations:
+
+ . Lisp code that encodes or decodes file names manually should
+ normally use 'utf-8' as the coding-system on Windows,
+ disregarding file-name-coding-system. This is a somewhat
+ unpleasant consequence, but it cannot be avoided. Fortunately,
+ very few Lisp packages need to do that.
+
+ More generally, passing to library functions (e.g., fopen or
+ opendir) file names already encoded in the ANSI codepage is
+ explicitly *verboten*, as all those functions, as shadowed and
+ emulated here, assume they will receive UTF-8 encoded file names.
+
+ For the same reasons, no CRT function or Win32 API can be called
+ directly in Emacs sources, without either converting the file
+ name sfrom UTF-8 to either UTF-16 or ANSI codepage, or going
+ through some shadowing function defined here.
+
+ . Environment variables stored in Vprocess_environment are encoded
+ in the ANSI codepage, so if getenv/egetenv is used for a variable
+ whose value is a file name or a list of directories, it needs to
+ be converted to UTF-8, before it is used as argument to functions
+ or decoded into a Lisp string.
+
+ . File names passed to external libraries, like the image libraries
+ and GnuTLS, need special handling. These libraries generally
+ don't support UTF-16 or UTF-8 file names, so they must get file
+ names encoded in the ANSI codepage. To facilitate using these
+ libraries with file names that are not encodable in the ANSI
+ codepage, use the function ansi_encode_filename, which will try
+ to use the short 8+3 alias of a file name if that file name is
+ not encodable in the ANSI codepage. See image.c and gnutls.c for
+ examples of how this should be done.
+
+ . Running subprocesses in non-ASCII directories and with non-ASCII
+ file arguments is limited to the current codepage (even though
+ Emacs is perfectly capable of finding an executable program file
+ even in a directory whose name cannot be encoded in the current
+ codepage). This is because the command-line arguments are
+ encoded _before_ they get to the w32-specific level, and the
+ encoding is not known in advance (it doesn't have to be the
+ current ANSI codepage), so w32proc.c functions cannot re-encode
+ them in UTF-16. This should be fixed, but will also require
+ changes in cmdproxy. The current limitation is not terribly bad
+ anyway, since very few, if any, Windows console programs that are
+ likely to be invoked by Emacs support UTF-16 encoded command
+ lines.
+
+ . For similar reasons, server.el and emacsclient are also limited
+ to the current ANSI codepage for now.
+
+ . Emacs itself can only handle command-line arguments encoded in
+ the current codepage.
+
+ . Turning on w32-unicode-filename on Windows 9X (if it at all
+ works) requires UNICOWS.DLL, which is currently loaded only in a
+ GUI session. */
+
+\f
+
/* Converting file names from UTF-8 to either UTF-16 or the ANSI
codepage defined by file-name-coding-system. */
return file_name_codepage;
}
-static int
+int
filename_to_utf16 (const char *fn_in, wchar_t *fn_out)
{
int result = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1,
return 0;
}
-static int
+int
filename_from_utf16 (const wchar_t *fn_in, char *fn_out)
{
int result = WideCharToMultiByte (CP_UTF8, 0, fn_in, -1,
return 0;
}
-static int
+int
filename_to_ansi (const char *fn_in, char *fn_out)
{
wchar_t fn_utf16[MAX_PATH];
ULONGLONG idle, kernel, user;
time_t now = time (NULL);
+ /* If system time jumped back for some reason, delete all samples
+ whose time is later than the current wall-clock time. This
+ prevents load average figures from becoming frozen for prolonged
+ periods of time, when system time is reset backwards. */
+ if (last_idx >= 0)
+ {
+ while (difftime (now, samples[last_idx].sample_time) < -1.0)
+ {
+ if (last_idx == first_idx)
+ {
+ first_idx = last_idx = -1;
+ break;
+ }
+ last_idx = buf_prev (last_idx);
+ }
+ }
+
/* Store another sample. We ignore samples that are less than 1 sec
apart. */
- if (difftime (now, samples[last_idx].sample_time) >= 1.0 - 2*DBL_EPSILON*now)
+ if (last_idx < 0
+ || (difftime (now, samples[last_idx].sample_time)
+ >= 1.0 - 2*DBL_EPSILON*now))
{
sample_system_load (&idle, &kernel, &user);
last_idx = buf_next (last_idx);
while (*fp)
{
- if (*fp == '/' || *fp == '\\')
+ if ((*fp == '/' || *fp == '\\') && *fp != path_sep)
*fp = path_sep;
fp++;
}
static int
get_long_basename (char * name, char * buf, int size)
{
- HANDLE dir_handle;
+ HANDLE dir_handle = INVALID_HANDLE_VALUE;
char fname_utf8[MAX_UTF8_PATH];
int len = 0;
- int cstatus;
+ int cstatus = -1;
/* Must be valid filename, no wild cards or other invalid characters. */
if (strpbrk (name, "*?|<>\""))
WIN32_FIND_DATAA find_data_ansi;
filename_to_ansi (name, fname_ansi);
- dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
- if (dir_handle != INVALID_HANDLE_VALUE)
- cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+ /* If the ANSI name includes ? characters, it is not encodable
+ in the ANSI codepage. In that case, we deliver the question
+ marks to the caller; calling FindFirstFileA in this case
+ could return some unrelated file name in the same
+ directory. */
+ if (_mbspbrk (fname_ansi, "?"))
+ {
+ /* Find the basename of fname_ansi. */
+ char *p = strrchr (fname_ansi, '\\');
+
+ if (!p)
+ p = fname_ansi;
+ else
+ p++;
+ cstatus = filename_from_ansi (p, fname_utf8);
+ }
+ else
+ {
+ dir_handle = FindFirstFileA (fname_ansi, &find_data_ansi);
+ if (dir_handle != INVALID_HANDLE_VALUE)
+ cstatus = filename_from_ansi (find_data_ansi.cFileName, fname_utf8);
+ }
}
if (cstatus == 0 && (len = strlen (fname_utf8)) < size)
}
}
+/* Re-encode FILENAME, a UTF-8 encoded unibyte string, using the
+ MS-Windows ANSI codepage. If FILENAME includes characters not
+ supported by the ANSI codepage, return the 8+3 alias of FILENAME,
+ if it exists. This is needed because the w32 build wants to
+ support file names outside of the system locale, but image
+ libraries typically don't support wide (a.k.a. "Unicode") APIs
+ required for that. */
+
+Lisp_Object
+ansi_encode_filename (Lisp_Object filename)
+{
+ Lisp_Object encoded_filename;
+ char fname[MAX_PATH];
+
+ filename_to_ansi (SSDATA (filename), fname);
+ if (_mbspbrk (fname, "?"))
+ {
+ char shortname[MAX_PATH];
+
+ if (w32_get_short_filename (SDATA (filename), shortname, MAX_PATH))
+ {
+ dostounix_filename (shortname);
+ encoded_filename = build_string (shortname);
+ }
+ }
+ else
+ encoded_filename = build_unibyte_string (fname);
+ return encoded_filename;
+}
+
static int
is_unc_volume (const char *filename)
{
return (NULL);
}
+/* The argv[] array holds ANSI-encoded strings, and so this function
+ works with ANS_encoded strings. */
void
init_environment (char ** argv)
{
char *p;
char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
if ((p = _mbsrchr (modname, '\\')) == NULL)
emacs_abort ();
{
static char modname[MAX_PATH];
- if (!GetModuleFileName (NULL, modname, MAX_PATH))
+ if (!GetModuleFileNameA (NULL, modname, MAX_PATH))
emacs_abort ();
argv[0] = modname;
}
xstrcasecmp (p, ".cmd") == 0));
}
-/* Emulate the Unix directory procedures opendir, closedir,
- and readdir. We can't use the procedures supplied in sysdep.c,
- so we provide them here. */
+/* Emulate the Unix directory procedures opendir, closedir, and
+ readdir. We rename them to sys_* names because some versions of
+ MinGW startup code call opendir and readdir to glob wildcards, and
+ the code that calls them doesn't grok UTF-8 encoded file names we
+ produce in dirent->d_name[]. */
struct dirent dir_static; /* simulated directory contents */
static HANDLE dir_find_handle = INVALID_HANDLE_VALUE;
static int dir_is_fat;
-static char dir_pathname[MAXPATHLEN+1];
-static WIN32_FIND_DATA dir_find_data;
+static char dir_pathname[MAX_UTF8_PATH];
+static WIN32_FIND_DATAW dir_find_data_w;
+static WIN32_FIND_DATAA dir_find_data_a;
+#define DIR_FIND_DATA_W 1
+#define DIR_FIND_DATA_A 2
+static int last_dir_find_data = -1;
/* Support shares on a network resource as subdirectories of a read-only
root directory. */
static HANDLE wnet_enum_handle = INVALID_HANDLE_VALUE;
static HANDLE open_unc_volume (const char *);
-static char *read_unc_volume (HANDLE, char *, int);
+static void *read_unc_volume (HANDLE, wchar_t *, char *, int);
static void close_unc_volume (HANDLE);
DIR *
-opendir (const char *filename)
+sys_opendir (const char *filename)
{
DIR *dirp;
dirp->dd_loc = 0;
dirp->dd_size = 0;
- strncpy (dir_pathname, map_w32_filename (filename, NULL), MAXPATHLEN);
- dir_pathname[MAXPATHLEN] = '\0';
+ strncpy (dir_pathname, map_w32_filename (filename, NULL), MAX_UTF8_PATH - 1);
+ dir_pathname[MAX_UTF8_PATH - 1] = '\0';
/* Note: We don't support symlinks to file names on FAT volumes.
Doing so would mean punishing 99.99% of use cases by resolving
all the possible symlinks in FILENAME, recursively. */
}
void
-closedir (DIR *dirp)
+sys_closedir (DIR *dirp)
{
/* If we have a find-handle open, close it. */
if (dir_find_handle != INVALID_HANDLE_VALUE)
}
struct dirent *
-readdir (DIR *dirp)
+sys_readdir (DIR *dirp)
{
int downcase = !NILP (Vw32_downcase_file_names);
if (wnet_enum_handle != INVALID_HANDLE_VALUE)
{
if (!read_unc_volume (wnet_enum_handle,
- dir_find_data.cFileName,
+ dir_find_data_w.cFileName,
+ dir_find_data_a.cFileName,
MAX_PATH))
return NULL;
}
/* If we aren't dir_finding, do a find-first, otherwise do a find-next. */
else if (dir_find_handle == INVALID_HANDLE_VALUE)
{
- char filename[MAXNAMLEN + 3];
+ char filename[MAX_UTF8_PATH + 2];
int ln;
- int dbcs_p = max_filename_mbslen () > 1;
strcpy (filename, dir_pathname);
ln = strlen (filename) - 1;
- if (!dbcs_p)
- {
- if (!IS_DIRECTORY_SEP (filename[ln]))
- strcat (filename, "\\");
- }
- else
- {
- char *end = filename + ln + 1;
- char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
-
- if (!IS_DIRECTORY_SEP (*last_char))
- strcat (filename, "\\");
- }
+ if (!IS_DIRECTORY_SEP (filename[ln]))
+ strcat (filename, "\\");
strcat (filename, "*");
/* Note: No need to resolve symlinks in FILENAME, because
FindFirst opens the directory that is the target of a
symlink. */
- dir_find_handle = FindFirstFile (filename, &dir_find_data);
+ if (w32_unicode_filenames)
+ {
+ wchar_t fnw[MAX_PATH];
+
+ filename_to_utf16 (filename, fnw);
+ dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w);
+ }
+ else
+ {
+ char fna[MAX_PATH];
+
+ filename_to_ansi (filename, fna);
+ /* If FILENAME is not representable by the current ANSI
+ codepage, we don't want FindFirstFileA to interpret the
+ '?' characters as a wildcard. */
+ if (_mbspbrk (fna, "?"))
+ dir_find_handle = INVALID_HANDLE_VALUE;
+ else
+ dir_find_handle = FindFirstFileA (fna, &dir_find_data_a);
+ }
if (dir_find_handle == INVALID_HANDLE_VALUE)
return NULL;
}
+ else if (w32_unicode_filenames)
+ {
+ if (!FindNextFileW (dir_find_handle, &dir_find_data_w))
+ return NULL;
+ }
else
{
- if (!FindNextFile (dir_find_handle, &dir_find_data))
+ if (!FindNextFileA (dir_find_handle, &dir_find_data_a))
return NULL;
}
value returned by stat(). */
dir_static.d_ino = 1;
- strcpy (dir_static.d_name, dir_find_data.cFileName);
-
- /* If the file name in cFileName[] includes `?' characters, it means
- the original file name used characters that cannot be represented
- by the current ANSI codepage. To avoid total lossage, retrieve
- the short 8+3 alias of the long file name. */
- if (_mbspbrk (dir_static.d_name, "?"))
+ if (w32_unicode_filenames)
{
- strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
- downcase = 1; /* 8+3 aliases are returned in all caps */
- }
- dir_static.d_namlen = strlen (dir_static.d_name);
- dir_static.d_reclen = sizeof (struct dirent) - MAXNAMLEN + 3 +
- dir_static.d_namlen - dir_static.d_namlen % 4;
+ if (downcase || dir_is_fat)
+ {
+ wchar_t tem[MAX_PATH];
- /* If the file name in cFileName[] includes `?' characters, it means
- the original file name used characters that cannot be represented
- by the current ANSI codepage. To avoid total lossage, retrieve
- the short 8+3 alias of the long file name. */
- if (_mbspbrk (dir_find_data.cFileName, "?"))
- {
- strcpy (dir_static.d_name, dir_find_data.cAlternateFileName);
- /* 8+3 aliases are returned in all caps, which could break
- various alists that look at filenames' extensions. */
- downcase = 1;
+ wcscpy (tem, dir_find_data_w.cFileName);
+ CharLowerW (tem);
+ filename_from_utf16 (tem, dir_static.d_name);
+ }
+ else
+ filename_from_utf16 (dir_find_data_w.cFileName, dir_static.d_name);
+ last_dir_find_data = DIR_FIND_DATA_W;
}
else
- strcpy (dir_static.d_name, dir_find_data.cFileName);
- dir_static.d_namlen = strlen (dir_static.d_name);
- if (dir_is_fat)
- _mbslwr (dir_static.d_name);
- else if (downcase)
{
- register char *p;
- int dbcs_p = max_filename_mbslen () > 1;
- for (p = dir_static.d_name; *p; )
+ char tem[MAX_PATH];
+
+ /* If the file name in cFileName[] includes `?' characters, it
+ means the original file name used characters that cannot be
+ represented by the current ANSI codepage. To avoid total
+ lossage, retrieve the short 8+3 alias of the long file
+ name. */
+ if (_mbspbrk (dir_find_data_a.cFileName, "?"))
{
- if (*p >= 'a' && *p <= 'z')
- break;
- if (dbcs_p)
- p = CharNextExA (file_name_codepage, p, 0);
- else
- p++;
+ strcpy (tem, dir_find_data_a.cAlternateFileName);
+ /* 8+3 aliases are returned in all caps, which could break
+ various alists that look at filenames' extensions. */
+ downcase = 1;
}
- if (!*p)
- _mbslwr (dir_static.d_name);
+ else if (downcase || dir_is_fat)
+ strcpy (tem, dir_find_data_a.cFileName);
+ else
+ filename_from_ansi (dir_find_data_a.cFileName, dir_static.d_name);
+ if (downcase || dir_is_fat)
+ {
+ _mbslwr (tem);
+ filename_from_ansi (tem, dir_static.d_name);
+ }
+ last_dir_find_data = DIR_FIND_DATA_A;
}
+ dir_static.d_namlen = strlen (dir_static.d_name);
+ dir_static.d_reclen = sizeof (struct dirent) - MAX_UTF8_PATH + 3 +
+ dir_static.d_namlen - dir_static.d_namlen % 4;
+
return &dir_static;
}
static HANDLE
open_unc_volume (const char *path)
{
- NETRESOURCE nr;
+ const char *fn = map_w32_filename (path, NULL);
+ DWORD result;
HANDLE henum;
- int result;
- nr.dwScope = RESOURCE_GLOBALNET;
- nr.dwType = RESOURCETYPE_DISK;
- nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
- nr.dwUsage = RESOURCEUSAGE_CONTAINER;
- nr.lpLocalName = NULL;
- nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL);
- nr.lpComment = NULL;
- nr.lpProvider = NULL;
+ if (w32_unicode_filenames)
+ {
+ NETRESOURCEW nrw;
+ wchar_t fnw[MAX_PATH];
+
+ nrw.dwScope = RESOURCE_GLOBALNET;
+ nrw.dwType = RESOURCETYPE_DISK;
+ nrw.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nrw.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nrw.lpLocalName = NULL;
+ filename_to_utf16 (fn, fnw);
+ nrw.lpRemoteName = fnw;
+ nrw.lpComment = NULL;
+ nrw.lpProvider = NULL;
+
+ result = WNetOpenEnumW (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nrw, &henum);
+ }
+ else
+ {
+ NETRESOURCEA nra;
+ char fna[MAX_PATH];
- result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
- RESOURCEUSAGE_CONNECTABLE, &nr, &henum);
+ nra.dwScope = RESOURCE_GLOBALNET;
+ nra.dwType = RESOURCETYPE_DISK;
+ nra.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ nra.dwUsage = RESOURCEUSAGE_CONTAINER;
+ nra.lpLocalName = NULL;
+ filename_to_ansi (fn, fna);
+ nra.lpRemoteName = fna;
+ nra.lpComment = NULL;
+ nra.lpProvider = NULL;
+ result = WNetOpenEnumA (RESOURCE_GLOBALNET, RESOURCETYPE_DISK,
+ RESOURCEUSAGE_CONNECTABLE, &nra, &henum);
+ }
if (result == NO_ERROR)
return henum;
else
return INVALID_HANDLE_VALUE;
}
-static char *
-read_unc_volume (HANDLE henum, char *readbuf, int size)
+static void *
+read_unc_volume (HANDLE henum, wchar_t *fname_w, char *fname_a, int size)
{
DWORD count;
int result;
- DWORD bufsize = 512;
char *buffer;
- char *ptr;
- int dbcs_p = max_filename_mbslen () > 1;
+ DWORD bufsize = 512;
+ void *retval;
count = 1;
- buffer = alloca (bufsize);
- result = WNetEnumResource (henum, &count, buffer, &bufsize);
- if (result != NO_ERROR)
- return NULL;
+ if (w32_unicode_filenames)
+ {
+ wchar_t *ptrw;
- /* WNetEnumResource returns \\resource\share...skip forward to "share". */
- ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
- ptr += 2;
- if (!dbcs_p)
- while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ bufsize *= 2;
+ buffer = alloca (bufsize);
+ result = WNetEnumResourceW (henum, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+ /* WNetEnumResource returns \\resource\share...skip forward to "share". */
+ ptrw = ((LPNETRESOURCEW) buffer)->lpRemoteName;
+ ptrw += 2;
+ while (*ptrw && *ptrw != L'/' && *ptrw != L'\\') ptrw++;
+ ptrw++;
+ wcsncpy (fname_w, ptrw, size);
+ retval = fname_w;
+ }
else
{
- while (*ptr && !IS_DIRECTORY_SEP (*ptr))
- ptr = CharNextExA (file_name_codepage, ptr, 0);
+ int dbcs_p = max_filename_mbslen () > 1;
+ char *ptra;
+
+ buffer = alloca (bufsize);
+ result = WNetEnumResourceA (henum, &count, buffer, &bufsize);
+ if (result != NO_ERROR)
+ return NULL;
+ ptra = ((LPNETRESOURCEA) buffer)->lpRemoteName;
+ ptra += 2;
+ if (!dbcs_p)
+ while (*ptra && !IS_DIRECTORY_SEP (*ptra)) ptra++;
+ else
+ {
+ while (*ptra && !IS_DIRECTORY_SEP (*ptra))
+ ptra = CharNextExA (file_name_codepage, ptra, 0);
+ }
+ ptra++;
+ strncpy (fname_a, ptra, size);
+ retval = fname_a;
}
- ptr++;
- strncpy (readbuf, ptr, size);
- return readbuf;
+ return retval;
}
static void
static void
logon_network_drive (const char *path)
{
- NETRESOURCE resource;
- char share[MAX_PATH];
+ char share[MAX_UTF8_PATH];
int n_slashes;
char drive[4];
UINT drvtype;
char *p;
- int dbcs_p;
+ DWORD val;
if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
drvtype = DRIVE_REMOTE;
return;
n_slashes = 2;
- strncpy (share, path, MAX_PATH);
+ strncpy (share, path, MAX_UTF8_PATH);
/* Truncate to just server and share name. */
- dbcs_p = max_filename_mbslen () > 1;
- for (p = share + 2; *p && p < share + MAX_PATH; )
+ for (p = share + 2; *p && p < share + MAX_UTF8_PATH; p++)
{
if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
{
*p = '\0';
break;
}
- if (dbcs_p)
- p = CharNextExA (file_name_codepage, p, 0);
- else
- p++;
}
- resource.dwType = RESOURCETYPE_DISK;
- resource.lpLocalName = NULL;
- resource.lpRemoteName = share;
- resource.lpProvider = NULL;
+ if (w32_unicode_filenames)
+ {
+ NETRESOURCEW resourcew;
+ wchar_t share_w[MAX_PATH];
- WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE);
+ resourcew.dwScope = RESOURCE_GLOBALNET;
+ resourcew.dwType = RESOURCETYPE_DISK;
+ resourcew.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ resourcew.dwUsage = RESOURCEUSAGE_CONTAINER;
+ resourcew.lpLocalName = NULL;
+ filename_to_utf16 (share, share_w);
+ resourcew.lpRemoteName = share_w;
+ resourcew.lpProvider = NULL;
+
+ val = WNetAddConnection2W (&resourcew, NULL, NULL, CONNECT_INTERACTIVE);
+ }
+ else
+ {
+ NETRESOURCEA resourcea;
+ char share_a[MAX_PATH];
+
+ resourcea.dwScope = RESOURCE_GLOBALNET;
+ resourcea.dwType = RESOURCETYPE_DISK;
+ resourcea.dwDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ resourcea.dwUsage = RESOURCEUSAGE_CONTAINER;
+ resourcea.lpLocalName = NULL;
+ filename_to_ansi (share, share_a);
+ resourcea.lpRemoteName = share_a;
+ resourcea.lpProvider = NULL;
+
+ val = WNetAddConnection2A (&resourcea, NULL, NULL, CONNECT_INTERACTIVE);
+ }
+
+ switch (val)
+ {
+ case NO_ERROR:
+ case ERROR_ALREADY_ASSIGNED:
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_LOGON_FAILURE:
+ errno = EACCES;
+ break;
+ case ERROR_BUSY:
+ errno = EAGAIN;
+ break;
+ case ERROR_BAD_NET_NAME:
+ case ERROR_NO_NET_OR_BAD_PATH:
+ case ERROR_NO_NETWORK:
+ case ERROR_CANCELLED:
+ default:
+ errno = ENOENT;
+ break;
+ }
}
/* Emulate faccessat(2). */
&& (flags & AT_SYMLINK_NOFOLLOW) == 0)
path = chase_symlinks (path);
- if ((attributes = GetFileAttributes (path)) == -1)
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ attributes = GetFileAttributesW (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ attributes = GetFileAttributesA (path_a);
+ }
+
+ if (attributes == -1)
{
DWORD w32err = GetLastError ();
int
sys_chdir (const char * path)
{
- /* FIXME: Temporary. Also, figure out what to do with
- map_w32_filename, as the original code did this:
- _chdir(map_w32_filename (path, NULL)). */
+ path = map_w32_filename (path, NULL);
if (w32_unicode_filenames)
{
- wchar_t newdir[MAXPATHLEN];
+ wchar_t newdir_w[MAX_PATH];
- if (filename_to_utf16 (path, newdir) == 0)
- return _wchdir (newdir);
+ if (filename_to_utf16 (path, newdir_w) == 0)
+ return _wchdir (newdir_w);
return -1;
}
else
{
- char newdir[MAXPATHLEN];
+ char newdir_a[MAX_PATH];
- if (filename_to_ansi (path, newdir) == 0)
- return _chdir (path);
+ if (filename_to_ansi (path, newdir_a) == 0)
+ return _chdir (newdir_a);
return -1;
}
}
sys_chmod (const char * path, int mode)
{
path = chase_symlinks (map_w32_filename (path, NULL));
- return _chmod (path, mode);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wchmod (path_w, mode);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _chmod (path_a, mode);
+ }
}
int
sys_creat (const char * path, int mode)
{
- return _creat (map_w32_filename (path, NULL), mode);
+ path = map_w32_filename (path, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wcreat (path_w, mode);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _creat (path_a, mode);
+ }
}
FILE *
}
else break;
- fd = _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, 0644);
+ path = map_w32_filename (path, NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ fd = _wopen (path_w, oflag | _O_NOINHERIT, 0644);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ fd = _open (path_a, oflag | _O_NOINHERIT, 0644);
+ }
if (fd < 0)
return NULL;
{
HANDLE fileh;
int result = -1;
- char oldname[MAX_PATH], newname[MAX_PATH];
+ char oldname[MAX_UTF8_PATH], newname[MAX_UTF8_PATH];
+ wchar_t oldname_w[MAX_PATH];
+ char oldname_a[MAX_PATH];
if (old == NULL || new == NULL)
{
strcpy (oldname, map_w32_filename (old, NULL));
strcpy (newname, map_w32_filename (new, NULL));
- fileh = CreateFile (oldname, 0, 0, NULL, OPEN_EXISTING,
- FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (oldname, oldname_w);
+ fileh = CreateFileW (oldname_w, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
+ else
+ {
+ filename_to_ansi (oldname, oldname_a);
+ fileh = CreateFileA (oldname_a, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ }
if (fileh != INVALID_HANDLE_VALUE)
{
int wlen;
WCHAR wbuffer[MAX_PATH]; /* extra space for link name */
} data;
- wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1,
+ /* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN
+ indicates that flag is unsupported for CP_UTF8, and OTOH says
+ it is the default anyway. */
+ wlen = MultiByteToWideChar (CP_UTF8, 0, newname, -1,
data.wid.cStreamName, MAX_PATH);
if (wlen > 0)
{
}
else
{
- /* Should try mapping GetLastError to errno; for now just
- indicate a general error (eg. links not supported). */
- errno = EINVAL; // perhaps EMLINK?
+ DWORD err = GetLastError ();
+ DWORD attributes;
+
+ switch (err)
+ {
+ case ERROR_ACCESS_DENIED:
+ /* This is what happens when OLDNAME is a directory,
+ since Windows doesn't support hard links to
+ directories. Posix says to set errno to EPERM in
+ that case. */
+ if (w32_unicode_filenames)
+ attributes = GetFileAttributesW (oldname_w);
+ else
+ attributes = GetFileAttributesA (oldname_a);
+ if (attributes != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
+ errno = EPERM;
+ else if (attributes == -1
+ && is_unc_volume (oldname)
+ && unc_volume_file_attributes (oldname) != -1)
+ errno = EPERM;
+ else
+ errno = EACCES;
+ break;
+ case ERROR_TOO_MANY_LINKS:
+ errno = EMLINK;
+ break;
+ case ERROR_NOT_SAME_DEVICE:
+ errno = EXDEV;
+ break;
+ default:
+ errno = EINVAL;
+ break;
+ }
}
}
int
sys_mkdir (const char * path)
{
- return _mkdir (map_w32_filename (path, NULL));
+ path = map_w32_filename (path, NULL);
+
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wmkdir (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _mkdir (path_a);
+ }
}
int
const char* mpath = map_w32_filename (path, NULL);
int res = -1;
- /* If possible, try to open file without _O_CREAT, to be able to
- write to existing hidden and system files. Force all file
- handles to be non-inheritable. */
- if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
- res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
- if (res < 0)
- res = _open (mpath, oflag | _O_NOINHERIT, mode);
+ if (w32_unicode_filenames)
+ {
+ wchar_t mpath_w[MAX_PATH];
+
+ filename_to_utf16 (mpath, mpath_w);
+ /* If possible, try to open file without _O_CREAT, to be able to
+ write to existing hidden and system files. Force all file
+ handles to be non-inheritable. */
+ if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+ res = _wopen (mpath_w, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res < 0)
+ res = _wopen (mpath_w, oflag | _O_NOINHERIT, mode);
+ }
+ else
+ {
+ char mpath_a[MAX_PATH];
+
+ filename_to_ansi (mpath, mpath_a);
+ if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+ res = _open (mpath_a, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res < 0)
+ res = _open (mpath_a, oflag | _O_NOINHERIT, mode);
+ }
return res;
}
sys_rename_replace (const char *oldname, const char *newname, BOOL force)
{
BOOL result;
- char temp[MAX_PATH];
+ char temp[MAX_UTF8_PATH], temp_a[MAX_PATH];;
int newname_dev;
int oldname_dev;
+ bool have_temp_a = false;
/* MoveFile on Windows 95 doesn't correctly change the short file name
alias in a number of circumstances (it is not easy to predict when
char * o;
char * p;
int i = 0;
+ char oldname_a[MAX_PATH];
oldname = map_w32_filename (oldname, NULL);
- if ((o = strrchr (oldname, '\\')))
+ filename_to_ansi (oldname, oldname_a);
+ filename_to_ansi (temp, temp_a);
+ if ((o = strrchr (oldname_a, '\\')))
o++;
else
- o = (char *) oldname;
+ o = (char *) oldname_a;
- if ((p = strrchr (temp, '\\')))
+ if ((p = strrchr (temp_a, '\\')))
p++;
else
- p = temp;
+ p = temp_a;
do
{
seems to make the second rename work properly. */
sprintf (p, "_.%s.%u", o, i);
i++;
- result = rename (oldname, temp);
+ result = rename (oldname_a, temp_a);
}
/* This loop must surely terminate! */
while (result < 0 && errno == EEXIST);
if (result < 0)
return -1;
+ have_temp_a = true;
}
/* If FORCE, emulate Unix behavior - newname is deleted if it already exists
/* volume_info is set indirectly by map_w32_filename. */
newname_dev = volume_info.serialnum;
- result = rename (temp, newname);
-
- if (result < 0 && force)
+ if (w32_unicode_filenames)
{
- DWORD w32err = GetLastError ();
-
- if (errno == EACCES
- && newname_dev != oldname_dev)
- {
- /* The implementation of `rename' on Windows does not return
- errno = EXDEV when you are moving a directory to a
- different storage device (ex. logical disk). It returns
- EACCES instead. So here we handle such situations and
- return EXDEV. */
- DWORD attributes;
-
- if ((attributes = GetFileAttributes (temp)) != -1
- && (attributes & FILE_ATTRIBUTE_DIRECTORY))
- errno = EXDEV;
- }
- else if (errno == EEXIST)
+ wchar_t temp_w[MAX_PATH], newname_w[MAX_PATH];
+
+ filename_to_utf16 (temp, temp_w);
+ filename_to_utf16 (newname, newname_w);
+ result = _wrename (temp_w, newname_w);
+ if (result < 0 && force)
{
- if (_chmod (newname, 0666) != 0)
- return result;
- if (_unlink (newname) != 0)
- return result;
- result = rename (temp, newname);
+ DWORD w32err = GetLastError ();
+
+ if (errno == EACCES
+ && newname_dev != oldname_dev)
+ {
+ /* The implementation of `rename' on Windows does not return
+ errno = EXDEV when you are moving a directory to a
+ different storage device (ex. logical disk). It returns
+ EACCES instead. So here we handle such situations and
+ return EXDEV. */
+ DWORD attributes;
+
+ if ((attributes = GetFileAttributesW (temp_w)) != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EXDEV;
+ }
+ else if (errno == EEXIST)
+ {
+ if (_wchmod (newname_w, 0666) != 0)
+ return result;
+ if (_wunlink (newname_w) != 0)
+ return result;
+ result = _wrename (temp_w, newname_w);
+ }
+ else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+ && is_symlink (temp))
+ {
+ /* This is Windows prohibiting the user from creating a
+ symlink in another place, since that requires
+ privileges. */
+ errno = EPERM;
+ }
}
- else if (w32err == ERROR_PRIVILEGE_NOT_HELD
- && is_symlink (temp))
+ }
+ else
+ {
+ char newname_a[MAX_PATH];
+
+ if (!have_temp_a)
+ filename_to_ansi (temp, temp_a);
+ filename_to_ansi (newname, newname_a);
+ result = rename (temp_a, newname_a);
+ if (result < 0 && force)
{
- /* This is Windows prohibiting the user from creating a
- symlink in another place, since that requires
- privileges. */
- errno = EPERM;
+ DWORD w32err = GetLastError ();
+
+ if (errno == EACCES
+ && newname_dev != oldname_dev)
+ {
+ DWORD attributes;
+
+ if ((attributes = GetFileAttributesA (temp_a)) != -1
+ && (attributes & FILE_ATTRIBUTE_DIRECTORY))
+ errno = EXDEV;
+ }
+ else if (errno == EEXIST)
+ {
+ if (_chmod (newname_a, 0666) != 0)
+ return result;
+ if (_unlink (newname_a) != 0)
+ return result;
+ result = rename (temp_a, newname_a);
+ }
+ else if (w32err == ERROR_PRIVILEGE_NOT_HELD
+ && is_symlink (temp))
+ errno = EPERM;
}
}
int
sys_rmdir (const char * path)
{
- return _rmdir (map_w32_filename (path, NULL));
+ path = map_w32_filename (path, NULL);
+
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ return _wrmdir (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ return _rmdir (path_a);
+ }
}
int
{
path = map_w32_filename (path, NULL);
- /* On Unix, unlink works without write permission. */
- _chmod (path, 0666);
- return _unlink (path);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ /* On Unix, unlink works without write permission. */
+ _wchmod (path_w, 0666);
+ return _wunlink (path_w);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ _chmod (path_a, 0666);
+ return _unlink (path_a);
+ }
}
static FILETIME utc_base_ft;
}
/* If this is non-zero, the caller wants accurate information about
- file's owner and group, which could be expensive to get. */
+ file's owner and group, which could be expensive to get. dired.c
+ uses this flag when needed for the job at hand. */
int w32_stat_get_owner_group;
/* MSVC stat function can't cope with UNC names and has other bugs, so
replace it with our own. This also allows us to calculate consistent
- inode values and owner/group without hacks in the main Emacs code. */
+ inode values and owner/group without hacks in the main Emacs code,
+ and support file names encoded in UTF-8. */
static int
stat_worker (const char * path, struct stat * buf, int follow_symlinks)
{
char *name, *save_name, *r;
- WIN32_FIND_DATA wfd;
+ WIN32_FIND_DATAW wfd_w;
+ WIN32_FIND_DATAA wfd_a;
HANDLE fh;
unsigned __int64 fake_inode = 0;
int permission;
DWORD access_rights = 0;
DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
FILETIME ctime, atime, wtime;
- int dbcs_p;
+ wchar_t name_w[MAX_PATH];
+ char name_a[MAX_PATH];
if (path == NULL || buf == NULL)
{
save_name = name = (char *) map_w32_filename (path, &path);
/* Must be valid filename, no wild cards or other invalid
- characters. We use _mbspbrk to support multibyte strings that
- might look to strpbrk as if they included literal *, ?, and other
- characters mentioned below that are disallowed by Windows
- filesystems. */
- if (_mbspbrk (name, "*?|<>\""))
+ characters. */
+ if (strpbrk (name, "*?|<>\""))
{
errno = ENOENT;
return -1;
if (is_windows_9x () != TRUE)
access_rights |= READ_CONTROL;
- fh = CreateFile (name, access_rights, 0, NULL, OPEN_EXISTING,
- file_flags, NULL);
- /* If CreateFile fails with READ_CONTROL, try again with zero as
- access rights. */
- if (fh == INVALID_HANDLE_VALUE && access_rights)
- fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING,
- file_flags, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (name, name_w);
+ fh = CreateFileW (name_w, access_rights, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ /* If CreateFile fails with READ_CONTROL, try again with
+ zero as access rights. */
+ if (fh == INVALID_HANDLE_VALUE && access_rights)
+ fh = CreateFileW (name_w, 0, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ }
+ else
+ {
+ filename_to_ansi (name, name_a);
+ fh = CreateFileA (name_a, access_rights, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ if (fh == INVALID_HANDLE_VALUE && access_rights)
+ fh = CreateFileA (name_a, 0, 0, NULL, OPEN_EXISTING,
+ file_flags, NULL);
+ }
if (fh == INVALID_HANDLE_VALUE)
goto no_true_file_attributes;
did not ask for extra precision, resolving symlinks will fly
in the face of that request, since the user then wants the
lightweight version of the code. */
- dbcs_p = max_filename_mbslen () > 1;
rootdir = (path >= save_name + len - 1
&& (IS_DIRECTORY_SEP (*path) || *path == 0));
}
else if (rootdir)
{
- if (!dbcs_p)
- {
- if (!IS_DIRECTORY_SEP (name[len-1]))
- strcat (name, "\\");
- }
- else
- {
- char *end = name + len;
- char *n = CharPrevExA (file_name_codepage, name, end, 0);
-
- if (!IS_DIRECTORY_SEP (*n))
- strcat (name, "\\");
- }
+ if (!IS_DIRECTORY_SEP (name[len-1]))
+ strcat (name, "\\");
if (GetDriveType (name) < 2)
{
errno = ENOENT;
}
else
{
- if (!dbcs_p)
- {
- if (IS_DIRECTORY_SEP (name[len-1]))
- name[len - 1] = 0;
- }
- else
- {
- char *end = name + len;
- char *n = CharPrevExA (file_name_codepage, name, end, 0);
+ int have_wfd = -1;
- if (IS_DIRECTORY_SEP (*n))
- *n = 0;
- }
+ if (IS_DIRECTORY_SEP (name[len-1]))
+ name[len - 1] = 0;
/* (This is hacky, but helps when doing file completions on
network drives.) Optimize by using information available from
active readdir if possible. */
len = strlen (dir_pathname);
- if (!dbcs_p)
- {
- if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
- len--;
- }
- else
- {
- char *end = dir_pathname + len;
- char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
-
- if (IS_DIRECTORY_SEP (*n))
- len--;
- }
+ if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+ len--;
if (dir_find_handle != INVALID_HANDLE_VALUE
+ && last_dir_find_data != -1
&& !(is_a_symlink && follow_symlinks)
- && strnicmp (save_name, dir_pathname, len) == 0
+ /* The 2 file-name comparisons below support only ASCII
+ characters, and will lose (compare not equal) when
+ the file names include non-ASCII characters that are
+ the same but for the case. However, doing this
+ properly involves: (a) converting both file names to
+ UTF-16, (b) lower-casing both names using CharLowerW,
+ and (c) comparing the results; this would be quite a
+ bit slower, whereas Plan B is for users who want
+ lightweight albeit inaccurate version of 'stat'. */
+ && c_strncasecmp (save_name, dir_pathname, len) == 0
&& IS_DIRECTORY_SEP (name[len])
&& xstrcasecmp (name + len + 1, dir_static.d_name) == 0)
{
+ have_wfd = last_dir_find_data;
/* This was the last entry returned by readdir. */
- wfd = dir_find_data;
+ if (last_dir_find_data == DIR_FIND_DATA_W)
+ wfd_w = dir_find_data_w;
+ else
+ wfd_a = dir_find_data_a;
}
else
{
logon_network_drive (name);
- fh = FindFirstFile (name, &wfd);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (name, name_w);
+ fh = FindFirstFileW (name_w, &wfd_w);
+ have_wfd = DIR_FIND_DATA_W;
+ }
+ else
+ {
+ filename_to_ansi (name, name_a);
+ /* If NAME includes characters not representable by
+ the current ANSI codepage, filename_to_ansi
+ usually replaces them with a '?'. We don't want
+ to let FindFirstFileA interpret those as wildcards,
+ and "succeed", returning us data from some random
+ file in the same directory. */
+ if (_mbspbrk (name_a, "?"))
+ fh = INVALID_HANDLE_VALUE;
+ else
+ fh = FindFirstFileA (name_a, &wfd_a);
+ have_wfd = DIR_FIND_DATA_A;
+ }
if (fh == INVALID_HANDLE_VALUE)
{
errno = ENOENT;
}
/* Note: if NAME is a symlink, the information we get from
FindFirstFile is for the symlink, not its target. */
- fattrs = wfd.dwFileAttributes;
- ctime = wfd.ftCreationTime;
- atime = wfd.ftLastAccessTime;
- wtime = wfd.ftLastWriteTime;
- fs_high = wfd.nFileSizeHigh;
- fs_low = wfd.nFileSizeLow;
+ if (have_wfd == DIR_FIND_DATA_W)
+ {
+ fattrs = wfd_w.dwFileAttributes;
+ ctime = wfd_w.ftCreationTime;
+ atime = wfd_w.ftLastAccessTime;
+ wtime = wfd_w.ftLastWriteTime;
+ fs_high = wfd_w.nFileSizeHigh;
+ fs_low = wfd_w.nFileSizeLow;
+ }
+ else
+ {
+ fattrs = wfd_a.dwFileAttributes;
+ ctime = wfd_a.ftCreationTime;
+ atime = wfd_a.ftLastAccessTime;
+ wtime = wfd_a.ftLastWriteTime;
+ fs_high = wfd_a.nFileSizeHigh;
+ fs_low = wfd_a.nFileSizeLow;
+ }
fake_inode = 0;
nlinks = 1;
serialnum = volume_info.serialnum;
get_file_owner_and_group (NULL, buf);
}
-#if 0
- /* Not sure if there is any point in this. */
- if (!NILP (Vw32_generate_fake_inodes))
- fake_inode = generate_inode_val (name);
- else if (fake_inode == 0)
- {
- /* For want of something better, try to make everything unique. */
- static DWORD gen_num = 0;
- fake_inode = ++gen_num;
- }
-#endif
-
buf->st_ino = fake_inode;
buf->st_dev = serialnum;
FIXME: Add proper support for fdopendir, fstatat, readlinkat.
Gnulib does this and can serve as a model. */
- char fullname[MAX_PATH];
+ char fullname[MAX_UTF8_PATH];
if (fd != AT_FDCWD)
{
return 0;
}
+/* Emacs expects us to support the traditional octal form of the mode
+ bits, which is not what msvcrt.dll wants. */
+
+#define WRITE_USER 00200
+
+int
+sys_umask (int mode)
+{
+ static int current_mask;
+ int retval, arg = 0;
+
+ /* The only bit we really support is the write bit. Files are
+ always readable on MS-Windows, and the execute bit does not exist
+ at all. */
+ /* FIXME: if the GROUP and OTHER bits are reset, we should use ACLs
+ to prevent access by other users on NTFS. */
+ if ((mode & WRITE_USER) != 0)
+ arg |= S_IWRITE;
+
+ retval = _umask (arg);
+ /* Merge into the return value the bits they've set the last time,
+ which msvcrt.dll ignores and never returns. Emacs insists on its
+ notion of mask being identical to what we return. */
+ retval |= (current_mask & ~WRITE_USER);
+ current_mask = mode;
+
+ return retval;
+}
+
\f
/* Symlink-related functions. */
#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
int
symlink (char const *filename, char const *linkname)
{
- char linkfn[MAX_PATH], *tgtfn;
+ char linkfn[MAX_UTF8_PATH], *tgtfn;
DWORD flags = 0;
int dir_access, filename_ends_in_slash;
- int dbcs_p;
/* Diagnostics follows Posix as much as possible. */
if (filename == NULL || linkname == NULL)
errno = ENOENT;
return -1;
}
- if (strlen (filename) > MAX_PATH || strlen (linkname) > MAX_PATH)
+ if (strlen (filename) > MAX_UTF8_PATH || strlen (linkname) > MAX_UTF8_PATH)
{
errno = ENAMETOOLONG;
return -1;
return -1;
}
- dbcs_p = max_filename_mbslen () > 1;
-
/* Note: since empty FILENAME was already rejected, we can safely
refer to FILENAME[1]. */
if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
directory where the Emacs process runs. Note that
make-symbolic-link always makes sure LINKNAME is a fully
expanded file name. */
- char tem[MAX_PATH];
+ char tem[MAX_UTF8_PATH];
char *p = linkfn + strlen (linkfn);
- if (!dbcs_p)
- {
- while (p > linkfn && !IS_ANY_SEP (p[-1]))
- p--;
- }
- else
- {
- char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
-
- while (p > linkfn && !IS_ANY_SEP (*p1))
- {
- p = p1;
- p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
- }
- }
+ while (p > linkfn && !IS_ANY_SEP (p[-1]))
+ p--;
if (p > linkfn)
strncpy (tem, linkfn, p - linkfn);
tem[p - linkfn] = '\0';
exist, but ends in a slash, we create a symlink to directory. If
FILENAME exists and is a directory, we always create a symlink to
directory. */
- if (!dbcs_p)
- filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
- else
- {
- const char *end = filename + strlen (filename);
- const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
-
- filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
- }
+ filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
if (dir_access == 0 || filename_ends_in_slash)
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
is_symlink (const char *filename)
{
DWORD attrs;
- WIN32_FIND_DATA wfd;
+ wchar_t filename_w[MAX_PATH];
+ char filename_a[MAX_PATH];
+ WIN32_FIND_DATAW wfdw;
+ WIN32_FIND_DATAA wfda;
HANDLE fh;
+ int attrs_mean_symlink;
- attrs = GetFileAttributes (filename);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (filename, filename_w);
+ attrs = GetFileAttributesW (filename_w);
+ }
+ else
+ {
+ filename_to_ansi (filename, filename_a);
+ attrs = GetFileAttributesA (filename_a);
+ }
if (attrs == -1)
{
DWORD w32err = GetLastError ();
if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0)
return 0;
logon_network_drive (filename);
- fh = FindFirstFile (filename, &wfd);
+ if (w32_unicode_filenames)
+ {
+ fh = FindFirstFileW (filename_w, &wfdw);
+ attrs_mean_symlink =
+ (wfdw.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+ && (wfdw.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+ }
+ else if (_mbspbrk (filename_a, "?"))
+ {
+ /* filename_to_ansi failed to convert the file name. */
+ errno = ENOENT;
+ return 0;
+ }
+ else
+ {
+ fh = FindFirstFileA (filename_a, &wfda);
+ attrs_mean_symlink =
+ (wfda.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
+ && (wfda.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+ }
if (fh == INVALID_HANDLE_VALUE)
return 0;
FindClose (fh);
- return (wfd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0
- && (wfd.dwReserved0 & IO_REPARSE_TAG_SYMLINK) == IO_REPARSE_TAG_SYMLINK;
+ return attrs_mean_symlink;
}
/* If NAME identifies a symbolic link, copy into BUF the file name of
int restore_privs = 0;
HANDLE sh;
ssize_t retval;
+ char resolved[MAX_UTF8_PATH];
if (name == NULL)
{
path = map_w32_filename (name, NULL);
- if (strlen (path) > MAX_PATH)
+ if (strlen (path) > MAX_UTF8_PATH)
{
errno = ENAMETOOLONG;
return -1;
e.g. 'C:\Users\All Users', GENERIC_READ fails with
ERROR_ACCESS_DENIED. Zero seems to work just fine, both for file
and directory symlinks. */
- sh = CreateFile (path, 0, 0, NULL, OPEN_EXISTING,
- FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
- NULL);
+ if (w32_unicode_filenames)
+ {
+ wchar_t path_w[MAX_PATH];
+
+ filename_to_utf16 (path, path_w);
+ sh = CreateFileW (path_w, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT
+ | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ }
+ else
+ {
+ char path_a[MAX_PATH];
+
+ filename_to_ansi (path, path_a);
+ sh = CreateFileA (path_a, 0, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT
+ | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ }
if (sh != INVALID_HANDLE_VALUE)
{
BYTE reparse_buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
reparse_data, then convert it to multibyte encoding in
the current locale's codepage. */
WCHAR *lwname;
- BYTE lname[MAX_PATH];
- USHORT lname_len;
+ size_t lname_size;
USHORT lwname_len =
reparse_data->SymbolicLinkReparseBuffer.PrintNameLength;
WCHAR *lwname_src =
reparse_data->SymbolicLinkReparseBuffer.PathBuffer
+ reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
- /* This updates file_name_codepage which we need below. */
- int dbcs_p = max_filename_mbslen () > 1;
+ size_t size_to_copy = buf_size;
/* According to MSDN, PrintNameLength does not include the
terminating null character. */
lwname = alloca ((lwname_len + 1) * sizeof(WCHAR));
memcpy (lwname, lwname_src, lwname_len);
lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
-
- lname_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
- lname, MAX_PATH, NULL, NULL);
- if (!lname_len)
- {
- /* WideCharToMultiByte failed. */
- DWORD w32err1 = GetLastError ();
-
- switch (w32err1)
- {
- case ERROR_INSUFFICIENT_BUFFER:
- errno = ENAMETOOLONG;
- break;
- case ERROR_INVALID_PARAMETER:
- errno = EFAULT;
- break;
- case ERROR_NO_UNICODE_TRANSLATION:
- errno = ENOENT;
- break;
- default:
- errno = EINVAL;
- break;
- }
- }
- else
- {
- size_t size_to_copy = buf_size;
- BYTE *p = lname, *p2;
- BYTE *pend = p + lname_len;
-
- /* Normalize like dostounix_filename does, but we don't
- want to assume that lname is null-terminated. */
- if (dbcs_p)
- p2 = CharNextExA (file_name_codepage, p, 0);
- else
- p2 = p + 1;
- if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
- {
- *p += 'a' - 'A';
- p += 2;
- }
- while (p <= pend)
- {
- if (*p == '\\')
- *p = '/';
- if (dbcs_p)
- {
- p = CharNextExA (file_name_codepage, p, 0);
- /* CharNextExA doesn't advance at null character. */
- if (!*p)
- break;
- }
- else
- ++p;
- }
- /* Testing for null-terminated LNAME is paranoia:
- WideCharToMultiByte should always return a
- null-terminated string when its 4th argument is -1
- and its 3rd argument is null-terminated (which they
- are, see above). */
- if (lname[lname_len - 1] == '\0')
- lname_len--;
- if (lname_len <= buf_size)
- size_to_copy = lname_len;
- strncpy (buf, lname, size_to_copy);
- /* Success! */
- retval = size_to_copy;
- }
+ filename_from_utf16 (lwname, resolved);
+ dostounix_filename (resolved);
+ lname_size = strlen (resolved) + 1;
+ if (lname_size <= buf_size)
+ size_to_copy = lname_size;
+ strncpy (buf, resolved, size_to_copy);
+ /* Success! */
+ retval = size_to_copy;
}
CloseHandle (sh);
}
{
/* Rely on a hack: an open directory is modeled as file descriptor 0,
as in fstatat. FIXME: Add proper support for readlinkat. */
- char fullname[MAX_PATH];
+ char fullname[MAX_UTF8_PATH];
if (fd != AT_FDCWD)
{
static char *
chase_symlinks (const char *file)
{
- static char target[MAX_PATH];
- char link[MAX_PATH];
+ static char target[MAX_UTF8_PATH];
+ char link[MAX_UTF8_PATH];
+ wchar_t target_w[MAX_PATH], link_w[MAX_PATH];
+ char target_a[MAX_PATH], link_a[MAX_PATH];
ssize_t res, link_len;
int loop_count = 0;
- int dbcs_p;
if (is_windows_9x () == TRUE || !is_symlink (file))
return (char *)file;
- if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
- return (char *)file;
+ if (w32_unicode_filenames)
+ {
+ wchar_t file_w[MAX_PATH];
+
+ filename_to_utf16 (file, file_w);
+ if (GetFullPathNameW (file_w, MAX_PATH, link_w, NULL) == 0)
+ return (char *)file;
+ filename_from_utf16 (link_w, link);
+ }
+ else
+ {
+ char file_a[MAX_PATH];
+
+ filename_to_ansi (file, file_a);
+ if (GetFullPathNameA (file_a, MAX_PATH, link_a, NULL) == 0)
+ return (char *)file;
+ filename_from_ansi (link_a, link);
+ }
+ link_len = strlen (link);
- dbcs_p = max_filename_mbslen () > 1;
target[0] = '\0';
do {
/* Remove trailing slashes, as we want to resolve the last
non-trivial part of the link name. */
- if (!dbcs_p)
- {
- while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
- link[link_len--] = '\0';
- }
- else if (link_len > 3)
- {
- char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
-
- while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
- {
- n[1] = '\0';
- n = CharPrevExA (file_name_codepage, link, n, 0);
- }
- }
+ while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+ link[link_len--] = '\0';
- res = readlink (link, target, MAX_PATH);
+ res = readlink (link, target, MAX_UTF8_PATH);
if (res > 0)
{
target[res] = '\0';
the symlink, then copy the result back to target. */
char *p = link + link_len;
- if (!dbcs_p)
- {
- while (p > link && !IS_ANY_SEP (p[-1]))
- p--;
- }
- else
- {
- char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
-
- while (p > link && !IS_ANY_SEP (*p1))
- {
- p = p1;
- p1 = CharPrevExA (file_name_codepage, link, p1, 0);
- }
- }
+ while (p > link && !IS_ANY_SEP (p[-1]))
+ p--;
strcpy (p, target);
strcpy (target, link);
}
/* Resolve any "." and ".." to get a fully-qualified file name
in link[] again. */
- link_len = GetFullPathName (target, MAX_PATH, link, NULL);
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (target, target_w);
+ link_len = GetFullPathNameW (target_w, MAX_PATH, link_w, NULL);
+ if (link_len > 0)
+ filename_from_utf16 (link_w, link);
+ }
+ else
+ {
+ filename_to_ansi (target, target_a);
+ link_len = GetFullPathNameA (target_a, MAX_PATH, link_a, NULL);
+ if (link_len > 0)
+ filename_from_ansi (link_a, link);
+ }
+ link_len = strlen (link);
}
} while (res > 0 && link_len > 0 && ++loop_count <= 100);
}
}
else if (err == ERROR_FILE_NOT_FOUND
- || err == ERROR_PATH_NOT_FOUND)
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot
+ be encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EIO;
DWORD err;
int st = 0, retval = -1;
SECURITY_INFORMATION flags = 0;
- PSID psid;
+ PSID psidOwner, psidGroup;
PACL pacl;
BOOL dflt;
BOOL dacl_present;
else
fname = filename;
- if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
- && psid)
+ if (get_security_descriptor_owner ((PSECURITY_DESCRIPTOR)acl, &psidOwner,
+ &dflt)
+ && psidOwner)
flags |= OWNER_SECURITY_INFORMATION;
- if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psid, &dflt)
- && psid)
+ if (get_security_descriptor_group ((PSECURITY_DESCRIPTOR)acl, &psidGroup,
+ &dflt)
+ && psidGroup)
flags |= GROUP_SECURITY_INFORMATION;
if (get_security_descriptor_dacl ((PSECURITY_DESCRIPTOR)acl, &dacl_present,
&pacl, &dflt)
e = errno;
errno = 0;
- if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
+ /* SetFileSecurity is deprecated by MS, and sometimes fails when
+ DACL inheritance is involved, but it seems to preserve ownership
+ better than SetNamedSecurityInfo, which is important e.g., in
+ copy-file. */
+ if (!set_file_security (fname, flags, (PSECURITY_DESCRIPTOR)acl))
{
err = GetLastError ();
+ if (errno != ENOTSUP)
+ err = set_named_security_info (fname, SE_FILE_OBJECT, flags,
+ psidOwner, psidGroup, pacl, NULL);
+ }
+ else
+ err = ERROR_SUCCESS;
+ if (err != ERROR_SUCCESS)
+ {
if (errno == ENOTSUP)
;
else if (err == ERROR_INVALID_OWNER
acl_free (current_acl);
}
}
- else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ else if (err == ERROR_FILE_NOT_FOUND
+ || err == ERROR_PATH_NOT_FOUND
+ /* ERROR_INVALID_NAME is what we get if
+ w32-unicode-filenames is nil and the file cannot be
+ encoded in the current ANSI codepage. */
+ || err == ERROR_INVALID_NAME)
errno = ENOENT;
else
errno = EACCES;
struct allocator const *alloc,
ssize_t (*preadlinkat) (int, char const *, char *, size_t))
{
- char linkname[MAX_PATH];
+ char linkname[MAX_UTF8_PATH];
ssize_t link_size;
link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
return NULL;
}
+int
+w32_copy_file (const char *from, const char *to,
+ int keep_time, int preserve_ownership, int copy_acls)
+{
+ acl_t acl = NULL;
+ BOOL copy_result;
+ wchar_t from_w[MAX_PATH], to_w[MAX_PATH];
+ char from_a[MAX_PATH], to_a[MAX_PATH];
+
+ /* We ignore preserve_ownership for now. */
+ preserve_ownership = preserve_ownership;
+
+ if (copy_acls)
+ {
+ acl = acl_get_file (from, ACL_TYPE_ACCESS);
+ if (acl == NULL && acl_errno_valid (errno))
+ return -2;
+ }
+ if (w32_unicode_filenames)
+ {
+ filename_to_utf16 (from, from_w);
+ filename_to_utf16 (to, to_w);
+ copy_result = CopyFileW (from_w, to_w, FALSE);
+ }
+ else
+ {
+ filename_to_ansi (from, from_a);
+ filename_to_ansi (to, to_a);
+ copy_result = CopyFileA (from_a, to_a, FALSE);
+ }
+ if (!copy_result)
+ {
+ /* CopyFile doesn't set errno when it fails. By far the most
+ "popular" reason is that the target is read-only. */
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_FILE_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_ACCESS_DENIED:
+ errno = EACCES;
+ break;
+ case ERROR_ENCRYPTION_FAILED:
+ errno = EIO;
+ break;
+ default:
+ errno = EPERM;
+ break;
+ }
+
+ if (acl)
+ acl_free (acl);
+ return -1;
+ }
+ /* CopyFile retains the timestamp by default. However, see
+ "Community Additions" for CopyFile: it sounds like that is not
+ entirely true. Testing on Windows XP confirms that modified time
+ is copied, but creation and last-access times are not.
+ FIXME? */
+ else if (!keep_time)
+ {
+ struct timespec now;
+ DWORD attributes;
+
+ if (w32_unicode_filenames)
+ {
+ /* Ensure file is writable while its times are set. */
+ attributes = GetFileAttributesW (to_w);
+ SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY);
+ now = current_timespec ();
+ if (set_file_times (-1, to, now, now))
+ {
+ /* Restore original attributes. */
+ SetFileAttributesW (to_w, attributes);
+ if (acl)
+ acl_free (acl);
+ return -3;
+ }
+ /* Restore original attributes. */
+ SetFileAttributesW (to_w, attributes);
+ }
+ else
+ {
+ attributes = GetFileAttributesA (to_a);
+ SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY);
+ now = current_timespec ();
+ if (set_file_times (-1, to, now, now))
+ {
+ SetFileAttributesA (to_a, attributes);
+ if (acl)
+ acl_free (acl);
+ return -3;
+ }
+ SetFileAttributesA (to_a, attributes);
+ }
+ }
+ if (acl != NULL)
+ {
+ bool fail =
+ acl_set_file (to, ACL_TYPE_ACCESS, acl) != 0;
+ acl_free (acl);
+ if (fail && acl_errno_valid (errno))
+ return -4;
+ }
+
+ return 0;
+}
+
\f
/* Support for browsing other processes and their attributes. See
process.c for the Lisp bindings. */
/* Hardware address and its family. */
for (n = 0; n < adapter->AddressLength; n++)
- p->u.contents[n] = make_number ((int) adapter->Address[n]);
+ p->contents[n] = make_number ((int) adapter->Address[n]);
/* Windows does not support AF_LINK or AF_PACKET family
of addresses. Use an arbitrary family number that is
identical to what GNU/Linux returns. */
HMODULE
w32_delayed_load (Lisp_Object library_id)
{
- HMODULE library_dll = NULL;
+ HMODULE dll_handle = NULL;
CHECK_SYMBOL (library_id);
if (CONSP (dlls))
for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls))
{
- CHECK_STRING_CAR (dlls);
- if ((library_dll = LoadLibrary (SDATA (XCAR (dlls)))))
- {
- char name[MAX_PATH];
- DWORD len;
-
- len = GetModuleFileNameA (library_dll, name, sizeof (name));
- found = Fcons (XCAR (dlls),
- (len > 0)
- /* Possibly truncated */
- ? make_specified_string (name, -1, len, 1)
- : Qnil);
- break;
- }
- }
+ Lisp_Object dll = XCAR (dlls);
+ char name[MAX_UTF8_PATH];
+ DWORD res = -1;
+
+ CHECK_STRING (dll);
+ dll = ENCODE_FILE (dll);
+ if (w32_unicode_filenames)
+ {
+ wchar_t name_w[MAX_PATH];
+
+ filename_to_utf16 (SSDATA (dll), name_w);
+ dll_handle = LoadLibraryW (name_w);
+ if (dll_handle)
+ {
+ res = GetModuleFileNameW (dll_handle, name_w,
+ sizeof (name_w));
+ if (res > 0)
+ filename_from_utf16 (name_w, name);
+ }
+ }
+ else
+ {
+ char name_a[MAX_PATH];
+
+ filename_to_ansi (SSDATA (dll), name_a);
+ dll_handle = LoadLibraryA (name_a);
+ if (dll_handle)
+ {
+ res = GetModuleFileNameA (dll_handle, name_a,
+ sizeof (name_a));
+ if (res > 0)
+ filename_from_ansi (name_a, name);
+ }
+ }
+ if (dll_handle)
+ {
+ ptrdiff_t len = strlen (name);
+ found = Fcons (dll,
+ (res > 0)
+ /* Possibly truncated */
+ ? make_specified_string (name, -1, len, 1)
+ : Qnil);
+ break;
+ }
+ }
Fput (library_id, QCloaded_from, found);
}
- return library_dll;
+ return dll_handle;
}
\f
Lisp_Object init_file;
int fd;
+ /* Implementation note: this function runs early during Emacs
+ startup, before startup.el is run. So Vload_path is still in
+ its initial unibyte form, but it holds UTF-8 encoded file
+ names, since init_callproc was already called. So we do not
+ need to ENCODE_FILE here, but we do need to convert the file
+ names from UTF-8 to ANSI. */
init_file = build_string ("term/w32-win");
- fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil);
+ fd = openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0);
if (fd < 0)
{
Lisp_Object load_path_print = Fprin1_to_string (Vload_path, Qnil);
char *buffer = alloca (1024
+ strlen (init_file_name)
+ strlen (load_path));
+ char *msg = buffer;
+ int needed;
sprintf (buffer,
"The Emacs Windows initialization file \"%s.el\" "
"not unpacked properly.\nSee the README.W32 file in the "
"top-level Emacs directory for more information.",
init_file_name, load_path);
+ needed = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer,
+ -1, NULL, 0);
+ if (needed > 0)
+ {
+ wchar_t *msg_w = alloca ((needed + 1) * sizeof (wchar_t));
+
+ MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1,
+ msg_w, needed);
+ needed = WideCharToMultiByte (CP_ACP, 0, msg_w, -1,
+ NULL, 0, NULL, NULL);
+ if (needed > 0)
+ {
+ char *msg_a = alloca (needed + 1);
+
+ WideCharToMultiByte (CP_ACP, 0, msg_w, -1, msg_a, needed,
+ NULL, NULL);
+ msg = msg_a;
+ }
+ }
MessageBox (NULL,
- buffer,
+ msg,
"Emacs Abort Dialog",
MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
/* Use the low-level system abort. */
g_b_init_get_sid_sub_authority = 0;
g_b_init_get_sid_sub_authority_count = 0;
g_b_init_get_security_info = 0;
- g_b_init_get_file_security = 0;
+ g_b_init_get_file_security_w = 0;
+ g_b_init_get_file_security_a = 0;
g_b_init_get_security_descriptor_owner = 0;
g_b_init_get_security_descriptor_group = 0;
g_b_init_is_valid_sid = 0;
g_b_init_get_length_sid = 0;
g_b_init_get_native_system_info = 0;
g_b_init_get_system_times = 0;
- g_b_init_create_symbolic_link = 0;
+ g_b_init_create_symbolic_link_w = 0;
+ g_b_init_create_symbolic_link_a = 0;
g_b_init_get_security_descriptor_dacl = 0;
g_b_init_convert_sd_to_sddl = 0;
g_b_init_convert_sddl_to_sd = 0;
g_b_init_is_valid_security_descriptor = 0;
- g_b_init_set_file_security = 0;
+ g_b_init_set_file_security_w = 0;
+ g_b_init_set_file_security_a = 0;
+ g_b_init_set_named_security_info_w = 0;
+ g_b_init_set_named_security_info_a = 0;
g_b_init_get_adapters_info = 0;
num_of_processors = 0;
/* The following sets a handler for shutdown notifications for