X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/220d91b834f7f7252b9953460422151b86b3520c..b6bd159922608fa474026837771d63bf7eadcf97:/src/w32.c diff --git a/src/w32.c b/src/w32.c index ae34ac6aad..91893ddfc6 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,6 +1,5 @@ /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API. - Copyright (C) 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010 Free Software Foundation, Inc. + Copyright (C) 1994-1995, 2000-2011 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -35,12 +34,11 @@ along with GNU Emacs. If not, see . */ #include /* for _mbspbrk */ #include #include +#include /* must include CRT headers *before* config.h */ -#ifdef HAVE_CONFIG_H #include -#endif #undef access #undef chdir @@ -65,6 +63,8 @@ along with GNU Emacs. If not, see . */ #undef strerror +#undef localtime + #include "lisp.h" #include @@ -95,9 +95,9 @@ typedef struct _MEMORY_STATUS_EX { #include #include #include -#if !defined(__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15) +#if !defined (__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15) /* This either is not in psapi.h or guarded by higher value of - _WIN32_WINNT than what we use. w32api suplied with MinGW 3.15 + _WIN32_WINNT than what we use. w32api supplied with MinGW 3.15 defines it in psapi.h */ typedef struct _PROCESS_MEMORY_COUNTERS_EX { DWORD cb; @@ -141,23 +141,20 @@ typedef struct _PROCESS_MEMORY_COUNTERS_EX { #include "dispextern.h" /* for xstrcasecmp */ #include "coding.h" /* for Vlocale_coding_system */ +#include "careadlinkat.h" +#include "allocator.h" + /* For serial_configure and serial_open. */ #include "process.h" typedef HRESULT (WINAPI * ShGetFolderPath_fn) (IN HWND, IN int, IN HANDLE, IN DWORD, OUT char *); +Lisp_Object QCloaded_from; + void globals_of_w32 (void); static DWORD get_rid (PSID); -extern Lisp_Object Vw32_downcase_file_names; -extern Lisp_Object Vw32_generate_fake_inodes; -extern Lisp_Object Vw32_get_true_file_attributes; -/* Defined in process.c for its own purpose. */ -extern Lisp_Object Qlocal; - -extern int w32_num_mouse_buttons; - /* Initialization states. @@ -1454,6 +1451,14 @@ sigprocmask (int how, const sigset_t *set, sigset_t *oset) return 0; } +int +pthread_sigmask (int how, const sigset_t *set, sigset_t *oset) +{ + if (sigprocmask (how, set, oset) == -1) + return EINVAL; + return 0; +} + int setpgrp (int pid, int gid) { @@ -1516,7 +1521,6 @@ w32_get_resource (char *key, LPDWORD lpdwtype) } char *get_emacs_configuration (void); -extern Lisp_Object Vsystem_configuration; void init_environment (char ** argv) @@ -1567,6 +1571,7 @@ init_environment (char ** argv) char locale_name[32]; struct stat ignored; char default_home[MAX_PATH]; + int appdata = 0; static const struct env_entry { @@ -1604,25 +1609,28 @@ init_environment (char ** argv) 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 (stat ("C:/.emacs", &ignored) < 0) - { - HRESULT profile_result; - /* Dynamically load ShGetFolderPath, as it won't exist on versions - of Windows 95 and NT4 that have not been updated to include - MSIE 5. */ - ShGetFolderPath_fn get_folder_path; - get_folder_path = (ShGetFolderPath_fn) - GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA"); - - if (get_folder_path != NULL) - { - profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL, - 0, default_home); + { + HRESULT profile_result; + /* Dynamically load ShGetFolderPath, as it won't exist on versions + of Windows 95 and NT4 that have not been updated to include + MSIE 5. */ + ShGetFolderPath_fn get_folder_path; + get_folder_path = (ShGetFolderPath_fn) + GetProcAddress (GetModuleHandle ("shell32.dll"), "SHGetFolderPathA"); + + if (get_folder_path != NULL) + { + profile_result = get_folder_path (NULL, CSIDL_APPDATA, NULL, + 0, default_home); - /* If we can't get the appdata dir, revert to old behavior. */ - if (profile_result == S_OK) - env_vars[0].def_value = default_home; - } - } + /* If we can't get the appdata dir, revert to old behavior. */ + if (profile_result == S_OK) + { + env_vars[0].def_value = default_home; + appdata = 1; + } + } + } /* Get default locale info and use it for LANG. */ if (GetLocaleInfo (LOCALE_USER_DEFAULT, @@ -1707,6 +1715,14 @@ init_environment (char ** argv) lpval = env_vars[i].def_value; dwType = REG_EXPAND_SZ; dont_free = 1; + if (!strcmp (env_vars[i].name, "HOME") && !appdata) + { + Lisp_Object warning[2]; + warning[0] = intern ("initialization"); + warning[1] = build_string ("Setting HOME to C:\\ by default is deprecated"); + Vdelayed_warnings_list = Fcons (Flist (2, warning), + Vdelayed_warnings_list); + } } if (lpval) @@ -1951,6 +1967,12 @@ gettimeofday (struct timeval *tv, struct timezone *tz) tv->tv_sec = tb.time; tv->tv_usec = tb.millitm * 1000L; + /* Implementation note: _ftime sometimes doesn't update the dstflag + according to the new timezone when the system timezone is + changed. We could fix that by using GetSystemTime and + GetTimeZoneInformation, but that doesn't seem necessary, since + Emacs always calls gettimeofday with the 2nd argument NULL (see + EMACS_GET_TIME). */ if (tz) { tz->tz_minuteswest = tb.timezone; /* minutes west of Greenwich */ @@ -2095,42 +2117,42 @@ GetCachedVolumeInformation (char * root_dir) info = lookup_volume_info (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; + { + 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; - /* Cache the volume information for future use, overwriting existing - entry if present. */ - if (info == NULL) - { - info = (volume_info_data *) xmalloc (sizeof (volume_info_data)); - add_volume_info (root_dir, info); - } - else - { - xfree (info->name); - xfree (info->type); - } + /* Cache the volume information for future use, overwriting existing + entry if present. */ + if (info == NULL) + { + info = (volume_info_data *) xmalloc (sizeof (volume_info_data)); + add_volume_info (root_dir, info); + } + else + { + xfree (info->name); + xfree (info->type); + } - info->name = xstrdup (name); - info->serialnum = serialnum; - info->maxcomp = maxcomp; - info->flags = flags; - info->type = xstrdup (type); - info->timestamp = GetTickCount (); - } + info->name = xstrdup (name); + info->serialnum = serialnum; + info->maxcomp = maxcomp; + info->flags = flags; + info->type = xstrdup (type); + info->timestamp = GetTickCount (); + } return info; } @@ -2389,8 +2411,8 @@ readdir (DIR *dirp) if (wnet_enum_handle != INVALID_HANDLE_VALUE) { if (!read_unc_volume (wnet_enum_handle, - dir_find_data.cFileName, - MAX_PATH)) + dir_find_data.cFileName, + MAX_PATH)) return NULL; } /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */ @@ -2500,7 +2522,7 @@ read_unc_volume (HANDLE henum, char *readbuf, int size) count = 1; buffer = alloca (bufsize); - result = WNetEnumResource (wnet_enum_handle, &count, buffer, &bufsize); + result = WNetEnumResource (henum, &count, buffer, &bufsize); if (result != NO_ERROR) return NULL; @@ -3263,8 +3285,6 @@ int stat (const char * path, struct stat * buf) { char *name, *r; - char drive_root[4]; - UINT devtype; WIN32_FIND_DATA wfd; HANDLE fh; unsigned __int64 fake_inode; @@ -3623,6 +3643,43 @@ utime (const char *name, struct utimbuf *times) return 0; } + +/* Symlink-related functions that always fail. Used in fileio.c and in + sysdep.c to avoid #ifdef's. */ +int +symlink (char const *dummy1, char const *dummy2) +{ + errno = ENOSYS; + return -1; +} + +ssize_t +readlink (const char *name, char *dummy1, size_t dummy2) +{ + /* `access' is much faster than `stat' on MS-Windows. */ + if (sys_access (name, 0) == 0) + errno = EINVAL; + return -1; +} + +char * +careadlinkat (int fd, char const *filename, + char *buffer, size_t buffer_size, + struct allocator const *alloc, + ssize_t (*preadlinkat) (int, char const *, char *, size_t)) +{ + errno = ENOSYS; + return NULL; +} + +ssize_t +careadlinkatcwd (int fd, char const *filename, char *buffer, + size_t buffer_size) +{ + (void) fd; + return readlink (filename, buffer, buffer_size); +} + /* Support for browsing other processes and their attributes. See process.c for the Lisp bindings. */ @@ -4037,7 +4094,6 @@ system_process_attributes (Lisp_Object pid) TOKEN_PRIMARY_GROUP group_token; unsigned euid; unsigned egid; - DWORD sess; PROCESS_MEMORY_COUNTERS mem; PROCESS_MEMORY_COUNTERS_EX mem_ex; DWORD minrss, maxrss; @@ -4514,75 +4570,75 @@ struct { int errnum; char * msg; } _wsa_errlist[] = { - WSAEINTR , "Interrupted function call", - WSAEBADF , "Bad file descriptor", - WSAEACCES , "Permission denied", - WSAEFAULT , "Bad address", - WSAEINVAL , "Invalid argument", - WSAEMFILE , "Too many open files", - - WSAEWOULDBLOCK , "Resource temporarily unavailable", - WSAEINPROGRESS , "Operation now in progress", - WSAEALREADY , "Operation already in progress", - WSAENOTSOCK , "Socket operation on non-socket", - WSAEDESTADDRREQ , "Destination address required", - WSAEMSGSIZE , "Message too long", - WSAEPROTOTYPE , "Protocol wrong type for socket", - WSAENOPROTOOPT , "Bad protocol option", - WSAEPROTONOSUPPORT , "Protocol not supported", - WSAESOCKTNOSUPPORT , "Socket type not supported", - WSAEOPNOTSUPP , "Operation not supported", - WSAEPFNOSUPPORT , "Protocol family not supported", - WSAEAFNOSUPPORT , "Address family not supported by protocol family", - WSAEADDRINUSE , "Address already in use", - WSAEADDRNOTAVAIL , "Cannot assign requested address", - WSAENETDOWN , "Network is down", - WSAENETUNREACH , "Network is unreachable", - WSAENETRESET , "Network dropped connection on reset", - WSAECONNABORTED , "Software caused connection abort", - WSAECONNRESET , "Connection reset by peer", - WSAENOBUFS , "No buffer space available", - WSAEISCONN , "Socket is already connected", - WSAENOTCONN , "Socket is not connected", - WSAESHUTDOWN , "Cannot send after socket shutdown", - WSAETOOMANYREFS , "Too many references", /* not sure */ - WSAETIMEDOUT , "Connection timed out", - WSAECONNREFUSED , "Connection refused", - WSAELOOP , "Network loop", /* not sure */ - WSAENAMETOOLONG , "Name is too long", - WSAEHOSTDOWN , "Host is down", - WSAEHOSTUNREACH , "No route to host", - WSAENOTEMPTY , "Buffer not empty", /* not sure */ - WSAEPROCLIM , "Too many processes", - WSAEUSERS , "Too many users", /* not sure */ - WSAEDQUOT , "Double quote in host name", /* really not sure */ - WSAESTALE , "Data is stale", /* not sure */ - WSAEREMOTE , "Remote error", /* not sure */ - - WSASYSNOTREADY , "Network subsystem is unavailable", - WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range", - WSANOTINITIALISED , "Winsock not initialized successfully", - WSAEDISCON , "Graceful shutdown in progress", + {WSAEINTR , "Interrupted function call"}, + {WSAEBADF , "Bad file descriptor"}, + {WSAEACCES , "Permission denied"}, + {WSAEFAULT , "Bad address"}, + {WSAEINVAL , "Invalid argument"}, + {WSAEMFILE , "Too many open files"}, + + {WSAEWOULDBLOCK , "Resource temporarily unavailable"}, + {WSAEINPROGRESS , "Operation now in progress"}, + {WSAEALREADY , "Operation already in progress"}, + {WSAENOTSOCK , "Socket operation on non-socket"}, + {WSAEDESTADDRREQ , "Destination address required"}, + {WSAEMSGSIZE , "Message too long"}, + {WSAEPROTOTYPE , "Protocol wrong type for socket"}, + {WSAENOPROTOOPT , "Bad protocol option"}, + {WSAEPROTONOSUPPORT , "Protocol not supported"}, + {WSAESOCKTNOSUPPORT , "Socket type not supported"}, + {WSAEOPNOTSUPP , "Operation not supported"}, + {WSAEPFNOSUPPORT , "Protocol family not supported"}, + {WSAEAFNOSUPPORT , "Address family not supported by protocol family"}, + {WSAEADDRINUSE , "Address already in use"}, + {WSAEADDRNOTAVAIL , "Cannot assign requested address"}, + {WSAENETDOWN , "Network is down"}, + {WSAENETUNREACH , "Network is unreachable"}, + {WSAENETRESET , "Network dropped connection on reset"}, + {WSAECONNABORTED , "Software caused connection abort"}, + {WSAECONNRESET , "Connection reset by peer"}, + {WSAENOBUFS , "No buffer space available"}, + {WSAEISCONN , "Socket is already connected"}, + {WSAENOTCONN , "Socket is not connected"}, + {WSAESHUTDOWN , "Cannot send after socket shutdown"}, + {WSAETOOMANYREFS , "Too many references"}, /* not sure */ + {WSAETIMEDOUT , "Connection timed out"}, + {WSAECONNREFUSED , "Connection refused"}, + {WSAELOOP , "Network loop"}, /* not sure */ + {WSAENAMETOOLONG , "Name is too long"}, + {WSAEHOSTDOWN , "Host is down"}, + {WSAEHOSTUNREACH , "No route to host"}, + {WSAENOTEMPTY , "Buffer not empty"}, /* not sure */ + {WSAEPROCLIM , "Too many processes"}, + {WSAEUSERS , "Too many users"}, /* not sure */ + {WSAEDQUOT , "Double quote in host name"}, /* really not sure */ + {WSAESTALE , "Data is stale"}, /* not sure */ + {WSAEREMOTE , "Remote error"}, /* not sure */ + + {WSASYSNOTREADY , "Network subsystem is unavailable"}, + {WSAVERNOTSUPPORTED , "WINSOCK.DLL version out of range"}, + {WSANOTINITIALISED , "Winsock not initialized successfully"}, + {WSAEDISCON , "Graceful shutdown in progress"}, #ifdef WSAENOMORE - WSAENOMORE , "No more operations allowed", /* not sure */ - WSAECANCELLED , "Operation cancelled", /* not sure */ - WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider", - WSAEINVALIDPROVIDER , "Invalid service provider version number", - WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider", - WSASYSCALLFAILURE , "System call failure", - WSASERVICE_NOT_FOUND , "Service not found", /* not sure */ - WSATYPE_NOT_FOUND , "Class type not found", - WSA_E_NO_MORE , "No more resources available", /* really not sure */ - WSA_E_CANCELLED , "Operation already cancelled", /* really not sure */ - WSAEREFUSED , "Operation refused", /* not sure */ + {WSAENOMORE , "No more operations allowed"}, /* not sure */ + {WSAECANCELLED , "Operation cancelled"}, /* not sure */ + {WSAEINVALIDPROCTABLE , "Invalid procedure table from service provider"}, + {WSAEINVALIDPROVIDER , "Invalid service provider version number"}, + {WSAEPROVIDERFAILEDINIT , "Unable to initialize a service provider"}, + {WSASYSCALLFAILURE , "System call failure"}, + {WSASERVICE_NOT_FOUND , "Service not found"}, /* not sure */ + {WSATYPE_NOT_FOUND , "Class type not found"}, + {WSA_E_NO_MORE , "No more resources available"}, /* really not sure */ + {WSA_E_CANCELLED , "Operation already cancelled"}, /* really not sure */ + {WSAEREFUSED , "Operation refused"}, /* not sure */ #endif - WSAHOST_NOT_FOUND , "Host not found", - WSATRY_AGAIN , "Authoritative host not found during name lookup", - WSANO_RECOVERY , "Non-recoverable error during name lookup", - WSANO_DATA , "Valid name, no data record of requested type", + {WSAHOST_NOT_FOUND , "Host not found"}, + {WSATRY_AGAIN , "Authoritative host not found during name lookup"}, + {WSANO_RECOVERY , "Non-recoverable error during name lookup"}, + {WSANO_DATA , "Valid name, no data record of requested type"}, - -1, NULL + {-1, NULL} }; char * @@ -5186,9 +5242,6 @@ sys_pipe (int * phandles) return rc; } -/* From ntproc.c */ -extern int w32_pipe_read_delay; - /* Function to do blocking read of one byte, needed to implement select. It is only allowed on sockets and pipes. */ int @@ -5405,7 +5458,6 @@ sys_read (int fd, char * buffer, unsigned int count) { HANDLE hnd = fd_info[fd].hnd; OVERLAPPED *ovl = &fd_info[fd].cp->ovl_read; - DWORD err = 0; int rc = 0; COMMTIMEOUTS ct; @@ -5657,18 +5709,76 @@ sys_write (int fd, const void * buffer, unsigned int count) return nchars; } +/* 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 + all Emacs features that repeatedly call time functions (e.g., + display-time) are in real danger of missing timezone and DST + changes. Calling tzset before each localtime call fixes that. */ +struct tm * +sys_localtime (const time_t *t) +{ + tzset (); + return localtime (t); +} + + + +/* Delayed loading of libraries. */ + +Lisp_Object Vlibrary_cache; + +/* The argument LIBRARIES is an alist that associates a symbol + LIBRARY_ID, identifying an external DLL library known to Emacs, to + a list of filenames under which the library is usually found. In + most cases, the argument passed as LIBRARIES is the variable + `dynamic-library-alist', which is initialized to a list of common + library names. If the function loads the library successfully, it + returns the handle of the DLL, and records the filename in the + property :loaded-from of LIBRARY_ID; it returns NULL if the library + could not be found, or when it was already loaded (because the + handle is not recorded anywhere, and so is lost after use). It + would be trivial to save the handle too in :loaded-from, but + currently there's no use case for it. */ +HMODULE +w32_delayed_load (Lisp_Object libraries, Lisp_Object library_id) +{ + HMODULE library_dll = NULL; + + CHECK_SYMBOL (library_id); + + if (CONSP (libraries) && NILP (Fassq (library_id, Vlibrary_cache))) + { + Lisp_Object found = Qnil; + Lisp_Object dlls = Fassq (library_id, libraries); + + if (CONSP (dlls)) + for (dlls = XCDR (dlls); CONSP (dlls); dlls = XCDR (dlls)) + { + CHECK_STRING_CAR (dlls); + if (library_dll = LoadLibrary (SDATA (XCAR (dlls)))) + { + found = XCAR (dlls); + break; + } + } + + Fput (library_id, QCloaded_from, found); + } + + return library_dll; +} + + static void check_windows_init_file (void) { - extern int noninteractive, inhibit_window_system; - /* A common indication that Emacs is not installed properly is when it cannot find the Windows installation file. If this file does not exist in the expected place, tell the user. */ if (!noninteractive && !inhibit_window_system) { - extern Lisp_Object Vwindow_system, Vload_path, Qfile_exists_p; Lisp_Object objs[2]; Lisp_Object full_load_path; Lisp_Object init_file; @@ -5858,6 +5968,11 @@ globals_of_w32 (void) get_process_times_fn = (GetProcessTimes_Proc) GetProcAddress (kernel32, "GetProcessTimes"); + DEFSYM (QCloaded_from, ":loaded-from"); + + Vlibrary_cache = Qnil; + staticpro (&Vlibrary_cache); + g_b_init_is_windows_9x = 0; g_b_init_open_process_token = 0; g_b_init_get_token_information = 0; @@ -6084,7 +6199,72 @@ serial_configure (struct Lisp_Process *p, Lisp_Object contact) p->childp = childp2; } -/* end of w32.c */ +#ifdef HAVE_GNUTLS + +ssize_t +emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz) +{ + int n, sc, err; + SELECT_TYPE fdset; + EMACS_TIME timeout; + struct Lisp_Process *process = (struct Lisp_Process *)p; + int fd = process->infd; + + for (;;) + { + n = sys_read (fd, (char*)buf, sz); + + if (n >= 0) + return n; + + err = errno; + + if (err == EWOULDBLOCK) + { + /* Set a small timeout. */ + EMACS_SET_SECS_USECS (timeout, 1, 0); + FD_ZERO (&fdset); + FD_SET ((int)fd, &fdset); + + /* Use select with the timeout to poll the selector. */ + sc = select (fd + 1, &fdset, (SELECT_TYPE *)0, (SELECT_TYPE *)0, + &timeout); + + if (sc > 0) + continue; /* Try again. */ + + /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. + Also accept select return 0 as an indicator to EAGAIN. */ + if (sc == 0 || errno == EWOULDBLOCK) + err = EAGAIN; + else + err = errno; /* Other errors are just passed on. */ + } + + emacs_gnutls_transport_set_errno (process->gnutls_state, err); -/* arch-tag: 90442dd3-37be-482b-b272-ac752e3049f1 - (do not change this comment) */ + return -1; + } +} + +ssize_t +emacs_gnutls_push (gnutls_transport_ptr_t p, const void* buf, size_t sz) +{ + struct Lisp_Process *process = (struct Lisp_Process *)p; + int fd = process->outfd; + ssize_t n = sys_write (fd, buf, sz); + + /* 0 or more bytes written means everything went fine. */ + if (n >= 0) + return n; + + /* Negative bytes written means we got an error in errno. + Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */ + emacs_gnutls_transport_set_errno (process->gnutls_state, + errno == EWOULDBLOCK ? EAGAIN : errno); + + return -1; +} +#endif /* HAVE_GNUTLS */ + +/* end of w32.c */