X-Git-Url: http://git.hcoop.net/bpt/emacs.git/blobdiff_plain/7d9fb4de782c3ff316c6ffc26f6ea291be2f653b..6349f70a31a56fbaf8b5dbcaaddf624d137f9d63:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 7d63c73eb1..46f36de8b9 100644 --- a/src/w32.c +++ b/src/w32.c @@ -19,6 +19,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 +49,6 @@ along with GNU Emacs. If not, see . */ #undef fopen #undef link #undef mkdir -#undef mktemp #undef open #undef rename #undef rmdir @@ -90,6 +91,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 @@ -202,6 +218,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 +248,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); + @@ -278,6 +298,7 @@ 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_get_adapters_info; /* BEGIN: Wrapper functions around OpenProcessToken @@ -420,6 +441,9 @@ 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); /* ** A utility function ** */ static BOOL @@ -1112,6 +1136,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 +1182,193 @@ w32_valid_pointer_p (void *p, int size) return -1; } -static char startup_dir[MAXPATHLEN]; + + +/* 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; +} + +static int +filename_to_utf16 (const char *fn_in, wchar_t *fn_out) +{ + int result = MultiByteToWideChar (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; +} + +static int +filename_from_utf16 (const wchar_t *fn_in, char *fn_out) +{ + int result = WideCharToMultiByte (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; +} + +static 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 = WideCharToMultiByte (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 = MultiByteToWideChar (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 * @@ -1328,8 +1560,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 +1742,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 +1787,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; + 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 */ - - 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 +1820,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++; } - - 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 +1872,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 +1890,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 +1913,44 @@ 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; + char fname_utf8[MAX_UTF8_PATH]; int len = 0; + int cstatus; - /* 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); - else - len = 0; - FindClose (dir_handle); + 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); + 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 +1960,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 +1973,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 +1982,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 +2006,29 @@ 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); + } +} + static int is_unc_volume (const char *filename) { @@ -1851,7 +2037,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 +2137,6 @@ w32_get_resource (char *key, LPDWORD lpdwtype) return (NULL); } -char *get_emacs_configuration (void); - void init_environment (char ** argv) { @@ -1964,6 +2148,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 +2170,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 +2233,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 @@ -2118,33 +2309,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 +2344,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,8 +2424,22 @@ 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]; @@ -2284,175 +2462,23 @@ 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). */ int -gettimeofday (struct timeval *restrict tv, struct timezone *restrict tz) +gettimeofday (struct timeval *__restrict tv, struct timezone *__restrict tz) { struct _timeb tb; _ftime (&tb); @@ -2487,8 +2513,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 +2523,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 +2628,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 +2641,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 +2703,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 +2759,7 @@ GetCachedVolumeInformation (char * root_dir) } info->name = xstrdup (name); + unixtodos_filename (info->name); info->serialnum = serialnum; info->maxcomp = maxcomp; info->flags = flags; @@ -2697,53 +2782,23 @@ 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] == ':') - { - 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])) + /* Copy the root name of the volume, if given. */ + if (root_len) { - char *str = temp; - int slashes = 4; - int dbcs_p = max_filename_mbslen () > 1; - + strncpy (temp, name, root_len); + temp[root_len] = '\0'; + unixtodos_filename (temp); 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 +2820,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 +2899,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 +2934,28 @@ 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; /* 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 +2984,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 +2995,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 +3012,59 @@ 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, "\\"); + strcat (filename, "*"); + + /* Note: No need to resolve symlinks in FILENAME, because + FindFirst opens the directory that is the target of a + symlink. */ + if (w32_unicode_filenames) { - if (!IS_DIRECTORY_SEP (filename[ln])) - strcat (filename, "\\"); + wchar_t fnw[MAX_PATH]; + + filename_to_utf16 (filename, fnw); + dir_find_handle = FindFirstFileW (fnw, &dir_find_data_w); } else { - char *end = filename + ln + 1; - char *last_char = CharPrevExA (file_name_codepage, filename, end, 0); + char fna[MAX_PATH]; - if (!IS_DIRECTORY_SEP (*last_char)) - strcat (filename, "\\"); + filename_to_ansi (filename, fna); + dir_find_handle = FindFirstFileA (fna, &dir_find_data_a); } - 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 (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 +3072,153 @@ 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); } 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); } + dir_static.d_namlen = strlen (dir_static.d_name); + dir_static.d_reclen = sizeof (struct dirent) - MAX_UTF8_PATH + 3 + + dir_static.d_namlen - dir_static.d_namlen % 4; + return &dir_static; } static HANDLE open_unc_volume (const char *path) { - NETRESOURCE nr; + const char *fn = map_w32_filename (path, NULL); + DWORD result; HANDLE henum; - int result; - nr.dwScope = RESOURCE_GLOBALNET; - nr.dwType = RESOURCETYPE_DISK; - nr.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; - nr.dwUsage = RESOURCEUSAGE_CONTAINER; - nr.lpLocalName = NULL; - nr.lpRemoteName = (LPSTR)map_w32_filename (path, NULL); - nr.lpComment = NULL; - nr.lpProvider = NULL; + if (w32_unicode_filenames) + { + NETRESOURCEW nrw; + wchar_t fnw[MAX_PATH]; + + nrw.dwScope = RESOURCE_GLOBALNET; + nrw.dwType = RESOURCETYPE_DISK; + nrw.dwDisplayType = RESOURCEDISPLAYTYPE_SERVER; + nrw.dwUsage = RESOURCEUSAGE_CONTAINER; + nrw.lpLocalName = NULL; + filename_to_utf16 (fn, fnw); + nrw.lpRemoteName = fnw; + nrw.lpComment = NULL; + nrw.lpProvider = NULL; - result = WNetOpenEnum (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, - RESOURCEUSAGE_CONNECTABLE, &nr, &henum); + 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 +3249,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 +3274,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; + + 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; - WNetAddConnection2 (&resource, NULL, NULL, CONNECT_INTERACTIVE); + 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 +3365,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 (); @@ -3265,6 +3428,62 @@ faccessat (int dirfd, const char * path, int mode, int flags) 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; + } + 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; +} + /* Shadow some MSVC runtime functions to map requests for long filenames to reasonable short names if necessary. This was originally added to permit running Emacs on NT 3.1 on a FAT partition, which doesn't support @@ -3273,20 +3492,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 +3588,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 +3615,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 +3628,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,7 +3656,10 @@ 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, + /* We used to pass MB_PRECOMPOSED as the 2nd arg here, but MSDN + indicates that flag is unsupported for CP_UTF8, and OTOH says + it is the default anyway. */ + wlen = MultiByteToWideChar (CP_UTF8, 0, newname, -1, data.wid.cStreamName, MAX_PATH); if (wlen > 0) { @@ -3392,9 +3683,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; + } } } @@ -3412,25 +3734,46 @@ sys_mkdir (const char * path) return _mkdir (map_w32_filename (path, NULL)); } -/* 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. +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; +} + +/* 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 +3788,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 @@ -3686,49 +4013,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 +4069,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; @@ -4252,7 +4530,7 @@ stat_worker (const char * path, struct stat * buf, int follow_symlinks) && xstrcasecmp (name + len + 1, dir_static.d_name) == 0) { /* This was the last entry returned by readdir. */ - wfd = dir_find_data; + wfd = dir_find_data_a; /* FIXME!!! */ } else { @@ -4486,6 +4764,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 +4781,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,7 +4821,31 @@ 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; @@ -5754,8 +6078,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 +6384,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,10 +7050,16 @@ 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) { + /* 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); + if (winsock_lib == NULL) { errno = ENETDOWN; @@ -6870,13 +7201,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 +7369,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; @@ -7387,6 +7724,269 @@ 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->u.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) +{ + 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 @@ -7688,6 +8288,7 @@ globals_of_w32 (void) 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_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 +8301,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 +8509,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;