X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/aa7b87b0b22bac620f917bf8678a9cf26078897f..75728599a0bc6903eaa538c049d96a178ba85150:/src/w32.c diff --git a/src/w32.c b/src/w32.c index 865e2b0022..d00aa7f1dc 100644 --- a/src/w32.c +++ b/src/w32.c @@ -1,5 +1,5 @@ /* Utility and Unix shadow routines for GNU Emacs on the Microsoft W32 API. - Copyright (C) 1994, 1995 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 2000, 2001 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -35,7 +35,11 @@ Boston, MA 02111-1307, USA. #include /* must include CRT headers *before* config.h */ -#include "config.h" + +#ifdef HAVE_CONFIG_H +#include +#endif + #undef access #undef chdir #undef chmod @@ -57,10 +61,16 @@ Boston, MA 02111-1307, USA. #undef read #undef write +#undef strerror + #include "lisp.h" #include +#ifdef __GNUC__ +#define _ANONYMOUS_UNION +#define _ANONYMOUS_STRUCT +#endif #include #ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */ @@ -74,21 +84,44 @@ Boston, MA 02111-1307, USA. #undef gethostname #undef gethostbyname #undef getservbyname +#undef getpeername #undef shutdown +#undef setsockopt +#undef listen +#undef getsockname +#undef accept +#undef recvfrom +#undef sendto #endif #include "w32.h" #include "ndir.h" #include "w32heap.h" - -#undef min -#undef max -#define min(x, y) (((x) < (y)) ? (x) : (y)) -#define max(x, y) (((x) > (y)) ? (x) : (y)) +#include "systime.h" extern Lisp_Object Vw32_downcase_file_names; extern Lisp_Object Vw32_generate_fake_inodes; extern Lisp_Object Vw32_get_true_file_attributes; +extern Lisp_Object Vw32_num_mouse_buttons; + + +/* Equivalent of strerror for W32 error codes. */ +char * +w32_strerror (int error_no) +{ + static char buf[500]; + + if (error_no == 0) + error_no = GetLastError (); + + buf[0] = '\0'; + if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, + error_no, + 0, /* choose most suitable language */ + buf, sizeof (buf), NULL)) + sprintf (buf, "w32 error %u", error_no); + return buf; +} static char startup_dir[MAXPATHLEN]; @@ -286,9 +319,9 @@ init_user_info () /* Ensure HOME and SHELL are defined. */ if (getenv ("HOME") == NULL) - putenv ("HOME=c:/"); + abort (); if (getenv ("SHELL") == NULL) - putenv (os_subtype == OS_WIN95 ? "SHELL=command" : "SHELL=cmd"); + abort (); /* Set dir and shell from environment variables. */ strcpy (the_passwd.pw_dir, getenv ("HOME")); @@ -465,7 +498,7 @@ get_long_basename (char * name, char * buf, int size) HANDLE dir_handle; int len = 0; - /* must be valid filename, no wild cards or other illegal characters */ + /* must be valid filename, no wild cards or other invalid characters */ if (strpbrk (name, "*?|<>\"")) return 0; @@ -555,12 +588,24 @@ sigsetmask (int signal_mask) return 0; } +int +sigmask (int sig) +{ + return 0; +} + int sigblock (int sig) { return 0; } +int +sigunblock (int sig) +{ + return 0; +} + int setpgrp (int pid, int gid) { @@ -641,7 +686,6 @@ extern Lisp_Object Vsystem_configuration; void init_environment (char ** argv) { - int len; static const char * const tempdirs[] = { "$TMPDIR", "$TEMP", "$TMP", "c:/" }; @@ -662,11 +706,11 @@ init_environment (char ** argv) read-only filesystem, like CD-ROM or a write-protected floppy. 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. */ - if (tmp && access (tmp, D_OK) == 0) + if (tmp && _access (tmp, D_OK) == 0) { char * var = alloca (strlen (tmp) + 8); sprintf (var, "TMPDIR=%s", tmp); - putenv (var); + _putenv (strdup (var)); break; } } @@ -677,52 +721,113 @@ init_environment (char ** argv) Qnil)), "While setting TMPDIR: "); - /* Check for environment variables and use registry if they don't exist */ + /* Check for environment variables and use registry settings if they + don't exist. Fallback on default values where applicable. */ { int i; LPBYTE lpval; DWORD dwType; - - static char * env_vars[] = - { - "HOME", - "PRELOAD_WINSOCK", - "emacs_dir", - "EMACSLOADPATH", - "SHELL", - "CMDPROXY", - "EMACSDATA", - "EMACSPATH", - "EMACSLOCKDIR", + char locale_name[32]; + + static struct env_entry + { + char * name; + char * def_value; + } env_vars[] = + { + {"HOME", "C:/"}, + {"PRELOAD_WINSOCK", NULL}, + {"emacs_dir", "C:/emacs"}, + {"EMACSLOADPATH", "%emacs_dir%/site-lisp;%emacs_dir%/../site-lisp;%emacs_dir%/lisp;%emacs_dir%/leim"}, + {"SHELL", "%emacs_dir%/bin/cmdproxy.exe"}, + {"EMACSDATA", "%emacs_dir%/etc"}, + {"EMACSPATH", "%emacs_dir%/bin"}, + {"EMACSLOCKDIR", "%emacs_dir%/lock"}, /* We no longer set INFOPATH because Info-default-directory-list - is then ignored. We use a hook in winnt.el instead. */ - /* "INFOPATH", */ - "EMACSDOC", - "TERM", + is then ignored. */ + /* {"INFOPATH", "%emacs_dir%/info"}, */ + {"EMACSDOC", "%emacs_dir%/etc"}, + {"TERM", "cmd"}, + {"LANG", NULL}, }; - for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) + /* Get default locale info and use it for LANG. */ + if (GetLocaleInfo (LOCALE_USER_DEFAULT, + LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP, + locale_name, sizeof (locale_name))) + { + for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) + { + if (strcmp (env_vars[i].name, "LANG") == 0) + { + env_vars[i].def_value = locale_name; + break; + } + } + } + +#define SET_ENV_BUF_SIZE (4 * MAX_PATH) /* to cover EMACSLOADPATH */ + + /* Treat emacs_dir specially: set it unconditionally based on our + location, if it appears that we are running from the bin subdir + of a standard installation. */ + { + char *p; + char modname[MAX_PATH]; + + if (!GetModuleFileName (NULL, modname, MAX_PATH)) + abort (); + if ((p = strrchr (modname, '\\')) == NULL) + abort (); + *p = 0; + + if ((p = strrchr (modname, '\\')) && stricmp (p, "\\bin") == 0) + { + char buf[SET_ENV_BUF_SIZE]; + + *p = 0; + for (p = modname; *p; p++) + if (*p == '\\') *p = '/'; + + _snprintf (buf, sizeof(buf)-1, "emacs_dir=%s", modname); + _putenv (strdup (buf)); + } + } + + for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) { - if (!getenv (env_vars[i]) - && (lpval = w32_get_resource (env_vars[i], &dwType)) != NULL) + if (!getenv (env_vars[i].name)) { - if (dwType == REG_EXPAND_SZ) - { - char buf1[500], buf2[500]; + int dont_free = 0; - ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500); - _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1); - putenv (strdup (buf2)); + if ((lpval = w32_get_resource (env_vars[i].name, &dwType)) == NULL) + { + lpval = env_vars[i].def_value; + dwType = REG_EXPAND_SZ; + dont_free = 1; } - else if (dwType == REG_SZ) + + if (lpval) { - char buf[500]; + if (dwType == REG_EXPAND_SZ) + { + char buf1[SET_ENV_BUF_SIZE], buf2[SET_ENV_BUF_SIZE]; + + ExpandEnvironmentStrings ((LPSTR) lpval, buf1, sizeof(buf1)); + _snprintf (buf2, sizeof(buf2)-1, "%s=%s", env_vars[i].name, buf1); + _putenv (strdup (buf2)); + } + else if (dwType == REG_SZ) + { + char buf[SET_ENV_BUF_SIZE]; - _snprintf (buf, 499, "%s=%s", env_vars[i], lpval); - putenv (strdup (buf)); - } + _snprintf (buf, sizeof(buf)-1, "%s=%s", env_vars[i].name, lpval); + _putenv (strdup (buf)); + } - xfree (lpval); + if (!dont_free) + xfree (lpval); + } } } } @@ -773,20 +878,40 @@ init_environment (char ** argv) argv[0] = modname; } + /* Determine if there is a middle mouse button, to allow parse_button + to decide whether right mouse events should be mouse-2 or + mouse-3. */ + XSETINT (Vw32_num_mouse_buttons, GetSystemMetrics (SM_CMOUSEBUTTONS)); + init_user_info (); } +char * +emacs_root_dir (void) +{ + static char root_dir[FILENAME_MAX]; + const char *p; + + p = getenv ("emacs_dir"); + if (p == NULL) + abort (); + strcpy (root_dir, p); + root_dir[parse_root (root_dir, NULL)] = '\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. */ -static char configuration_buffer[32]; - 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 ()) @@ -825,23 +950,91 @@ get_emacs_configuration (void) break; } - /* Let oem be "*" until we figure out how to decode the OEM field. */ - oem = "*"; + /* 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; + } - os = (GetVersion () & OS_WIN95) ? "windows95" : "nt"; + 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); + } - sprintf (configuration_buffer, "%s-%s-%s%d.%d", arch, oem, os, - get_w32_major_version (), get_w32_minor_version ()); return configuration_buffer; } +char * +get_emacs_configuration_options (void) +{ + static char options_buffer[256]; + +/* 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 + + sprintf (options_buffer, COMPILER_VERSION); +#ifdef EMACSDEBUG + strcat (options_buffer, " --no-opt"); +#endif +#ifdef USER_CFLAGS + strcat (options_buffer, " --cflags"); + strcat (options_buffer, USER_CFLAGS); +#endif +#ifdef USER_LDFLAGS + strcat (options_buffer, " --ldflags"); + strcat (options_buffer, USER_LDFLAGS); +#endif + return options_buffer; +} + + #include /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */ void gettimeofday (struct timeval *tv, struct timezone *tz) { - struct _timeb tb; + struct timeb tb; _ftime (&tb); tv->tv_sec = tb.time; @@ -932,7 +1125,7 @@ lookup_volume_info (char * root_dir) static void add_volume_info (char * root_dir, volume_info_data * info) { - info->root_dir = strdup (root_dir); + info->root_dir = xstrdup (root_dir); info->next = volume_cache; volume_cache = info; } @@ -1015,15 +1208,15 @@ GetCachedVolumeInformation (char * root_dir) } else { - free (info->name); - free (info->type); + xfree (info->name); + xfree (info->type); } - info->name = strdup (name); + info->name = xstrdup (name); info->serialnum = serialnum; info->maxcomp = maxcomp; info->flags = flags; - info->type = strdup (type); + info->type = xstrdup (type); info->timestamp = GetTickCount (); } @@ -1101,7 +1294,14 @@ map_w32_filename (const char * name, const char ** pPath) char * path; const char * save_name = name; - if (is_fat_volume (name, &path)) /* truncate to 8.3 */ + if (strlen (name) >= MAX_PATH) + { + /* Return a filename which will cause callers to fail. */ + strcpy (shortname, "?"); + return shortname; + } + + if (is_fat_volume (name, (const char **)&path)) /* truncate to 8.3 */ { register int left = 8; /* maximum number of chars in part */ register int extn = 0; /* extension added? */ @@ -1354,9 +1554,9 @@ open_unc_volume (char *path) char * read_unc_volume (HANDLE henum, char *readbuf, int size) { - int count; + DWORD count; int result; - int bufsize = 512; + DWORD bufsize = 512; char *buffer; char *ptr; @@ -1640,14 +1840,20 @@ sys_mktemp (char * template) int sys_open (const char * path, int oflag, int mode) { - /* Force all file handles to be non-inheritable. */ - return _open (map_w32_filename (path, NULL), oflag | _O_NOINHERIT, mode); + const char* mpath = map_w32_filename (path, NULL); + /* Try to open file without _O_CREAT, to be able to write to hidden + and system files. Force all file handles to be + non-inheritable. */ + int res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode); + if (res >= 0) + return res; + return _open (mpath, oflag | _O_NOINHERIT, mode); } int sys_rename (const char * oldname, const char * newname) { - int result; + BOOL result; char temp[MAX_PATH]; /* MoveFile on Windows 95 doesn't correctly change the short file name @@ -1691,7 +1897,7 @@ sys_rename (const char * oldname, const char * newname) result = rename (oldname, temp); } /* This loop must surely terminate! */ - while (result < 0 && (errno == EEXIST || errno == EACCES)); + while (result < 0 && errno == EEXIST); if (result < 0) return -1; } @@ -1711,7 +1917,7 @@ sys_rename (const char * oldname, const char * newname) result = rename (temp, newname); if (result < 0 - && (errno == EEXIST || errno == EACCES) + && errno == EEXIST && _chmod (newname, 0666) == 0 && _unlink (newname) == 0) result = rename (temp, newname); @@ -1865,7 +2071,7 @@ stat (const char * path, struct stat * buf) } name = (char *) map_w32_filename (path, &path); - /* must be valid filename, no wild cards or other illegal characters */ + /* must be valid filename, no wild cards or other invalid characters */ if (strpbrk (name, "*?|<>\"")) { errno = ENOENT; @@ -1925,9 +2131,11 @@ stat (const char * path, struct stat * buf) /* (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 (IS_DIRECTORY_SEP (dir_pathname[len-1])) + len--; if (dir_find_handle != INVALID_HANDLE_VALUE - && (len = strlen (dir_pathname)), - strnicmp (name, dir_pathname, len) == 0 + && strnicmp (name, dir_pathname, len) == 0 && IS_DIRECTORY_SEP (name[len]) && stricmp (name + len + 1, dir_static.d_name) == 0) { @@ -1946,16 +2154,11 @@ stat (const char * path, struct stat * buf) } } - if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - buf->st_mode = _S_IFDIR; - buf->st_nlink = 2; /* doesn't really matter */ - fake_inode = 0; /* this doesn't either I think */ - } - else if (!NILP (Vw32_get_true_file_attributes) - /* No access rights required to get info. */ - && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, 0, NULL)) - != INVALID_HANDLE_VALUE) + if (!NILP (Vw32_get_true_file_attributes) + /* No access rights required to get info. */ + && (fh = CreateFile (name, 0, 0, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL)) + != INVALID_HANDLE_VALUE) { /* This is more accurate in terms of gettting the correct number of links, but is quite slow (it is noticable when Emacs is @@ -1978,25 +2181,33 @@ stat (const char * path, struct stat * buf) fake_inode = 0; } - switch (GetFileType (fh)) + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - case FILE_TYPE_DISK: - buf->st_mode = _S_IFREG; - break; - case FILE_TYPE_PIPE: - buf->st_mode = _S_IFIFO; - break; - case FILE_TYPE_CHAR: - case FILE_TYPE_UNKNOWN: - default: - buf->st_mode = _S_IFCHR; + buf->st_mode = _S_IFDIR; + } + else + { + switch (GetFileType (fh)) + { + case FILE_TYPE_DISK: + buf->st_mode = _S_IFREG; + break; + case FILE_TYPE_PIPE: + buf->st_mode = _S_IFIFO; + break; + case FILE_TYPE_CHAR: + case FILE_TYPE_UNKNOWN: + default: + buf->st_mode = _S_IFCHR; + } } CloseHandle (fh); } else { /* Don't bother to make this information more accurate. */ - buf->st_mode = _S_IFREG; + buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? + _S_IFREG : _S_IFDIR; buf->st_nlink = 1; fake_inode = 0; } @@ -2089,21 +2300,15 @@ fstat (int desc, struct stat * buf) } if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { buf->st_mode = _S_IFDIR; - buf->st_nlink = 2; /* doesn't really matter */ - fake_inode = 0; /* this doesn't either I think */ - } - else - { - buf->st_nlink = info.nNumberOfLinks; - /* Might as well use file index to fake inode values, but this - is not guaranteed to be unique unless we keep a handle open - all the time (even then there are situations where it is - not unique). Reputedly, there are at most 48 bits of info - (on NTFS, presumably less on FAT). */ - fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; - } + + buf->st_nlink = info.nNumberOfLinks; + /* Might as well use file index to fake inode values, but this + is not guaranteed to be unique unless we keep a handle open + all the time (even then there are situations where it is + not unique). Reputedly, there are at most 48 bits of info + (on NTFS, presumably less on FAT). */ + fake_inode = info.nFileIndexLow ^ info.nFileIndexHigh; /* MSVC defines _ino_t to be short; other libc's might not. */ if (sizeof (buf->st_ino) == 2) @@ -2220,7 +2425,18 @@ unsigned long (PASCAL *pfn_inet_addr) (const char * cp); int (PASCAL *pfn_gethostname) (char * name, int namelen); struct hostent * (PASCAL *pfn_gethostbyname) (const char * name); struct servent * (PASCAL *pfn_getservbyname) (const char * name, const char * proto); - +int (PASCAL *pfn_getpeername) (SOCKET s, struct sockaddr *addr, int * namelen); +int (PASCAL *pfn_setsockopt) (SOCKET s, int level, int optname, + const char * optval, int optlen); +int (PASCAL *pfn_listen) (SOCKET s, int backlog); +int (PASCAL *pfn_getsockname) (SOCKET s, struct sockaddr * name, + int * namelen); +SOCKET (PASCAL *pfn_accept) (SOCKET s, struct sockaddr * addr, int * addrlen); +int (PASCAL *pfn_recvfrom) (SOCKET s, char * buf, int len, int flags, + struct sockaddr * from, int * fromlen); +int (PASCAL *pfn_sendto) (SOCKET s, const char * buf, int len, int flags, + const struct sockaddr * to, int tolen); + /* SetHandleInformation is only needed to make sockets non-inheritable. */ BOOL (WINAPI *pfn_SetHandleInformation) (HANDLE object, DWORD mask, DWORD flags); #ifndef HANDLE_FLAG_INHERIT @@ -2289,8 +2505,14 @@ init_winsock (int load_now) LOAD_PROC( gethostname ); LOAD_PROC( gethostbyname ); LOAD_PROC( getservbyname ); + LOAD_PROC( getpeername ); LOAD_PROC( WSACleanup ); - + LOAD_PROC( setsockopt ); + LOAD_PROC( listen ); + LOAD_PROC( getsockname ); + LOAD_PROC( accept ); + LOAD_PROC( recvfrom ); + LOAD_PROC( sendto ); #undef LOAD_PROC /* specify version 1.1 of winsock */ @@ -2356,6 +2578,99 @@ static void check_errno () pfn_WSASetLastError (0); } +/* Extend strerror to handle the winsock-specific error codes. */ +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", +#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 failured", + 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", + + -1, NULL +}; + +char * +sys_strerror(int error_no) +{ + int i; + static char unknown_msg[40]; + + if (error_no >= 0 && error_no < sys_nerr) + return sys_errlist[error_no]; + + for (i = 0; _wsa_errlist[i].errnum >= 0; i++) + if (_wsa_errlist[i].errnum == error_no) + return _wsa_errlist[i].msg; + + sprintf(unknown_msg, "Unidentified error: %d", error_no); + return unknown_msg; +} + /* [andrewi 3-May-96] I've had conflicting results using both methods, but I believe the method of keeping the socket handle separate (and insuring it is not inheritable) is the correct one. */ @@ -2368,12 +2683,12 @@ static void check_errno () #define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd) #endif +int socket_to_fd (SOCKET s); + int sys_socket(int af, int type, int protocol) { - int fd; - long s; - child_process * cp; + SOCKET s; if (winsock_lib == NULL) { @@ -2384,89 +2699,114 @@ sys_socket(int af, int type, int protocol) check_errno (); /* call the real socket function */ - s = (long) pfn_socket (af, type, protocol); + s = pfn_socket (af, type, protocol); if (s != INVALID_SOCKET) - { - /* Although under NT 3.5 _open_osfhandle will accept a socket - handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT, - that does not work under NT 3.1. However, we can get the same - effect by using a backdoor function to replace an existing - descriptor handle with the one we want. */ + return socket_to_fd (s); - /* allocate a file descriptor (with appropriate flags) */ - fd = _open ("NUL:", _O_RDWR); - if (fd >= 0) - { + set_errno (); + return -1; +} + +/* Convert a SOCKET to a file descriptor. */ +int +socket_to_fd (SOCKET s) +{ + int fd; + child_process * cp; + + /* Although under NT 3.5 _open_osfhandle will accept a socket + handle, if opened with SO_OPENTYPE == SO_SYNCHRONOUS_NONALERT, + that does not work under NT 3.1. However, we can get the same + effect by using a backdoor function to replace an existing + descriptor handle with the one we want. */ + + /* allocate a file descriptor (with appropriate flags) */ + fd = _open ("NUL:", _O_RDWR); + if (fd >= 0) + { #ifdef SOCK_REPLACE_HANDLE - /* now replace handle to NUL with our socket handle */ - CloseHandle ((HANDLE) _get_osfhandle (fd)); - _free_osfhnd (fd); - _set_osfhnd (fd, s); - /* setmode (fd, _O_BINARY); */ + /* now replace handle to NUL with our socket handle */ + CloseHandle ((HANDLE) _get_osfhandle (fd)); + _free_osfhnd (fd); + _set_osfhnd (fd, s); + /* setmode (fd, _O_BINARY); */ #else - /* Make a non-inheritable copy of the socket handle. */ + /* Make a non-inheritable copy of the socket handle. Note + that it is possible that sockets aren't actually kernel + handles, which appears to be the case on Windows 9x when + the MS Proxy winsock client is installed. */ + { + /* Apparently there is a bug in NT 3.51 with some service + packs, which prevents using DuplicateHandle to make a + socket handle non-inheritable (causes WSACleanup to + hang). The work-around is to use SetHandleInformation + instead if it is available and implemented. */ + if (pfn_SetHandleInformation) { - HANDLE parent; + pfn_SetHandleInformation ((HANDLE) s, HANDLE_FLAG_INHERIT, 0); + } + else + { + HANDLE parent = GetCurrentProcess (); HANDLE new_s = INVALID_HANDLE_VALUE; - parent = GetCurrentProcess (); - - /* Apparently there is a bug in NT 3.51 with some service - packs, which prevents using DuplicateHandle to make a - socket handle non-inheritable (causes WSACleanup to - hang). The work-around is to use SetHandleInformation - instead if it is available and implemented. */ - if (!pfn_SetHandleInformation - || !pfn_SetHandleInformation ((HANDLE) s, - HANDLE_FLAG_INHERIT, - 0)) - { - DuplicateHandle (parent, + if (DuplicateHandle (parent, (HANDLE) s, parent, &new_s, 0, FALSE, - DUPLICATE_SAME_ACCESS); - pfn_closesocket (s); - s = (SOCKET) new_s; - } - fd_info[fd].hnd = (HANDLE) s; + DUPLICATE_SAME_ACCESS)) + { + /* It is possible that DuplicateHandle succeeds even + though the socket wasn't really a kernel handle, + because a real handle has the same value. So + test whether the new handle really is a socket. */ + long nonblocking = 0; + if (pfn_ioctlsocket ((SOCKET) new_s, FIONBIO, &nonblocking) == 0) + { + pfn_closesocket (s); + s = (SOCKET) new_s; + } + else + { + CloseHandle (new_s); + } + } } + } + fd_info[fd].hnd = (HANDLE) s; #endif - /* set our own internal flags */ - fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE; + /* set our own internal flags */ + fd_info[fd].flags = FILE_SOCKET | FILE_BINARY | FILE_READ | FILE_WRITE; - cp = new_child (); - if (cp) - { - cp->fd = fd; - cp->status = STATUS_READ_ACKNOWLEDGED; - - /* attach child_process to fd_info */ - if (fd_info[ fd ].cp != NULL) - { - DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd)); - abort (); - } - - fd_info[ fd ].cp = cp; + cp = new_child (); + if (cp) + { + cp->fd = fd; + cp->status = STATUS_READ_ACKNOWLEDGED; - /* success! */ - winsock_inuse++; /* count open sockets */ - return fd; + /* attach child_process to fd_info */ + if (fd_info[ fd ].cp != NULL) + { + DebPrint (("sys_socket: fd_info[%d] apparently in use!\n", fd)); + abort (); } - /* clean up */ - _close (fd); + fd_info[ fd ].cp = cp; + + /* success! */ + winsock_inuse++; /* count open sockets */ + return fd; } - pfn_closesocket (s); - h_errno = EMFILE; - } - set_errno (); + /* clean up */ + _close (fd); + } + pfn_closesocket (s); + h_errno = EMFILE; return -1; } @@ -2542,7 +2882,7 @@ sys_gethostname (char * name, int namelen) return pfn_gethostname (name, namelen); if (namelen > MAX_COMPUTERNAME_LENGTH) - return !GetComputerName (name, &namelen); + return !GetComputerName (name, (DWORD *)&namelen); h_errno = EFAULT; return SOCKET_ERROR; @@ -2585,10 +2925,30 @@ sys_getservbyname(const char * name, const char * proto) } int -sys_shutdown (int s, int how) +sys_getpeername (int s, struct sockaddr *addr, int * namelen) { - int rc; + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_getpeername (SOCK_HANDLE (s), addr, namelen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + + +int +sys_shutdown (int s, int how) +{ if (winsock_lib == NULL) { h_errno = ENETDOWN; @@ -2607,6 +2967,171 @@ sys_shutdown (int s, int how) return SOCKET_ERROR; } +int +sys_setsockopt (int s, int level, int optname, const char * optval, int optlen) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_setsockopt (SOCK_HANDLE (s), level, optname, + optval, optlen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +int +sys_listen (int s, int backlog) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_listen (SOCK_HANDLE (s), backlog); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +int +sys_getsockname (int s, struct sockaddr * name, int * namelen) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_getsockname (SOCK_HANDLE (s), name, namelen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +int +sys_accept (int s, struct sockaddr * addr, int * addrlen) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return -1; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + SOCKET t = pfn_accept (SOCK_HANDLE (s), addr, addrlen); + if (t != INVALID_SOCKET) + return socket_to_fd (t); + + set_errno (); + return -1; + } + h_errno = ENOTSOCK; + return -1; +} + +int +sys_recvfrom (int s, char * buf, int len, int flags, + struct sockaddr * from, int * fromlen) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_recvfrom (SOCK_HANDLE (s), buf, len, flags, from, fromlen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +int +sys_sendto (int s, const char * buf, int len, int flags, + const struct sockaddr * to, int tolen) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_sendto (SOCK_HANDLE (s), buf, len, flags, to, tolen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +/* Windows does not have an fcntl function. Provide an implementation + solely for making sockets non-blocking. */ +int +fcntl (int s, int cmd, int options) +{ + if (winsock_lib == NULL) + { + h_errno = ENETDOWN; + return -1; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + if (cmd == F_SETFL && options == O_NDELAY) + { + unsigned long nblock = 1; + int rc = pfn_ioctlsocket (SOCK_HANDLE (s), FIONBIO, &nblock); + if (rc == SOCKET_ERROR) + set_errno(); + /* Keep track of the fact that we set this to non-blocking. */ + fd_info[s].flags |= FILE_NDELAY; + return rc; + } + else + { + h_errno = EINVAL; + return SOCKET_ERROR; + } + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + #endif /* HAVE_SOCKETS */ @@ -2717,7 +3242,6 @@ sys_pipe (int * phandles) { int rc; unsigned flags; - child_process * cp; /* make pipe handles non-inheritable; when we spawn a child, we replace the relevant handle with an inheritable one. Also put @@ -2791,7 +3315,20 @@ _sys_read_ahead (int fd) } #ifdef HAVE_SOCKETS else if (fd_info[fd].flags & FILE_SOCKET) - rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0); + { + unsigned long nblock = 0; + /* We always want this to block, so temporarily disable NDELAY. */ + if (fd_info[fd].flags & FILE_NDELAY) + pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock); + + rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0); + + if (fd_info[fd].flags & FILE_NDELAY) + { + nblock = 1; + pfn_ioctlsocket (SOCK_HANDLE (fd), FIONBIO, &nblock); + } + } #endif if (rc == sizeof (char)) @@ -3041,7 +3578,7 @@ check_windows_init_file () objs[1] = decode_env_path (0, (getenv ("EMACSLOADPATH"))); full_load_path = Fappend (2, objs); init_file = build_string ("term/w32-win"); - fd = openp (full_load_path, init_file, ".el:.elc", NULL, 0); + fd = openp (full_load_path, init_file, Vload_suffixes, NULL, Qnil); if (fd < 0) { Lisp_Object load_path_print = Fprin1_to_string (full_load_path, Qnil); @@ -3069,7 +3606,7 @@ check_windows_init_file () } else { - close (fd); + _close (fd); } } } @@ -3179,6 +3716,9 @@ init_ntproc () (*drive)++; } + + /* Reset the volume info cache. */ + volume_cache = NULL; } /* Check to see if Emacs has been installed correctly. */