X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/bd358779861f265a7acff31ead40172735af693e..6d81466ac65eb84cb77eaa73e589628b5afcb963:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 7a39a617ee..aba0b5a81f 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,5 +1,6 @@ /* 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. @@ -19,6 +20,8 @@ along with GNU Emacs. If not, see . */ /* Geoff Voelker (voelker@cs.washington.edu) 7-29-94 */ + +#include #include /* for offsetof */ #include #include @@ -47,7 +50,6 @@ along with GNU Emacs. If not, see . */ #undef fopen #undef link #undef mkdir -#undef mktemp #undef open #undef rename #undef rmdir @@ -90,6 +92,21 @@ typedef struct _MEMORY_STATUS_EX { DWORDLONG ullAvailExtendedVirtual; } MEMORY_STATUS_EX,*LPMEMORY_STATUS_EX; +/* These are here so that GDB would know about these data types. This + allows to attach GDB to Emacs when a fatal exception is triggered + and Windows pops up the "application needs to be closed" dialog. + At that point, _gnu_exception_handler, the top-level exception + handler installed by the MinGW startup code, is somewhere on the + call-stack of the main thread, so going to that call frame and + looking at the argument to _gnu_exception_handler, which is a + PEXCEPTION_POINTERS pointer, can reveal the exception code + (excptr->ExceptionRecord->ExceptionCode) and the address where the + exception happened (excptr->ExceptionRecord->ExceptionAddress), as + well as some additional information specific to the exception. */ +PEXCEPTION_POINTERS excptr; +PEXCEPTION_RECORD excprec; +PCONTEXT ctxrec; + #include #include @@ -124,6 +141,7 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX { #include #include +#include /* This is not in MinGW's sddl.h (but they are in MSVC headers), so we define them by hand if not already defined. */ @@ -202,6 +220,8 @@ typedef struct _REPARSE_DATA_BUFFER { #undef recvfrom #undef sendto +#include /* should be after winsock2.h */ + #include "w32.h" #include #include "w32common.h" @@ -230,10 +250,12 @@ static int enable_privilege (LPCTSTR, BOOL, TOKEN_PRIVILEGES *); static int restore_privilege (TOKEN_PRIVILEGES *); static BOOL WINAPI revert_to_self (void); -extern int sys_access (const char *, int); +static int sys_access (const char *, int); extern void *e_malloc (size_t); extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *, - EMACS_TIME *, void *); + struct timespec *, void *); +extern int sys_dup (int); + @@ -253,7 +275,8 @@ static BOOL g_b_init_lookup_account_sid; 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; @@ -272,12 +295,17 @@ static BOOL g_b_init_equal_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; /* BEGIN: Wrapper functions around OpenProcessToken @@ -306,12 +334,8 @@ GetProcessTimes_Proc get_process_times_fn = NULL; #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, @@ -335,16 +359,42 @@ typedef DWORD (WINAPI * GetSecurityInfo_Proc) ( 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, @@ -404,9 +454,13 @@ typedef BOOL (WINAPI * GetSystemTimes_Proc) ( 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, @@ -420,6 +474,12 @@ typedef BOOL (WINAPI *ConvertSecurityDescriptorToStringSecurityDescriptor_Proc) LPTSTR *StringSecurityDescriptor, PULONG StringSecurityDescriptorLen); typedef BOOL (WINAPI *IsValidSecurityDescriptor_Proc) (PSECURITY_DESCRIPTOR); +typedef DWORD (WINAPI *GetAdaptersInfo_Proc) ( + PIP_ADAPTER_INFO pAdapterInfo, + PULONG pOutBufLen); + +int (WINAPI *pMultiByteToWideChar)(UINT,DWORD,LPCSTR,int,LPWSTR,int); +int (WINAPI *pWideCharToMultiByte)(UINT,DWORD,LPCWSTR,int,LPSTR,int,LPCSTR,LPBOOL); /* ** A utility function ** */ static BOOL @@ -655,64 +715,184 @@ get_security_info (HANDLE handle, } 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) { - g_b_init_set_file_security = 1; - hm_advapi32 = LoadLibrary ("Advapi32.dll"); - s_pfn_Set_File_Security = - (SetFileSecurity_Proc) GetProcAddress ( - hm_advapi32, SetFileSecurity_Name); + 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 + { + 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 @@ -949,11 +1129,12 @@ get_system_times (LPFILETIME lpIdleTime, } 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) @@ -961,39 +1142,74 @@ create_symbolic_link (LPTSTR lpSymlinkFilename, 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; @@ -1112,6 +1328,28 @@ convert_sddl_to_sd (LPCTSTR StringSecurityDescriptor, return retval; } +static DWORD WINAPI +get_adapters_info (PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen) +{ + static GetAdaptersInfo_Proc s_pfn_Get_Adapters_Info = NULL; + HMODULE hm_iphlpapi = NULL; + + if (is_windows_9x () == TRUE) + return ERROR_NOT_SUPPORTED; + + if (g_b_init_get_adapters_info == 0) + { + g_b_init_get_adapters_info = 1; + hm_iphlpapi = LoadLibrary ("Iphlpapi.dll"); + if (hm_iphlpapi) + s_pfn_Get_Adapters_Info = (GetAdaptersInfo_Proc) + GetProcAddress (hm_iphlpapi, "GetAdaptersInfo"); + } + if (s_pfn_Get_Adapters_Info == NULL) + return ERROR_NOT_SUPPORTED; + return s_pfn_Get_Adapters_Info (pAdapterInfo, pOutBufLen); +} + /* Return 1 if P is a valid pointer to an object of size SIZE. Return @@ -1136,7 +1374,297 @@ w32_valid_pointer_p (void *p, int size) return -1; } -static char startup_dir[MAXPATHLEN]; + + +/* 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 did 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 + names from UTF-8 to 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 + 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 thus a requirement even in + non-GUI sessions, something the we previously avoided. */ + + + +/* Converting file names from UTF-8 to either UTF-16 or the ANSI + codepage defined by file-name-coding-system. */ + +/* Current codepage for encoding file names. */ +static int file_name_codepage; + +/* Produce a Windows ANSI codepage suitable for encoding file names. + Return the information about that codepage in CP_INFO. */ +static int +codepage_for_filenames (CPINFO *cp_info) +{ + /* A simple cache to avoid calling GetCPInfo every time we need to + encode/decode a file name. The file-name encoding is not + supposed to be changed too frequently, if ever. */ + static Lisp_Object last_file_name_encoding; + static CPINFO cp; + Lisp_Object current_encoding; + + current_encoding = Vfile_name_coding_system; + if (NILP (current_encoding)) + current_encoding = Vdefault_file_name_coding_system; + + if (!EQ (last_file_name_encoding, current_encoding)) + { + /* Default to the current ANSI codepage. */ + file_name_codepage = w32_ansi_code_page; + + if (NILP (current_encoding)) + { + char *cpname = SDATA (SYMBOL_NAME (current_encoding)); + char *cp = NULL, *end; + int cpnum; + + if (strncmp (cpname, "cp", 2) == 0) + cp = cpname + 2; + else if (strncmp (cpname, "windows-", 8) == 0) + cp = cpname + 8; + + if (cp) + { + end = cp; + cpnum = strtol (cp, &end, 10); + if (cpnum && *end == '\0' && end - cp >= 2) + file_name_codepage = cpnum; + } + } + + if (!file_name_codepage) + file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */ + + if (!GetCPInfo (file_name_codepage, &cp)) + { + file_name_codepage = CP_ACP; + if (!GetCPInfo (file_name_codepage, &cp)) + emacs_abort (); + } + } + if (cp_info) + *cp_info = cp; + + return file_name_codepage; +} + +int +filename_to_utf16 (const char *fn_in, wchar_t *fn_out) +{ + int result = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, fn_in, -1, + fn_out, MAX_PATH); + + if (!result) + { + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_NO_UNICODE_TRANSLATION: + default: + errno = ENOENT; + break; + } + return -1; + } + return 0; +} + +int +filename_from_utf16 (const wchar_t *fn_in, char *fn_out) +{ + int result = pWideCharToMultiByte (CP_UTF8, 0, fn_in, -1, + fn_out, MAX_UTF8_PATH, NULL, NULL); + + if (!result) + { + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_NO_UNICODE_TRANSLATION: + default: + errno = ENOENT; + break; + } + return -1; + } + return 0; +} + +int +filename_to_ansi (const char *fn_in, char *fn_out) +{ + wchar_t fn_utf16[MAX_PATH]; + + if (filename_to_utf16 (fn_in, fn_utf16) == 0) + { + int result; + int codepage = codepage_for_filenames (NULL); + + result = pWideCharToMultiByte (codepage, 0, fn_utf16, -1, + fn_out, MAX_PATH, NULL, NULL); + if (!result) + { + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_NO_UNICODE_TRANSLATION: + default: + errno = ENOENT; + break; + } + return -1; + } + return 0; + } + return -1; +} + +int +filename_from_ansi (const char *fn_in, char *fn_out) +{ + wchar_t fn_utf16[MAX_PATH]; + int codepage = codepage_for_filenames (NULL); + int result = pMultiByteToWideChar (codepage, MB_ERR_INVALID_CHARS, fn_in, -1, + fn_utf16, MAX_PATH); + + if (!result) + { + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_INVALID_FLAGS: + case ERROR_INVALID_PARAMETER: + errno = EINVAL; + break; + case ERROR_INSUFFICIENT_BUFFER: + case ERROR_NO_UNICODE_TRANSLATION: + default: + errno = ENOENT; + break; + } + return -1; + } + return filename_from_utf16 (fn_utf16, fn_out); +} + + + +/* The directory where we started, in UTF-8. */ +static char startup_dir[MAX_UTF8_PATH]; /* Get the current working directory. */ char * @@ -1289,9 +1817,28 @@ getloadavg (double loadavg[], int nelem) 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); @@ -1328,8 +1875,8 @@ getloadavg (double loadavg[], int nelem) static char dflt_passwd_name[PASSWD_FIELD_SIZE]; static char dflt_passwd_passwd[PASSWD_FIELD_SIZE]; static char dflt_passwd_gecos[PASSWD_FIELD_SIZE]; -static char dflt_passwd_dir[PASSWD_FIELD_SIZE]; -static char dflt_passwd_shell[PASSWD_FIELD_SIZE]; +static char dflt_passwd_dir[MAX_UTF8_PATH]; +static char dflt_passwd_shell[MAX_UTF8_PATH]; static struct passwd dflt_passwd = { @@ -1510,15 +2057,32 @@ init_user_info (void) } dflt_group.gr_gid = dflt_passwd.pw_gid; - /* Ensure HOME and SHELL are defined. */ - if (getenv ("HOME") == NULL) - emacs_abort (); - if (getenv ("SHELL") == NULL) - emacs_abort (); - /* Set dir and shell from environment variables. */ - strcpy (dflt_passwd.pw_dir, getenv ("HOME")); - strcpy (dflt_passwd.pw_shell, getenv ("SHELL")); + if (w32_unicode_filenames) + { + wchar_t *home = _wgetenv (L"HOME"); + wchar_t *shell = _wgetenv (L"SHELL"); + + /* Ensure HOME and SHELL are defined. */ + if (home == NULL) + emacs_abort (); + if (shell == NULL) + emacs_abort (); + filename_from_utf16 (home, dflt_passwd.pw_dir); + filename_from_utf16 (shell, dflt_passwd.pw_shell); + } + else + { + char *home = getenv ("HOME"); + char *shell = getenv ("SHELL"); + + if (home == NULL) + emacs_abort (); + if (shell == NULL) + emacs_abort (); + filename_from_ansi (home, dflt_passwd.pw_dir); + filename_from_ansi (shell, dflt_passwd.pw_shell); + } xfree (buf); if (token) @@ -1538,93 +2102,32 @@ srandom (int seed) srand (seed); } -/* Current codepage for encoding file names. */ -static int file_name_codepage; - /* Return the maximum length in bytes of a multibyte character sequence encoded in the current ANSI codepage. This is required to correctly walk the encoded file names one character at a time. */ static int max_filename_mbslen (void) { - /* A simple cache to avoid calling GetCPInfo every time we need to - normalize a file name. The file-name encoding is not supposed to - be changed too frequently, if ever. */ - static Lisp_Object last_file_name_encoding; - static int last_max_mbslen; - Lisp_Object current_encoding; - - current_encoding = Vfile_name_coding_system; - if (NILP (current_encoding)) - current_encoding = Vdefault_file_name_coding_system; - - if (!EQ (last_file_name_encoding, current_encoding)) - { - CPINFO cp_info; - - last_file_name_encoding = current_encoding; - /* Default to the current ANSI codepage. */ - file_name_codepage = w32_ansi_code_page; - if (!NILP (current_encoding)) - { - char *cpname = SDATA (SYMBOL_NAME (current_encoding)); - char *cp = NULL, *end; - int cpnum; - - if (strncmp (cpname, "cp", 2) == 0) - cp = cpname + 2; - else if (strncmp (cpname, "windows-", 8) == 0) - cp = cpname + 8; - - if (cp) - { - end = cp; - cpnum = strtol (cp, &end, 10); - if (cpnum && *end == '\0' && end - cp >= 2) - file_name_codepage = cpnum; - } - } - - if (!file_name_codepage) - file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */ + CPINFO cp_info; - if (!GetCPInfo (file_name_codepage, &cp_info)) - { - file_name_codepage = CP_ACP; - if (!GetCPInfo (file_name_codepage, &cp_info)) - emacs_abort (); - } - last_max_mbslen = cp_info.MaxCharSize; - } - - return last_max_mbslen; + codepage_for_filenames (&cp_info); + return cp_info.MaxCharSize; } -/* Normalize filename by converting all path separators to - the specified separator. Also conditionally convert upper - case path name components to lower case. */ +/* Normalize filename by converting in-place all of its path + separators to the separator specified by PATH_SEP. */ static void -normalize_filename (register char *fp, char path_sep, int multibyte) +normalize_filename (register char *fp, char path_sep) { - char sep; - char *elem, *p2; - int dbcs_p = max_filename_mbslen () > 1; - - /* Multibyte file names are in the Emacs internal representation, so - we can traverse them by bytes with no problems. */ - if (multibyte) - dbcs_p = 0; + char *p2; /* Always lower-case drive letters a-z, even if the filesystem preserves case in filenames. This is so filenames can be compared by string comparison functions that are case-sensitive. Even case-preserving filesystems do not distinguish case in drive letters. */ - if (dbcs_p) - p2 = CharNextExA (file_name_codepage, fp, 0); - else - p2 = fp + 1; + p2 = fp + 1; if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z') { @@ -1632,68 +2135,26 @@ normalize_filename (register char *fp, char path_sep, int multibyte) fp += 2; } - if (multibyte || NILP (Vw32_downcase_file_names)) + while (*fp) { - while (*fp) - { - if (*fp == '/' || *fp == '\\') - *fp = path_sep; - if (!dbcs_p) - fp++; - else - fp = CharNextExA (file_name_codepage, fp, 0); - } - return; + if ((*fp == '/' || *fp == '\\') && *fp != path_sep) + *fp = path_sep; + fp++; } - - sep = path_sep; /* convert to this path separator */ - elem = fp; /* start of current path element */ - - do { - if (*fp >= 'a' && *fp <= 'z') - elem = 0; /* don't convert this element */ - - if (*fp == 0 || *fp == ':') - { - sep = *fp; /* restore current separator (or 0) */ - *fp = '/'; /* after conversion of this element */ - } - - if (*fp == '/' || *fp == '\\') - { - if (elem && elem != fp) - { - *fp = 0; /* temporary end of string */ - _mbslwr (elem); /* while we convert to lower case */ - } - *fp = sep; /* convert (or restore) path separator */ - elem = fp + 1; /* next element starts after separator */ - sep = path_sep; - } - if (*fp) - { - if (!dbcs_p) - fp++; - else - fp = CharNextExA (file_name_codepage, fp, 0); - } - } while (*fp); } -/* Destructively turn backslashes into slashes. MULTIBYTE non-zero - means the file name is a multibyte string in Emacs's internal - representation. */ +/* Destructively turn backslashes into slashes. */ void -dostounix_filename (register char *p, int multibyte) +dostounix_filename (register char *p) { - normalize_filename (p, '/', multibyte); + normalize_filename (p, '/'); } /* Destructively turn slashes into backslashes. */ void unixtodos_filename (register char *p) { - normalize_filename (p, '\\', 0); + normalize_filename (p, '\\'); } /* Remove all CR's that are followed by a LF. @@ -1726,9 +2187,9 @@ crlf_to_lf (register int n, register unsigned char *buf) /* Parse the root part of file name, if present. Return length and optionally store pointer to char after root. */ static int -parse_root (char * name, char ** pPath) +parse_root (const char * name, const char ** pPath) { - char * start = name; + const char * start = name; if (name == NULL) return 0; @@ -1744,17 +2205,13 @@ parse_root (char * name, char ** pPath) else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) { int slashes = 2; - int dbcs_p = max_filename_mbslen () > 1; name += 2; do { if (IS_DIRECTORY_SEP (*name) && --slashes == 0) break; - if (dbcs_p) - name = CharNextExA (file_name_codepage, name, 0); - else - name++; + name++; } while ( *name ); if (IS_DIRECTORY_SEP (name[0])) @@ -1771,23 +2228,63 @@ parse_root (char * name, char ** pPath) static int get_long_basename (char * name, char * buf, int size) { - WIN32_FIND_DATA find_data; - HANDLE dir_handle; + HANDLE dir_handle = INVALID_HANDLE_VALUE; + char fname_utf8[MAX_UTF8_PATH]; int len = 0; + int cstatus = -1; - /* must be valid filename, no wild cards or other invalid characters */ - if (_mbspbrk (name, "*?|<>\"")) + /* Must be valid filename, no wild cards or other invalid characters. */ + if (strpbrk (name, "*?|<>\"")) return 0; - dir_handle = FindFirstFile (name, &find_data); - if (dir_handle != INVALID_HANDLE_VALUE) + if (w32_unicode_filenames) { - if ((len = strlen (find_data.cFileName)) < size) - memcpy (buf, find_data.cFileName, len + 1); + wchar_t fname_utf16[MAX_PATH]; + WIN32_FIND_DATAW find_data_wide; + + filename_to_utf16 (name, fname_utf16); + dir_handle = FindFirstFileW (fname_utf16, &find_data_wide); + if (dir_handle != INVALID_HANDLE_VALUE) + cstatus = filename_from_utf16 (find_data_wide.cFileName, fname_utf8); + } + else + { + char fname_ansi[MAX_PATH]; + WIN32_FIND_DATAA find_data_ansi; + + filename_to_ansi (name, fname_ansi); + /* 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 - len = 0; - FindClose (dir_handle); + { + 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) + memcpy (buf, fname_utf8, len + 1); + else + len = 0; + + if (dir_handle != INVALID_HANDLE_VALUE) + FindClose (dir_handle); + return len; } @@ -1797,12 +2294,12 @@ w32_get_long_filename (char * name, char * buf, int size) { char * o = buf; char * p; - char * q; - char full[ MAX_PATH ]; + const char * q; + char full[ MAX_UTF8_PATH ]; int len; len = strlen (name); - if (len >= MAX_PATH) + if (len >= MAX_UTF8_PATH) return FALSE; /* Use local copy for destructive modification. */ @@ -1810,7 +2307,7 @@ w32_get_long_filename (char * name, char * buf, int size) unixtodos_filename (full); /* Copy root part verbatim. */ - len = parse_root (full, &p); + len = parse_root (full, (const char **)&p); memcpy (o, full, len); o += len; *o = '\0'; @@ -1819,7 +2316,7 @@ w32_get_long_filename (char * name, char * buf, int size) while (p != NULL && *p) { q = p; - p = _mbschr (q, '\\'); + p = strchr (q, '\\'); if (p) *p = '\0'; len = get_long_basename (full, o, size); if (len > 0) @@ -1843,6 +2340,61 @@ w32_get_long_filename (char * name, char * buf, int size) return TRUE; } +unsigned int +w32_get_short_filename (char * name, char * buf, int size) +{ + if (w32_unicode_filenames) + { + wchar_t name_utf16[MAX_PATH], short_name[MAX_PATH]; + unsigned int retval; + + filename_to_utf16 (name, name_utf16); + retval = GetShortPathNameW (name_utf16, short_name, size); + if (retval && retval < size) + filename_from_utf16 (short_name, buf); + return retval; + } + else + { + char name_ansi[MAX_PATH]; + + filename_to_ansi (name, name_ansi); + return GetShortPathNameA (name_ansi, buf, 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); + } + else + encoded_filename = build_unibyte_string (fname); + return encoded_filename; +} + static int is_unc_volume (const char *filename) { @@ -1851,7 +2403,7 @@ is_unc_volume (const char *filename) if (!IS_DIRECTORY_SEP (ptr[0]) || !IS_DIRECTORY_SEP (ptr[1]) || !ptr[2]) return 0; - if (_mbspbrk (ptr + 2, "*?|<>\"\\/")) + if (strpbrk (ptr + 2, "*?|<>\"\\/")) return 0; return 1; @@ -1951,8 +2503,8 @@ w32_get_resource (char *key, LPDWORD lpdwtype) return (NULL); } -char *get_emacs_configuration (void); - +/* The argv[] array holds ANSI-encoded strings, and so this function + works with ANS_encoded strings. */ void init_environment (char ** argv) { @@ -1964,6 +2516,13 @@ init_environment (char ** argv) const int imax = sizeof (tempdirs) / sizeof (tempdirs[0]); + /* Implementation note: This function explicitly works with ANSI + file names, not with UTF-8 encoded file names. This is because + this function pushes variables into the Emacs's environment, and + the environment variables are always assumed to be in the + locale-specific encoding. Do NOT call any functions that accept + UTF-8 file names from this function! */ + /* Make sure they have a usable $TMPDIR. Many Emacs functions use temporary files and assume "/tmp" if $TMPDIR is unset, which will break on DOS/Windows. Refuse to work if we cannot find @@ -1979,8 +2538,8 @@ init_environment (char ** argv) The only way to be really sure is to actually create a file and see if it succeeds. But I think that's too much to ask. */ - /* MSVCRT's _access crashes with D_OK. */ - if (tmp && faccessat (AT_FDCWD, tmp, D_OK, AT_EACCESS) == 0) + /* MSVCRT's _access crashes with D_OK, so we use our replacement. */ + if (tmp && sys_access (tmp, D_OK) == 0) { char * var = alloca (strlen (tmp) + 8); sprintf (var, "TMPDIR=%s", tmp); @@ -2042,7 +2601,7 @@ init_environment (char ** argv) /* For backwards compatibility, check if a .emacs file exists in C:/ If not, then we can try to default to the appdata directory under the user's profile, which is more likely to be writable. */ - if (!check_existing ("C:/.emacs")) + if (sys_access ("C:/.emacs", F_OK) != 0) { HRESULT profile_result; /* Dynamically load ShGetFolderPath, as it won't exist on versions @@ -2089,7 +2648,7 @@ 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 (); @@ -2118,33 +2677,6 @@ init_environment (char ** argv) _putenv (strdup (buf)); } } - /* Handle running emacs from the build directory: src/oo-spd/i386/ */ - - /* FIXME: should use substring of get_emacs_configuration (). - But I don't think the Windows build supports alpha, mips etc - anymore, so have taken the easy option for now. */ - else if (p && (xstrcasecmp (p, "\\i386") == 0 - || xstrcasecmp (p, "\\AMD64") == 0)) - { - *p = 0; - p = _mbsrchr (modname, '\\'); - if (p != NULL) - { - *p = 0; - p = _mbsrchr (modname, '\\'); - if (p && xstrcasecmp (p, "\\src") == 0) - { - char buf[SET_ENV_BUF_SIZE]; - - *p = 0; - for (p = modname; *p; p = CharNext (p)) - if (*p == '\\') *p = '/'; - - _snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname); - _putenv (strdup (buf)); - } - } - } } for (i = 0; i < N_ENV_VARS; i++) @@ -2180,7 +2712,7 @@ init_environment (char ** argv) strcpy (&fname[pend - pstart + 1], "cmdproxy.exe"); ExpandEnvironmentStrings ((LPSTR) fname, bufc, sizeof (bufc)); - if (check_existing (bufc)) + if (sys_access (bufc, F_OK) == 0) { lpval = bufc; dwType = REG_SZ; @@ -2260,13 +2792,27 @@ init_environment (char ** argv) /* Remember the initial working directory for getcwd. */ /* FIXME: Do we need to resolve possible symlinks in startup_dir? Does it matter anywhere in Emacs? */ - if (!GetCurrentDirectory (MAXPATHLEN, startup_dir)) - emacs_abort (); + if (w32_unicode_filenames) + { + wchar_t wstartup_dir[MAX_PATH]; + + if (!GetCurrentDirectoryW (MAX_PATH, wstartup_dir)) + emacs_abort (); + filename_from_utf16 (wstartup_dir, startup_dir); + } + else + { + char astartup_dir[MAX_PATH]; + + if (!GetCurrentDirectoryA (MAX_PATH, astartup_dir)) + emacs_abort (); + filename_from_ansi (astartup_dir, startup_dir); + } { static char modname[MAX_PATH]; - if (!GetModuleFileName (NULL, modname, MAX_PATH)) + if (!GetModuleFileNameA (NULL, modname, MAX_PATH)) emacs_abort (); argv[0] = modname; } @@ -2284,170 +2830,18 @@ init_environment (char ** argv) char * emacs_root_dir (void) { - static char root_dir[FILENAME_MAX]; + static char root_dir[MAX_UTF8_PATH]; const char *p; p = getenv ("emacs_dir"); if (p == NULL) emacs_abort (); - strcpy (root_dir, p); + filename_from_ansi (p, root_dir); root_dir[parse_root (root_dir, NULL)] = '\0'; - dostounix_filename (root_dir, 0); + dostounix_filename (root_dir); return root_dir; } -/* We don't have scripts to automatically determine the system configuration - for Emacs before it's compiled, and we don't want to have to make the - user enter it, so we define EMACS_CONFIGURATION to invoke this runtime - routine. */ - -char * -get_emacs_configuration (void) -{ - char *arch, *oem, *os; - int build_num; - static char configuration_buffer[32]; - - /* Determine the processor type. */ - switch (get_processor_type ()) - { - -#ifdef PROCESSOR_INTEL_386 - case PROCESSOR_INTEL_386: - case PROCESSOR_INTEL_486: - case PROCESSOR_INTEL_PENTIUM: -#ifdef _WIN64 - arch = "amd64"; -#else - arch = "i386"; -#endif - break; -#endif -#ifdef PROCESSOR_AMD_X8664 - case PROCESSOR_AMD_X8664: - arch = "amd64"; - break; -#endif - -#ifdef PROCESSOR_MIPS_R2000 - case PROCESSOR_MIPS_R2000: - case PROCESSOR_MIPS_R3000: - case PROCESSOR_MIPS_R4000: - arch = "mips"; - break; -#endif - -#ifdef PROCESSOR_ALPHA_21064 - case PROCESSOR_ALPHA_21064: - arch = "alpha"; - break; -#endif - - default: - arch = "unknown"; - break; - } - - /* Use the OEM field to reflect the compiler/library combination. */ -#ifdef _MSC_VER -#define COMPILER_NAME "msvc" -#else -#ifdef __GNUC__ -#define COMPILER_NAME "mingw" -#else -#define COMPILER_NAME "unknown" -#endif -#endif - oem = COMPILER_NAME; - - switch (osinfo_cache.dwPlatformId) { - case VER_PLATFORM_WIN32_NT: - os = "nt"; - build_num = osinfo_cache.dwBuildNumber; - break; - case VER_PLATFORM_WIN32_WINDOWS: - if (osinfo_cache.dwMinorVersion == 0) { - os = "windows95"; - } else { - os = "windows98"; - } - build_num = LOWORD (osinfo_cache.dwBuildNumber); - break; - case VER_PLATFORM_WIN32s: - /* Not supported, should not happen. */ - os = "windows32s"; - build_num = LOWORD (osinfo_cache.dwBuildNumber); - break; - default: - os = "unknown"; - build_num = 0; - break; - } - - if (osinfo_cache.dwPlatformId == VER_PLATFORM_WIN32_NT) { - sprintf (configuration_buffer, "%s-%s-%s%d.%d.%d", arch, oem, os, - get_w32_major_version (), get_w32_minor_version (), build_num); - } else { - sprintf (configuration_buffer, "%s-%s-%s.%d", arch, oem, os, build_num); - } - - return configuration_buffer; -} - -char * -get_emacs_configuration_options (void) -{ - static char *options_buffer; - char cv[32]; /* Enough for COMPILER_VERSION. */ - char *options[] = { - cv, /* To be filled later. */ -#ifdef EMACSDEBUG - " --no-opt", -#endif -#ifdef ENABLE_CHECKING - " --enable-checking", -#endif - /* configure.bat already sets USER_CFLAGS and USER_LDFLAGS - with a starting space to save work here. */ -#ifdef USER_CFLAGS - " --cflags", USER_CFLAGS, -#endif -#ifdef USER_LDFLAGS - " --ldflags", USER_LDFLAGS, -#endif - NULL - }; - size_t size = 0; - int i; - -/* Work out the effective configure options for this build. */ -#ifdef _MSC_VER -#define COMPILER_VERSION "--with-msvc (%d.%02d)", _MSC_VER / 100, _MSC_VER % 100 -#else -#ifdef __GNUC__ -#define COMPILER_VERSION "--with-gcc (%d.%d)", __GNUC__, __GNUC_MINOR__ -#else -#define COMPILER_VERSION "" -#endif -#endif - - if (_snprintf (cv, sizeof (cv) - 1, COMPILER_VERSION) < 0) - return "Error: not enough space for compiler version"; - cv[sizeof (cv) - 1] = '\0'; - - for (i = 0; options[i]; i++) - size += strlen (options[i]); - - options_buffer = xmalloc (size + 1); - options_buffer[0] = '\0'; - - for (i = 0; options[i]; i++) - strcat (options_buffer, options[i]); - - return options_buffer; -} - - #include /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */ @@ -2487,8 +2881,6 @@ gettimeofday (struct timeval *__restrict tv, struct timezone *__restrict tz) int fdutimens (int fd, char const *file, struct timespec const timespec[2]) { - struct _utimbuf ut; - if (!timespec) { errno = ENOSYS; @@ -2499,12 +2891,28 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2]) errno = EBADF; return -1; } - ut.actime = timespec[0].tv_sec; - ut.modtime = timespec[1].tv_sec; + /* _futime's prototype defines 2nd arg as having the type 'struct + _utimbuf', while utime needs to accept 'struct utimbuf' for + compatibility with Posix. So we need to use 2 different (but + equivalent) types to avoid compiler warnings, sigh. */ if (fd >= 0) - return _futime (fd, &ut); + { + struct _utimbuf _ut; + + _ut.actime = timespec[0].tv_sec; + _ut.modtime = timespec[1].tv_sec; + return _futime (fd, &_ut); + } else - return _utime (file, &ut); + { + struct utimbuf ut; + + ut.actime = timespec[0].tv_sec; + ut.modtime = timespec[1].tv_sec; + /* Call 'utime', which is implemented below, not the MS library + function, which fails on directories. */ + return utime (file, &ut); + } } @@ -2588,6 +2996,7 @@ static void add_volume_info (char * root_dir, volume_info_data * info) { info->root_dir = xstrdup (root_dir); + unixtodos_filename (info->root_dir); info->next = volume_cache; volume_cache = info; } @@ -2600,14 +3009,30 @@ static volume_info_data * GetCachedVolumeInformation (char * root_dir) { volume_info_data * info; - char default_root[ MAX_PATH ]; + char default_root[ MAX_UTF8_PATH ]; + char name[MAX_PATH+1]; + char type[MAX_PATH+1]; /* NULL for root_dir means use root from current directory. */ if (root_dir == NULL) { - if (GetCurrentDirectory (MAX_PATH, default_root) == 0) - return NULL; - parse_root (default_root, &root_dir); + if (w32_unicode_filenames) + { + wchar_t curdirw[MAX_PATH]; + + if (GetCurrentDirectoryW (MAX_PATH, curdirw) == 0) + return NULL; + filename_from_utf16 (curdirw, default_root); + } + else + { + char curdira[MAX_PATH]; + + if (GetCurrentDirectoryA (MAX_PATH, curdira) == 0) + return NULL; + filename_from_ansi (curdira, default_root); + } + parse_root (default_root, (const char **)&root_dir); *root_dir = 0; root_dir = default_root; } @@ -2646,20 +3071,47 @@ GetCachedVolumeInformation (char * root_dir) if (info == NULL || ! VOLINFO_STILL_VALID (root_dir, info)) { - char name[ 256 ]; DWORD serialnum; DWORD maxcomp; DWORD flags; - char type[ 256 ]; /* Info is not cached, or is stale. */ - if (!GetVolumeInformation (root_dir, - name, sizeof (name), - &serialnum, - &maxcomp, - &flags, - type, sizeof (type))) - return NULL; + if (w32_unicode_filenames) + { + wchar_t root_w[MAX_PATH]; + wchar_t name_w[MAX_PATH+1]; + wchar_t type_w[MAX_PATH+1]; + + filename_to_utf16 (root_dir, root_w); + if (!GetVolumeInformationW (root_w, + name_w, sizeof (name_w), + &serialnum, + &maxcomp, + &flags, + type_w, sizeof (type_w))) + return NULL; + /* Hmm... not really 100% correct, as these 2 are not file + names... */ + filename_from_utf16 (name_w, name); + filename_from_utf16 (type_w, type); + } + else + { + char root_a[MAX_PATH]; + char name_a[MAX_PATH+1]; + char type_a[MAX_PATH+1]; + + filename_to_ansi (root_dir, root_a); + if (!GetVolumeInformationA (root_a, + name_a, sizeof (name_a), + &serialnum, + &maxcomp, + &flags, + type_a, sizeof (type_a))) + return NULL; + filename_from_ansi (name_a, name); + filename_from_ansi (type_a, type); + } /* Cache the volume information for future use, overwriting existing entry if present. */ @@ -2675,6 +3127,7 @@ GetCachedVolumeInformation (char * root_dir) } info->name = xstrdup (name); + unixtodos_filename (info->name); info->serialnum = serialnum; info->maxcomp = maxcomp; info->flags = flags; @@ -2697,52 +3150,22 @@ GetCachedVolumeInformation (char * root_dir) static int get_volume_info (const char * name, const char ** pPath) { - char temp[MAX_PATH]; + char temp[MAX_UTF8_PATH]; char *rootname = NULL; /* default to current volume */ volume_info_data * info; + int root_len = parse_root (name, pPath); if (name == NULL) return FALSE; - /* Find the root name of the volume if given. */ - if (isalpha (name[0]) && name[1] == ':') + /* Copy the root name of the volume, if given. */ + if (root_len) { + strncpy (temp, name, root_len); + temp[root_len] = '\0'; + unixtodos_filename (temp); rootname = temp; - temp[0] = *name++; - temp[1] = *name++; - temp[2] = '\\'; - temp[3] = 0; } - else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1])) - { - char *str = temp; - int slashes = 4; - int dbcs_p = max_filename_mbslen () > 1; - - rootname = temp; - do - { - if (IS_DIRECTORY_SEP (*name) && --slashes == 0) - break; - if (!dbcs_p) - *str++ = *name++; - else - { - const char *p = name; - - name = CharNextExA (file_name_codepage, name, 0); - memcpy (str, p, name - p); - str += name - p; - } - } - while ( *name ); - - *str++ = '\\'; - *str = 0; - } - - if (pPath) - *pPath = name; info = GetCachedVolumeInformation (rootname); if (info != NULL) @@ -2765,18 +3188,19 @@ is_fat_volume (const char * name, const char ** pPath) return FALSE; } -/* Map filename to a valid 8.3 name if necessary. - The result is a pointer to a static buffer, so CAVEAT EMPTOR! */ +/* Convert all slashes in a filename to backslashes, and map filename + to a valid 8.3 name if necessary. The result is a pointer to a + static buffer, so CAVEAT EMPTOR! */ const char * map_w32_filename (const char * name, const char ** pPath) { - static char shortname[MAX_PATH]; + static char shortname[MAX_UTF8_PATH]; char * str = shortname; char c; char * path; const char * save_name = name; - if (strlen (name) >= MAX_PATH) + if (strlen (name) >= sizeof (shortname)) { /* Return a filename which will cause callers to fail. */ strcpy (shortname, "?"); @@ -2843,7 +3267,7 @@ map_w32_filename (const char * name, const char ** pPath) str[-1] = c; /* replace last character of part */ /* FALLTHRU */ default: - if ( left ) + if ( left && 'A' <= c && c <= 'Z' ) { *str++ = tolower (c); /* map to lower case (looks nicer) */ left--; @@ -2878,25 +3302,31 @@ is_exec (const char * name) 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; @@ -2925,8 +3355,8 @@ opendir (const char *filename) 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. */ @@ -2936,7 +3366,7 @@ opendir (const char *filename) } void -closedir (DIR *dirp) +sys_closedir (DIR *dirp) { /* If we have a find-handle open, close it. */ if (dir_find_handle != INVALID_HANDLE_VALUE) @@ -2953,52 +3383,65 @@ closedir (DIR *dirp) } 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; } @@ -3006,112 +3449,155 @@ readdir (DIR *dirp) 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; + } + 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); } - if (!*p) - _mbslwr (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]; - result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_CONNECTABLE, &nr, &henum); + 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]; + + 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 @@ -3142,13 +3628,12 @@ unc_volume_file_attributes (const char *path) 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; @@ -3168,28 +3653,70 @@ logon_network_drive (const char *path) 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]; + + 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; - WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE); + 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). */ @@ -3217,7 +3744,22 @@ faccessat (int dirfd, const char * path, int mode, int flags) && (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 (); @@ -3247,7 +3789,63 @@ faccessat (int dirfd, const char * path, int mode, int flags) return -1; } if ((mode & X_OK) != 0 - && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) + && !(is_exec (path) || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) + { + errno = EACCES; + return -1; + } + if ((mode & W_OK) != 0 && (attributes & FILE_ATTRIBUTE_READONLY) != 0) + { + errno = EACCES; + return -1; + } + if ((mode & D_OK) != 0 && (attributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + { + errno = EACCES; + return -1; + } + return 0; +} + +/* A version of 'access' to be used locally with file names in + locale-specific encoding. Does not resolve symlinks and does not + support file names on FAT12 and FAT16 volumes, but that's OK, since + we only invoke this function for files inside the Emacs source or + installation tree, on directories (so any symlinks should have the + directory bit set), and on short file names such as "C:/.emacs". */ +static int +sys_access (const char *fname, int mode) +{ + char fname_copy[MAX_PATH], *p; + DWORD attributes; + + strcpy (fname_copy, fname); + /* Do the equivalent of unixtodos_filename. */ + for (p = fname_copy; *p; p = CharNext (p)) + if (*p == '/') + *p = '\\'; + + if ((attributes = GetFileAttributesA (fname_copy)) == -1) + { + DWORD w32err = GetLastError (); + + switch (w32err) + { + case ERROR_INVALID_NAME: + case ERROR_BAD_PATHNAME: + case ERROR_FILE_NOT_FOUND: + case ERROR_BAD_NETPATH: + errno = ENOENT; + break; + default: + errno = EACCES; + break; + } + return -1; + } + if ((mode & X_OK) != 0 + && !(is_exec (fname_copy) + || (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { errno = EACCES; return -1; @@ -3273,20 +3871,63 @@ faccessat (int dirfd, const char * path, int mode, int flags) int sys_chdir (const char * path) { - return _chdir (map_w32_filename (path, NULL)); + path = map_w32_filename (path, NULL); + if (w32_unicode_filenames) + { + wchar_t newdir_w[MAX_PATH]; + + if (filename_to_utf16 (path, newdir_w) == 0) + return _wchdir (newdir_w); + return -1; + } + else + { + char newdir_a[MAX_PATH]; + + if (filename_to_ansi (path, newdir_a) == 0) + return _chdir (newdir_a); + return -1; + } } int 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 * @@ -3326,7 +3967,21 @@ sys_fopen (const char * path, const char * mode) } 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; @@ -3339,7 +3994,9 @@ sys_link (const char * old, const char * new) { 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) { @@ -3350,8 +4007,18 @@ sys_link (const char * old, const char * new) 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; @@ -3368,8 +4035,11 @@ sys_link (const char * old, const char * new) WCHAR wbuffer[MAX_PATH]; /* extra space for link name */ } data; - wlen = MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, newname, -1, - data.wid.cStreamName, MAX_PATH); + /* 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 = pMultiByteToWideChar (CP_UTF8, 0, newname, -1, + data.wid.cStreamName, MAX_PATH); if (wlen > 0) { LPVOID context = NULL; @@ -3392,9 +4062,40 @@ sys_link (const char * old, const char * new) } 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; + } } } @@ -3409,28 +4110,80 @@ sys_link (const char * old, const char * new) 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 +sys_open (const char * path, int oflag, int mode) +{ + const char* mpath = map_w32_filename (path, NULL); + int res = -1; + + 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; } -/* Because of long name mapping issues, we need to implement this - ourselves. Also, MSVC's _mktemp returns NULL when it can't generate - a unique name, instead of setting the input template to an empty - string. +/* Implementation of mkostemp for MS-Windows, to avoid race conditions + when using mktemp. - Standard algorithm seems to be use pid or tid with a letter on the - front (in place of the 6 X's) and cycle through the letters to find a - unique name. We extend that to allow any reasonable character as the - first of the 6 X's. */ -char * -sys_mktemp (char * template) + Standard algorithm for generating a temporary file name seems to be + use pid or tid with a letter on the front (in place of the 6 X's) + and cycle through the letters to find a unique name. We extend + that to allow any reasonable character as the first of the 6 X's, + so that the number of simultaneously used temporary files will be + greater. */ + +int +mkostemp (char * template, int flags) { char * p; - int i; + int i, fd = -1; unsigned uid = GetCurrentThreadId (); + int save_errno = errno; static char first_char[] = "abcdefghijklmnopqrstuvwyz0123456789!%-_@#"; + errno = EINVAL; if (template == NULL) - return NULL; + return -1; + p = template + strlen (template); i = 5; /* replace up to the last 5 X's with uid in decimal */ @@ -3445,38 +4198,22 @@ sys_mktemp (char * template) i = 0; do { - int save_errno = errno; p[0] = first_char[i]; - if (faccessat (AT_FDCWD, template, F_OK, AT_EACCESS) < 0) + if ((fd = sys_open (template, + flags | _O_CREAT | _O_EXCL | _O_RDWR, + S_IRUSR | S_IWUSR)) >= 0 + || errno != EEXIST) { - errno = save_errno; - return template; + if (fd >= 0) + errno = save_errno; + return fd; } } while (++i < sizeof (first_char)); } - /* Template is badly formed or else we can't generate a unique name, - so return empty string */ - template[0] = 0; - return template; -} - -int -sys_open (const char * path, int oflag, int mode) -{ - 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); - - return res; + /* Template is badly formed or else we can't generate a unique name. */ + return -1; } int @@ -3489,9 +4226,10 @@ int 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 @@ -3516,17 +4254,20 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) 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 { @@ -3534,12 +4275,13 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) 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 @@ -3558,41 +4300,81 @@ sys_rename_replace (const char *oldname, const char *newname, BOOL force) /* 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 (); + wchar_t temp_w[MAX_PATH], newname_w[MAX_PATH]; - 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) + 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); - } - else if (w32err == ERROR_PRIVILEGE_NOT_HELD - && is_symlink (temp)) + 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 + { + 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; } } @@ -3608,7 +4390,22 @@ sys_rename (char const *old, char const *new) 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 @@ -3616,9 +4413,23 @@ sys_unlink (const char * path) { 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; @@ -3686,49 +4497,6 @@ convert_from_time_t (time_t time, FILETIME * pft) pft->dwLowDateTime = tmp.LowPart; } -#if 0 -/* No reason to keep this; faking inode values either by hashing or even - using the file index from GetInformationByHandle, is not perfect and - so by default Emacs doesn't use the inode values on Windows. - Instead, we now determine file-truename correctly (except for - possible drive aliasing etc). */ - -/* Modified version of "PJW" algorithm (see the "Dragon" compiler book). */ -static unsigned -hashval (const unsigned char * str) -{ - unsigned h = 0; - while (*str) - { - h = (h << 4) + *str++; - h ^= (h >> 28); - } - return h; -} - -/* Return the hash value of the canonical pathname, excluding the - drive/UNC header, to get a hopefully unique inode number. */ -static DWORD -generate_inode_val (const char * name) -{ - char fullname[ MAX_PATH ]; - char * p; - unsigned hash; - - /* Get the truly canonical filename, if it exists. (Note: this - doesn't resolve aliasing due to subst commands, or recognize hard - links. */ - if (!w32_get_long_filename ((char *)name, fullname, MAX_PATH)) - emacs_abort (); - - parse_root (fullname, &p); - /* Normal W32 filesystems are still case insensitive. */ - _strlwr (p); - return hashval (p); -} - -#endif - static PSECURITY_DESCRIPTOR get_file_security_desc_by_handle (HANDLE h) { @@ -3785,12 +4553,6 @@ get_rid (PSID sid) /* Caching SID and account values for faster lokup. */ -#ifdef __GNUC__ -# define FLEXIBLE_ARRAY_MEMBER -#else -# define FLEXIBLE_ARRAY_MEMBER 1 -#endif - struct w32_id { unsigned rid; struct w32_id *next; @@ -3941,18 +4703,21 @@ is_slow_fs (const char *name) } /* 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; @@ -3964,7 +4729,8 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) 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) { @@ -3974,20 +4740,16 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) 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; } - /* Remove trailing directory separator, unless name is the root - directory of a drive or UNC volume in which case ensure there - is a trailing separator. */ len = strlen (name); + /* Allocate 1 extra byte so that we could append a slash to a root + directory, down below. */ name = strcpy (alloca (len + 2), name); /* Avoid a somewhat costly call to is_symlink if the filesystem @@ -4029,13 +4791,26 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) 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; @@ -4162,7 +4937,6 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) 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)); @@ -4190,19 +4964,9 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) } 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, "\\"); - } + /* Make sure root directories end in a slash. */ + if (!IS_DIRECTORY_SEP (name[len-1])) + strcat (name, "\\"); if (GetDriveType (name) < 2) { errno = ENOENT; @@ -4214,51 +4978,67 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) } 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; - } + /* Make sure non-root directories do NOT end in a slash, + otherwise FindFirstFile might fail. */ + 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; @@ -4268,12 +5048,24 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) } /* 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; @@ -4288,18 +5080,6 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) 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; @@ -4358,11 +5138,14 @@ fstatat (int fd, char const *name, struct stat *st, int flags) 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) { - if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name) + char lastc = dir_pathname[strlen (dir_pathname) - 1]; + + if (_snprintf (fullname, sizeof fullname, "%s%s%s", + dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name) < 0) { errno = ENAMETOOLONG; @@ -4486,6 +5269,9 @@ fstat (int desc, struct stat * buf) return 0; } +/* A version of 'utime' which handles directories as well as + files. */ + int utime (const char *name, struct utimbuf *times) { @@ -4500,13 +5286,32 @@ utime (const char *name, struct utimbuf *times) times = &deftime; } - /* Need write access to set times. */ - fh = CreateFile (name, FILE_WRITE_ATTRIBUTES, - /* If NAME specifies a directory, FILE_SHARE_DELETE - allows other processes to delete files inside it, - while we have the directory open. */ - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (w32_unicode_filenames) + { + wchar_t name_utf16[MAX_PATH]; + + if (filename_to_utf16 (name, name_utf16) != 0) + return -1; /* errno set by filename_to_utf16 */ + + /* Need write access to set times. */ + fh = CreateFileW (name_utf16, FILE_WRITE_ATTRIBUTES, + /* If NAME specifies a directory, FILE_SHARE_DELETE + allows other processes to delete files inside it, + while we have the directory open. */ + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } + else + { + char name_ansi[MAX_PATH]; + + if (filename_to_ansi (name, name_ansi) != 0) + return -1; /* errno set by filename_to_ansi */ + + fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + } if (fh != INVALID_HANDLE_VALUE) { convert_from_time_t (times->actime, &atime); @@ -4521,12 +5326,60 @@ utime (const char *name, struct utimbuf *times) } else { - errno = EINVAL; + DWORD err = GetLastError (); + + switch (err) + { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_BAD_NETPATH: + case ERROR_DEV_NOT_EXIST: + /* ERROR_INVALID_NAME is the error CreateFile sets when the + file name includes ?s, i.e. translation to ANSI failed. */ + case ERROR_INVALID_NAME: + errno = ENOENT; + break; + case ERROR_TOO_MANY_OPEN_FILES: + errno = ENFILE; + break; + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + errno = EACCES; + break; + default: + errno = EINVAL; + break; + } return -1; } return 0; } +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 & S_IWRITE) != 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 & ~S_IWRITE); + current_mask = mode; + + return retval; +} + /* Symlink-related functions. */ #ifndef SYMBOLIC_LINK_FLAG_DIRECTORY @@ -4536,10 +5389,9 @@ utime (const char *name, struct utimbuf *times) 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) @@ -4552,7 +5404,7 @@ symlink (char const *filename, char const *linkname) 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; @@ -4565,8 +5417,6 @@ symlink (char const *filename, char const *linkname) 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]))) @@ -4578,24 +5428,11 @@ symlink (char const *filename, char const *linkname) 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'; @@ -4610,15 +5447,7 @@ symlink (char const *filename, char const *linkname) 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; @@ -4688,10 +5517,23 @@ static int 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 (); @@ -4714,12 +5556,30 @@ is_symlink (const char *filename) 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 @@ -4736,6 +5596,7 @@ readlink (const char *name, char *buf, size_t buf_size) int restore_privs = 0; HANDLE sh; ssize_t retval; + char resolved[MAX_UTF8_PATH]; if (name == NULL) { @@ -4750,7 +5611,7 @@ readlink (const char *name, char *buf, size_t buf_size) path = map_w32_filename (name, NULL); - if (strlen (path) > MAX_PATH) + if (strlen (path) > MAX_UTF8_PATH) { errno = ENAMETOOLONG; return -1; @@ -4780,9 +5641,26 @@ readlink (const char *name, char *buf, size_t buf_size) 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]; @@ -4801,89 +5679,27 @@ readlink (const char *name, char *buf, size_t buf_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); } @@ -4922,7 +5738,7 @@ readlinkat (int fd, char const *name, char *buffer, { /* 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) { @@ -4961,41 +5777,45 @@ readlinkat (int fd, char const *name, char *buffer, 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'; @@ -5006,27 +5826,28 @@ chase_symlinks (const char *file) 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); @@ -5143,7 +5964,11 @@ acl_get_file (const char *fname, acl_type_t type) } } 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; @@ -5164,7 +5989,7 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl) DWORD err; int st = 0, retval = -1; SECURITY_INFORMATION flags = 0; - PSID psid; + PSID psidOwner, psidGroup; PACL pacl; BOOL dflt; BOOL dacl_present; @@ -5190,11 +6015,13 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl) 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) @@ -5221,10 +6048,22 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl) 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 @@ -5254,7 +6093,12 @@ acl_set_file (const char *fname, acl_type_t type, acl_t acl) 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; @@ -5287,7 +6131,7 @@ careadlinkat (int fd, char const *filename, 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)); @@ -5307,6 +6151,116 @@ careadlinkat (int fd, char const *filename, 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; +} + /* Support for browsing other processes and their attributes. See process.c for the Lisp bindings. */ @@ -5754,8 +6708,8 @@ system_process_attributes (Lisp_Object pid) { /* Decode the command name from locale-specific encoding. */ - cmd_str = make_unibyte_string (pe.szExeFile, - strlen (pe.szExeFile)); + cmd_str = build_unibyte_string (pe.szExeFile); + decoded_cmd = code_convert_string_norecord (cmd_str, Vlocale_coding_system, 0); @@ -6060,6 +7014,7 @@ term_winsock (void) { if (winsock_lib != NULL && winsock_inuse == 0) { + release_listen_threads (); /* Not sure what would cause WSAENETDOWN, or even if it can happen after WSAStartup returns successfully, but it seems reasonable to allow unloading winsock anyway in that case. */ @@ -6725,19 +7680,25 @@ sys_sendto (int s, const char * buf, int len, int flags, } /* Windows does not have an fcntl function. Provide an implementation - solely for making sockets non-blocking. */ + good enough for Emacs. */ int fcntl (int s, int cmd, int options) { - if (winsock_lib == NULL) - { - errno = ENETDOWN; - return -1; - } + /* In the w32 Emacs port, fcntl (fd, F_DUPFD_CLOEXEC, fd1) is always + invoked in a context where fd1 is closed and all descriptors less + than fd1 are open, so sys_dup is an adequate implementation. */ + if (cmd == F_DUPFD_CLOEXEC) + return sys_dup (s); check_errno (); if (fd_info[s].flags & FILE_SOCKET) { + if (winsock_lib == NULL) + { + errno = ENETDOWN; + return -1; + } + if (cmd == F_SETFL && options == O_NONBLOCK) { unsigned long nblock = 1; @@ -6754,13 +7715,36 @@ fcntl (int s, int cmd, int options) return SOCKET_ERROR; } } + else if ((fd_info[s].flags & (FILE_PIPE | FILE_WRITE)) + == (FILE_PIPE | FILE_WRITE)) + { + /* Force our writes to pipes be non-blocking. */ + if (cmd == F_SETFL && options == O_NONBLOCK) + { + HANDLE h = (HANDLE)_get_osfhandle (s); + DWORD pipe_mode = PIPE_NOWAIT; + + if (!SetNamedPipeHandleState (h, &pipe_mode, NULL, NULL)) + { + DebPrint (("SetNamedPipeHandleState: %lu\n", GetLastError ())); + return SOCKET_ERROR; + } + fd_info[s].flags |= FILE_NDELAY; + return 0; + } + else + { + errno = EINVAL; + return SOCKET_ERROR; + } + } errno = ENOTSOCK; return SOCKET_ERROR; } /* Shadow main io functions: we need to handle pipes and sockets more - intelligently, and implement non-blocking mode as well. */ + intelligently. */ int sys_close (int fd) @@ -6870,13 +7854,14 @@ sys_dup2 (int src, int dst) return rc; } -/* Unix pipe() has only one arg */ int -sys_pipe (int * phandles) +pipe2 (int * phandles, int pipe2_flags) { int rc; unsigned flags; + eassert (pipe2_flags == O_CLOEXEC); + /* make pipe handles non-inheritable; when we spawn a child, we replace the relevant handle with an inheritable one. Also put pipes into binary mode; we will do text mode translation ourselves @@ -7037,7 +8022,12 @@ _sys_wait_accept (int fd) rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_ACCEPT); if (rc != SOCKET_ERROR) { - rc = WaitForSingleObject (hEv, INFINITE); + do { + rc = WaitForSingleObject (hEv, 500); + Sleep (5); + } while (rc == WAIT_TIMEOUT + && cp->status != STATUS_READ_ERROR + && cp->char_avail); pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0); if (rc == WAIT_OBJECT_0) cp->status = STATUS_READ_SUCCEEDED; @@ -7239,7 +8229,6 @@ sys_read (int fd, char * buffer, unsigned int count) /* From w32xfns.c */ extern HANDLE interrupt_handle; -/* For now, don't bother with a non-blocking mode */ int sys_write (int fd, const void * buffer, unsigned int count) { @@ -7374,6 +8363,22 @@ sys_write (int fd, const void * buffer, unsigned int count) nchars += n; if (n < 0) { + /* When there's no buffer space in a pipe that is in the + non-blocking mode, _write returns ENOSPC. We return + EAGAIN instead, which should trigger the logic in + send_process that enters waiting loop and calls + wait_reading_process_output to allow process input to + be accepted during the wait. Those calls to + wait_reading_process_output allow sys_select to + notice when process input becomes available, thus + avoiding deadlock whereby each side of the pipe is + blocked on write, waiting for the other party to read + its end of the pipe. */ + if (errno == ENOSPC + && fd < MAXDESC + && ((fd_info[fd].flags & (FILE_PIPE | FILE_NDELAY)) + == (FILE_PIPE | FILE_NDELAY))) + errno = EAGAIN; nchars = n; break; } @@ -7387,6 +8392,270 @@ sys_write (int fd, const void * buffer, unsigned int count) return nchars; } + +/* Emulation of SIOCGIFCONF and getifaddrs, see process.c. */ + +extern Lisp_Object conv_sockaddr_to_lisp (struct sockaddr *, int); + +/* Return information about network interface IFNAME, or about all + interfaces (if IFNAME is nil). */ +static Lisp_Object +network_interface_get_info (Lisp_Object ifname) +{ + ULONG ainfo_len = sizeof (IP_ADAPTER_INFO); + IP_ADAPTER_INFO *adapter, *ainfo = xmalloc (ainfo_len); + DWORD retval = get_adapters_info (ainfo, &ainfo_len); + Lisp_Object res = Qnil; + + if (retval == ERROR_BUFFER_OVERFLOW) + { + ainfo = xrealloc (ainfo, ainfo_len); + retval = get_adapters_info (ainfo, &ainfo_len); + } + + if (retval == ERROR_SUCCESS) + { + int eth_count = 0, tr_count = 0, fddi_count = 0, ppp_count = 0; + int sl_count = 0, wlan_count = 0, lo_count = 0, ifx_count = 0; + int if_num; + struct sockaddr_in sa; + + /* For the below, we need some winsock functions, so make sure + the winsock DLL is loaded. If we cannot successfully load + it, they will have no use of the information we provide, + anyway, so punt. */ + if (!winsock_lib && !init_winsock (1)) + goto done; + + for (adapter = ainfo; adapter; adapter = adapter->Next) + { + char namebuf[MAX_ADAPTER_NAME_LENGTH + 4]; + u_long ip_addr; + /* Present Unix-compatible interface names, instead of the + Windows names, which are really GUIDs not readable by + humans. */ + static const char *ifmt[] = { + "eth%d", "tr%d", "fddi%d", "ppp%d", "sl%d", "wlan%d", + "lo", "ifx%d" + }; + enum { + NONE = -1, + ETHERNET = 0, + TOKENRING = 1, + FDDI = 2, + PPP = 3, + SLIP = 4, + WLAN = 5, + LOOPBACK = 6, + OTHER_IF = 7 + } ifmt_idx; + + switch (adapter->Type) + { + case MIB_IF_TYPE_ETHERNET: + /* Windows before Vista reports wireless adapters as + Ethernet. Work around by looking at the Description + string. */ + if (strstr (adapter->Description, "Wireless ")) + { + ifmt_idx = WLAN; + if_num = wlan_count++; + } + else + { + ifmt_idx = ETHERNET; + if_num = eth_count++; + } + break; + case MIB_IF_TYPE_TOKENRING: + ifmt_idx = TOKENRING; + if_num = tr_count++; + break; + case MIB_IF_TYPE_FDDI: + ifmt_idx = FDDI; + if_num = fddi_count++; + break; + case MIB_IF_TYPE_PPP: + ifmt_idx = PPP; + if_num = ppp_count++; + break; + case MIB_IF_TYPE_SLIP: + ifmt_idx = SLIP; + if_num = sl_count++; + break; + case IF_TYPE_IEEE80211: + ifmt_idx = WLAN; + if_num = wlan_count++; + break; + case MIB_IF_TYPE_LOOPBACK: + if (lo_count < 0) + { + ifmt_idx = LOOPBACK; + if_num = lo_count++; + } + else + ifmt_idx = NONE; + break; + default: + ifmt_idx = OTHER_IF; + if_num = ifx_count++; + break; + } + if (ifmt_idx == NONE) + continue; + sprintf (namebuf, ifmt[ifmt_idx], if_num); + + sa.sin_family = AF_INET; + ip_addr = sys_inet_addr (adapter->IpAddressList.IpAddress.String); + if (ip_addr == INADDR_NONE) + { + /* Bogus address, skip this interface. */ + continue; + } + sa.sin_addr.s_addr = ip_addr; + sa.sin_port = 0; + if (NILP (ifname)) + res = Fcons (Fcons (build_string (namebuf), + conv_sockaddr_to_lisp ((struct sockaddr*) &sa, + sizeof (struct sockaddr))), + res); + else if (strcmp (namebuf, SSDATA (ifname)) == 0) + { + Lisp_Object hwaddr = Fmake_vector (make_number (6), Qnil); + register struct Lisp_Vector *p = XVECTOR (hwaddr); + Lisp_Object flags = Qnil; + int n; + u_long net_mask; + + /* Flags. We guess most of them by type, since the + Windows flags are different and hard to get by. */ + flags = Fcons (intern ("up"), flags); + if (ifmt_idx == ETHERNET || ifmt_idx == WLAN) + { + flags = Fcons (intern ("broadcast"), flags); + flags = Fcons (intern ("multicast"), flags); + } + flags = Fcons (intern ("running"), flags); + if (ifmt_idx == PPP) + { + flags = Fcons (intern ("pointopoint"), flags); + flags = Fcons (intern ("noarp"), flags); + } + if (adapter->HaveWins) + flags = Fcons (intern ("WINS"), flags); + if (adapter->DhcpEnabled) + flags = Fcons (intern ("dynamic"), flags); + + res = Fcons (flags, res); + + /* Hardware address and its family. */ + for (n = 0; n < adapter->AddressLength; 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. */ + res = Fcons (Fcons (make_number (1), hwaddr), res); + + /* Network mask. */ + sa.sin_family = AF_INET; + net_mask = sys_inet_addr (adapter->IpAddressList.IpMask.String); + if (net_mask != INADDR_NONE) + { + sa.sin_addr.s_addr = net_mask; + sa.sin_port = 0; + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + } + else + res = Fcons (Qnil, res); + + sa.sin_family = AF_INET; + if (ip_addr != INADDR_NONE) + { + /* Broadcast address is only reported by + GetAdaptersAddresses, which is of limited + availability. Generate it on our own. */ + u_long bcast_addr = (ip_addr & net_mask) | ~net_mask; + + sa.sin_addr.s_addr = bcast_addr; + sa.sin_port = 0; + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + + /* IP address. */ + sa.sin_addr.s_addr = ip_addr; + sa.sin_port = 0; + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + } + else + res = Fcons (Qnil, Fcons (Qnil, res)); + } + } + /* GetAdaptersInfo is documented to not report loopback + interfaces, so we generate one out of thin air. */ + if (!lo_count) + { + sa.sin_family = AF_INET; + sa.sin_port = 0; + if (NILP (ifname)) + { + sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1"); + res = Fcons (Fcons (build_string ("lo"), + conv_sockaddr_to_lisp ((struct sockaddr*) &sa, + sizeof (struct sockaddr))), + res); + } + else if (strcmp (SSDATA (ifname), "lo") == 0) + { + res = Fcons (Fcons (intern ("running"), + Fcons (intern ("loopback"), + Fcons (intern ("up"), Qnil))), Qnil); + /* 772 is what 3 different GNU/Linux systems report for + the loopback interface. */ + res = Fcons (Fcons (make_number (772), + Fmake_vector (make_number (6), + make_number (0))), + res); + sa.sin_addr.s_addr = sys_inet_addr ("255.0.0.0"); + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + sa.sin_addr.s_addr = sys_inet_addr ("0.0.0.0"); + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + sa.sin_addr.s_addr = sys_inet_addr ("127.0.0.1"); + res = Fcons (conv_sockaddr_to_lisp ((struct sockaddr *) &sa, + sizeof (struct sockaddr)), + res); + } + + } + } + + done: + xfree (ainfo); + return res; +} + +Lisp_Object +network_interface_list (void) +{ + return network_interface_get_info (Qnil); +} + +Lisp_Object +network_interface_info (Lisp_Object ifname) +{ + CHECK_STRING (ifname); + return network_interface_get_info (ifname); +} + + /* The Windows CRT functions are "optimized for speed", so they don't check for timezone and DST changes if they were last called less than 1 minute ago (see http://support.microsoft.com/kb/821231). So @@ -7414,7 +8683,7 @@ sys_localtime (const time_t *t) HMODULE w32_delayed_load (Lisp_Object library_id) { - HMODULE library_dll = NULL; + HMODULE dll_handle = NULL; CHECK_SYMBOL (library_id); @@ -7427,26 +8696,56 @@ w32_delayed_load (Lisp_Object 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; } @@ -7465,8 +8764,14 @@ check_windows_init_file (void) 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); @@ -7475,6 +8780,8 @@ check_windows_init_file (void) 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\" " @@ -7486,8 +8793,27 @@ check_windows_init_file (void) "not unpacked properly.\nSee the README.W32 file in the " "top-level Emacs directory for more information.", init_file_name, load_path); + needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, + -1, NULL, 0); + if (needed > 0) + { + wchar_t *msg_w = alloca ((needed + 1) * sizeof (wchar_t)); + + pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, buffer, -1, + msg_w, needed); + needed = pWideCharToMultiByte (CP_ACP, 0, msg_w, -1, + NULL, 0, NULL, NULL); + if (needed > 0) + { + char *msg_a = alloca (needed + 1); + + pWideCharToMultiByte (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. */ @@ -7641,6 +8967,57 @@ shutdown_handler (DWORD type) return FALSE; } +/* On Windows 9X, load UNICOWS.DLL and return its handle, or die. On + NT, return a handle to GDI32.DLL. */ +HANDLE +maybe_load_unicows_dll (void) +{ + if (os_subtype == OS_9X) + { + HANDLE ret = LoadLibrary ("Unicows.dll"); + if (ret) + { + /* These two functions are present on Windows 9X as stubs + that always fail. We need the real implementations from + UNICOWS.DLL, so we must call these functions through + pointers, and assign the correct addresses to these + pointers at program startup (see emacs.c, which calls + this function early on). */ + pMultiByteToWideChar = GetProcAddress (ret, "MultiByteToWideChar"); + pWideCharToMultiByte = GetProcAddress (ret, "WideCharToMultiByte"); + return ret; + } + else + { + int button; + + button = MessageBox (NULL, + "Emacs cannot load the UNICOWS.DLL library.\n" + "This library is essential for using Emacs\n" + "on this system. You need to install it.\n\n" + "Emacs will exit when you click OK.", + "Emacs cannot load UNICOWS.DLL", + MB_ICONERROR | MB_TASKMODAL + | MB_SETFOREGROUND | MB_OK); + switch (button) + { + case IDOK: + default: + exit (1); + } + } + } + else + { + /* On NT family of Windows, these two functions are always + linked in, so we just assign their addresses to the 2 + pointers; no need for the LoadLibrary dance. */ + pMultiByteToWideChar = MultiByteToWideChar; + pWideCharToMultiByte = WideCharToMultiByte; + return LoadLibrary ("Gdi32.dll"); + } +} + /* globals_of_w32 is used to initialize those global variables that must always be initialized on startup even when the global variable @@ -7663,7 +9040,8 @@ globals_of_w32 (void) 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; @@ -7682,12 +9060,17 @@ globals_of_w32 (void) 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 console apps. This actually applies to Emacs in both console and @@ -7700,12 +9083,21 @@ globals_of_w32 (void) /* Reset, in case it has some value inherited from dump time. */ w32_stat_get_owner_group = 0; + + /* If w32_unicode_filenames is non-zero, we will be using Unicode + (a.k.a. "wide") APIs to invoke functions that accept file + names. */ + if (is_windows_9x ()) + w32_unicode_filenames = 0; + else + w32_unicode_filenames = 1; } /* For make-serial-process */ int -serial_open (char *port) +serial_open (Lisp_Object port_obj) { + char *port = SSDATA (port_obj); HANDLE hnd; child_process *cp; int fd = -1; @@ -7899,7 +9291,7 @@ emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz) { int n, err; SELECT_TYPE fdset; - EMACS_TIME timeout; + struct timespec timeout; struct Lisp_Process *process = (struct Lisp_Process *)p; int fd = process->infd;