Mercurial > emacs
changeset 15141:6565b268b12a
(nt_stat, nt_ctime): Functions deleted.
Undefine macros of CRT functions.
author | Geoff Voelker <voelker@cs.washington.edu> |
---|---|
date | Fri, 03 May 1996 18:32:52 +0000 |
parents | f13ffba51542 |
children | 6879c02a290c |
files | src/w32.c |
diffstat | 1 files changed, 1774 insertions(+), 795 deletions(-) [+] |
line wrap: on
line diff
--- a/src/w32.c Fri May 03 18:32:30 1996 +0000 +++ b/src/w32.c Fri May 03 18:32:52 1996 +0000 @@ -22,55 +22,37 @@ */ -/* Define stat before including config.h. */ -#include <string.h> -#include <sys/stat.h> -#include <malloc.h> - -static int is_toplevel_share_name (char *); -static int stat_toplevel_share (char *, void *); - -int -nt_stat (char *filename, struct stat *statbuf) -{ - int l = strlen (filename); - char *str = NULL; - - /* stat has a bug when passed a name of a directory with a trailing - backslash (but a trailing forward slash works fine). */ - if (filename[l - 1] == '\\') - { - str = (char *) alloca (l + 1); - strcpy (str, filename); - str[l - 1] = '/'; - return stat (str, statbuf); - } - - if (stat (filename, statbuf) == 0) - return 0; - else if (is_toplevel_share_name (filename)) - return stat_toplevel_share (filename, statbuf); - else - return -1; -} - -/* Place a wrapper around the NT version of ctime. It returns NULL - on network directories, so we handle that case here. - Define it before including config.h. (Ulrich Leodolter, 1/11/95). */ -char * -nt_ctime (const time_t *t) -{ - char *str = (char *) ctime (t); - return (str ? str : "Sun Jan 01 00:00:00 1970"); -} - -#include <config.h> -#include <windows.h> #include <stdlib.h> #include <stdio.h> #include <io.h> +#include <errno.h> #include <fcntl.h> #include <ctype.h> +#include <signal.h> +#include <sys/time.h> + +/* must include CRT headers *before* config.h */ +#include "config.h" +#undef access +#undef chdir +#undef chmod +#undef creat +#undef ctime +#undef fopen +#undef link +#undef mkdir +#undef mktemp +#undef open +#undef rename +#undef rmdir +#undef unlink + +#undef close +#undef dup +#undef dup2 +#undef pipe +#undef read +#undef write #define getwd _getwd #include "lisp.h" @@ -78,114 +60,35 @@ #include <pwd.h> +#include <windows.h> + +#ifdef HAVE_SOCKETS /* TCP connection support, if kernel can do it */ +#include <sys/socket.h> +#undef socket +#undef bind +#undef connect +#undef htons +#undef ntohs +#undef inet_addr +#undef gethostname +#undef gethostbyname +#undef getservbyname +#endif + +#include "nt.h" #include "ndir.h" #include "ntheap.h" -extern int report_file_error (char *, Lisp_Object); - -/* Routines for extending stat above. */ -static int -get_unassigned_drive_letter () +/* Get the current working directory. */ +char * +getwd (char *dir) { - int i; - unsigned int mask; - - mask = GetLogicalDrives (); - for (i = 0; i < 26; i++) - { - if (mask & (1 << i)) - continue; - break; - } - return (i == 26 ? -1 : 'A' + i); -} - -void dostounix_filename (char *); - -/* Return nonzero if NAME is of the form \\host\share (forward slashes - also valid), otherwise return 0. */ -static int -is_toplevel_share_name (char *filename) -{ - int len; - char *name; - char *host; - char *share; - char *suffix; - - len = strlen (filename); - name = alloca (len + 1); - strcpy (name, filename); - - dostounix_filename (name); - if (name[0] != '/' || name[1] != '/') - return 0; - - host = strtok (&name[2], "/"); - share = strtok (NULL, "/"); - suffix = strtok (NULL, "/"); - if (!host || !share || suffix) - return 0; - - return 1; + if (GetCurrentDirectory (MAXPATHLEN, dir) > 0) + return dir; + return NULL; } - -/* FILENAME is of the form \\host\share, and stat can't handle names - of this form. But stat can handle \\host\share if it's been - assigned a drive letter. So we create a network connection to this - share, assign it a drive letter, stat the drive letter, and - disconnect from the share. Hassle... */ -static int -stat_toplevel_share (char *filename, void *statbuf) -{ - NETRESOURCE net; - int drive_letter; - char drive[4]; - int result; - - drive_letter = get_unassigned_drive_letter (); - if (drive_letter < 0) - return -1; - - drive[0] = drive_letter; - drive[1] = ':'; - drive[2] = '\0'; - net.dwType = RESOURCETYPE_DISK; - net.lpLocalName = drive; - net.lpRemoteName = filename; - net.lpProvider = NULL; - - switch (WNetAddConnection2 (&net, NULL, NULL, 0)) - { - case NO_ERROR: - break; - case ERROR_ALREADY_ASSIGNED: - default: - return -1; - } - - /* Name the toplevel directory on the drive letter. */ - drive[2] = '/'; - drive[3] = '\0'; - result = stat (drive, (void *) statbuf); - - /* Strip the slash so we can disconnect. */ - drive[2] = '\0'; - if (WNetCancelConnection2 (drive, 0, TRUE) != NO_ERROR) - result = -1; - - return result; -} - - -/* Get the current working directory. */ -int -getwd (char *dir) -{ - return GetCurrentDirectory (MAXPATHLEN, dir); -} - +#ifndef HAVE_SOCKETS /* Emulate gethostname. */ int gethostname (char *buffer, int size) @@ -194,6 +97,7 @@ certainly large enough. */ return !GetComputerName (buffer, &size); } +#endif /* HAVE_SOCKETS */ /* Emulate getloadavg. */ int @@ -209,124 +113,14 @@ return i; } -/* Emulate sleep...we could have done this with a define, but that - would necessitate including windows.h in the files that used it. - This is much easier. */ -void -nt_sleep (int seconds) -{ - Sleep (seconds * 1000); -} - -/* Emulate rename. */ - -#ifndef ENOENT -#define ENOENT 2 -#endif -#ifndef EXDEV -#define EXDEV 18 -#endif -#ifndef EINVAL -#define EINVAL 22 -#endif - -int -rename (const char *oldname, const char *newname) -{ -#ifdef WINDOWS95 - int i, len, len0, len1; - char *dirs[2], *names[2], *ptr; - - /* A bug in MoveFile under Windows 95 incorrectly renames files in - some cases. If the old name is of the form FILENAME or - FILENAME.SUF, and the new name is of the form FILENAME~ or - FILENAME.SUF~, and both the source and target are in the same - directory, then MoveFile renames the long form of the filename to - FILENAME~ (FILENAME.SUF~) but leaves the DOS short form as - FILENAME (FILENAME.SUF). The result is that the two different - filenames refer to the same file. In this case, rename the - source to a temporary name that can then successfully be renamed - to the target. */ - - dirs[0] = names[0] = oldname; - dirs[1] = names[1] = newname; - for (i = 0; i < 2; i++) - { - /* Canonicalize and remove prefix. */ - len = strlen (names[i]); - for (ptr = names[i] + len - 1; ptr > names[i]; ptr--) - { - if (IS_ANY_SEP (ptr[0]) && ptr[1] != '\0') - { - names[i] = ptr + 1; - break; - } - } - } - - len0 = strlen (names[0]); - len1 = strlen (names[1]); - - /* The predicate is whether the file is being renamed to a filename - with ~ appended. This is conservative, but should be correct. */ - if ((len0 == len1 - 1) - && (names[1][len0] == '~') - && (!strnicmp (names[0], names[1], len0))) - { - /* Rename the source to a temporary name that can succesfully be - renamed to the target. The temporary name is in the directory - of the target. */ - char *tmp, *fulltmp; - - tmp = "eXXXXXX"; - fulltmp = alloca (strlen (dirs[1]) + strlen (tmp) + 1); - fulltmp[0] = '\0'; - if (dirs[1] != names[1]) - { - len = names[1] - dirs[1]; - strncpy (fulltmp, dirs[1], len); - fulltmp[len] = '\0'; - } - strcat (fulltmp, tmp); - mktemp (fulltmp); - - if (rename (oldname, fulltmp) < 0) - return -1; - - oldname = fulltmp; - } -#endif - - if (!MoveFile (oldname, newname)) - { - switch (GetLastError ()) - { - case ERROR_FILE_NOT_FOUND: - errno = ENOENT; - break; - case ERROR_ACCESS_DENIED: - /* This gets returned when going across devices. */ - errno = EXDEV; - break; - case ERROR_FILE_EXISTS: - case ERROR_ALREADY_EXISTS: - default: - errno = EINVAL; - break; - } - return -1; - } - errno = 0; - return 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. */ struct direct dir_static; /* simulated directory contents */ -static int dir_finding; -static HANDLE dir_find_handle; +static HANDLE dir_find_handle = INVALID_HANDLE_VALUE; +static int dir_is_fat; +static char dir_pathname[MAXPATHLEN+1]; DIR * opendir (char *filename) @@ -334,22 +128,21 @@ DIR *dirp; /* Opening is done by FindFirstFile. However, a read is inherent to - this operation, so we have a flag to handle the open at read - time. This flag essentially means "there is a find-handle open and - it needs to be closed." */ + this operation, so we defer the open until read time. */ - if (!(dirp = (DIR *) malloc (sizeof (DIR)))) - { - return 0; - } + if (!(dirp = (DIR *) malloc (sizeof (DIR)))) + return NULL; + if (dir_find_handle != INVALID_HANDLE_VALUE) + return NULL; dirp->dd_fd = 0; dirp->dd_loc = 0; dirp->dd_size = 0; - /* This is tacky, but we need the directory name for our - implementation of readdir. */ - strncpy (dirp->dd_buf, filename, DIRBLKSIZ); + strncpy (dir_pathname, filename, MAXPATHLEN); + dir_pathname[MAXPATHLEN] = '\0'; + dir_is_fat = is_fat_volume (filename, NULL); + return dirp; } @@ -357,10 +150,10 @@ closedir (DIR *dirp) { /* If we have a find-handle open, close it. */ - if (dir_finding) + if (dir_find_handle != INVALID_HANDLE_VALUE) { FindClose (dir_find_handle); - dir_finding = 0; + dir_find_handle = INVALID_HANDLE_VALUE; } xfree ((char *) dirp); } @@ -371,46 +164,44 @@ WIN32_FIND_DATA find_data; /* If we aren't dir_finding, do a find-first, otherwise do a find-next. */ - if (!dir_finding) + if (dir_find_handle == INVALID_HANDLE_VALUE) { char filename[MAXNAMLEN + 3]; int ln; - strncpy (filename, dirp->dd_buf, MAXNAMLEN); - ln = strlen (filename)-1; - if (!IS_ANY_SEP (filename[ln])) + strcpy (filename, dir_pathname); + ln = strlen (filename) - 1; + if (!IS_DIRECTORY_SEP (filename[ln])) strcat (filename, "\\"); - strcat (filename, "*.*"); + strcat (filename, "*"); dir_find_handle = FindFirstFile (filename, &find_data); - if (dir_find_handle == INVALID_HANDLE_VALUE) + if (dir_find_handle == INVALID_HANDLE_VALUE) return NULL; - - dir_finding = 1; - } - else + } + else { if (!FindNextFile (dir_find_handle, &find_data)) return NULL; } - /* NT's unique ID for a file is 64 bits, so we have to fake it here. - This should work as long as we never use 0. */ + /* Emacs never uses this value, so don't bother making it match + value returned by stat(). */ dir_static.d_ino = 1; dir_static.d_reclen = sizeof (struct direct) - MAXNAMLEN + 3 + dir_static.d_namlen - dir_static.d_namlen % 4; dir_static.d_namlen = strlen (find_data.cFileName); - strncpy (dir_static.d_name, find_data.cFileName, MAXNAMLEN); + strcpy (dir_static.d_name, find_data.cFileName); + if (dir_is_fat) + _strlwr (dir_static.d_name); return &dir_static; } -/* Emulate getpwuid and getpwnam. */ - -int getuid (); /* forward declaration */ +/* Emulate getpwuid, getpwnam and others. */ #define PASSWD_FIELD_SIZE 256 @@ -432,22 +223,39 @@ the_passwd_shell, }; +int +getuid () +{ + return the_passwd.pw_uid; +} + +int +geteuid () +{ + /* I could imagine arguing for checking to see whether the user is + in the Administrators group and returning a UID of 0 for that + case, but I don't know how wise that would be in the long run. */ + return getuid (); +} + +int +getgid () +{ + return the_passwd.pw_gid; +} + +int +getegid () +{ + return getgid (); +} + struct passwd * getpwuid (int uid) { - int size = PASSWD_FIELD_SIZE; - - if (!GetUserName (the_passwd.pw_name, &size)) - return NULL; - - the_passwd.pw_passwd[0] = '\0'; - the_passwd.pw_uid = 0; - the_passwd.pw_gid = 0; - strcpy (the_passwd.pw_gecos, the_passwd.pw_name); - the_passwd.pw_dir[0] = '\0'; - the_passwd.pw_shell[0] = '\0'; - - return &the_passwd; + if (uid == the_passwd.pw_uid) + return &the_passwd; + return NULL; } struct passwd * @@ -459,12 +267,311 @@ if (!pw) return pw; - if (strcmp (name, pw->pw_name)) + if (stricmp (name, pw->pw_name)) return NULL; return pw; } +void +init_user_info () +{ + /* Find the user's real name by opening the process token and + looking up the name associated with the user-sid in that token. + + Use the relative portion of the identifier authority value from + the user-sid as the user id value (same for group id using the + primary group sid from the process token). */ + + char user_sid[256], name[256], domain[256]; + DWORD length = sizeof (name), dlength = sizeof (domain), trash; + HANDLE token = NULL; + SID_NAME_USE user_type; + + if (OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &token) + && GetTokenInformation (token, TokenUser, + (PVOID) user_sid, sizeof (user_sid), &trash) + && LookupAccountSid (NULL, *((PSID *) user_sid), name, &length, + domain, &dlength, &user_type)) + { + strcpy (the_passwd.pw_name, name); + /* Determine a reasonable uid value. */ + if (stricmp ("administrator", name) == 0) + { + the_passwd.pw_uid = 0; + the_passwd.pw_gid = 0; + } + else + { + SID_IDENTIFIER_AUTHORITY * pSIA; + + pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid)); + /* I believe the relative portion is the last 4 bytes (of 6) + with msb first. */ + the_passwd.pw_uid = ((pSIA->Value[2] << 24) + + (pSIA->Value[3] << 16) + + (pSIA->Value[4] << 8) + + (pSIA->Value[5] << 0)); + /* restrict to conventional uid range for normal users */ + the_passwd.pw_uid = the_passwd.pw_uid % 60001; + + /* Get group id */ + if (GetTokenInformation (token, TokenPrimaryGroup, + (PVOID) user_sid, sizeof (user_sid), &trash)) + { + SID_IDENTIFIER_AUTHORITY * pSIA; + + pSIA = GetSidIdentifierAuthority (*((PSID *) user_sid)); + the_passwd.pw_gid = ((pSIA->Value[2] << 24) + + (pSIA->Value[3] << 16) + + (pSIA->Value[4] << 8) + + (pSIA->Value[5] << 0)); + /* I don't know if this is necessary, but for safety... */ + the_passwd.pw_gid = the_passwd.pw_gid % 60001; + } + else + the_passwd.pw_gid = the_passwd.pw_uid; + } + } + /* If security calls are not supported (presumably because we + are running under Windows 95), fallback to this. */ + else if (GetUserName (name, &length)) + { + strcpy (the_passwd.pw_name, name); + if (stricmp ("administrator", name) == 0) + the_passwd.pw_uid = 0; + else + the_passwd.pw_uid = 123; + the_passwd.pw_gid = the_passwd.pw_uid; + } + else + { + strcpy (the_passwd.pw_name, "unknown"); + the_passwd.pw_uid = 123; + the_passwd.pw_gid = 123; + } + + /* Ensure HOME and SHELL are defined. */ + if (getenv ("HOME") == NULL) + putenv ("HOME=c:/"); + if (getenv ("SHELL") == NULL) + putenv ((GetVersion () & 0x80000000) ? "SHELL=command" : "SHELL=cmd"); + + /* Set dir and shell from environment variables. */ + strcpy (the_passwd.pw_dir, getenv ("HOME")); + strcpy (the_passwd.pw_shell, getenv ("SHELL")); + + if (token) + CloseHandle (token); +} + +int +random () +{ + /* rand () on NT gives us 15 random bits...hack together 30 bits. */ + return ((rand () << 15) | rand ()); +} + +void +srandom (int seed) +{ + srand (seed); +} + +/* Destructively turn backslashes into slashes. */ +void +dostounix_filename (p) + register char *p; +{ + while (*p) + { + if (*p == '\\') + *p = '/'; + p++; + } +} + +/* Destructively turn slashes into backslashes. */ +void +unixtodos_filename (p) + register char *p; +{ + while (*p) + { + if (*p == '/') + *p = '\\'; + p++; + } +} + +/* Remove all CR's that are followed by a LF. + (From msdos.c...probably should figure out a way to share it, + although this code isn't going to ever change.) */ +int +crlf_to_lf (n, buf) + register int n; + register unsigned char *buf; +{ + unsigned char *np = buf; + unsigned char *startp = buf; + unsigned char *endp = buf + n; + + if (n == 0) + return n; + while (buf < endp - 1) + { + if (*buf == 0x0d) + { + if (*(++buf) != 0x0a) + *np++ = 0x0d; + } + else + *np++ = *buf++; + } + if (buf < endp) + *np++ = *buf++; + return np - startp; +} + +/* Routines that are no-ops on NT but are defined to get Emacs to compile. */ + +int +sigsetmask (int signal_mask) +{ + return 0; +} + +int +sigblock (int sig) +{ + return 0; +} + +int +setpgrp (int pid, int gid) +{ + return 0; +} + +int +alarm (int seconds) +{ + return 0; +} + +int +unrequest_sigio (void) +{ + return 0; +} + +int +request_sigio (void) +{ + return 0; +} + +#define REG_ROOT "SOFTWARE\\GNU\\Emacs" + +LPBYTE +nt_get_resource (key, lpdwtype) + char *key; + LPDWORD lpdwtype; +{ + LPBYTE lpvalue; + HKEY hrootkey = NULL; + DWORD cbData; + BOOL ok = FALSE; + + /* Check both the current user and the local machine to see if + we have any resources. */ + + if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) + { + lpvalue = NULL; + + if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS + && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL + && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS) + { + return (lpvalue); + } + + if (lpvalue) xfree (lpvalue); + + RegCloseKey (hrootkey); + } + + if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) + { + lpvalue = NULL; + + if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS && + (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL && + RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS) + { + return (lpvalue); + } + + if (lpvalue) xfree (lpvalue); + + RegCloseKey (hrootkey); + } + + return (NULL); +} + +void +init_environment () +{ + /* Check for environment variables and use registry if they don't exist */ + { + int i; + LPBYTE lpval; + DWORD dwType; + + static char * env_vars[] = + { + "HOME", + "emacs_dir", + "EMACSLOADPATH", + "SHELL", + "EMACSDATA", + "EMACSPATH", + "EMACSLOCKDIR", + "INFOPATH", + "EMACSDOC", + "TERM", + }; + + for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) + { + if (!getenv (env_vars[i]) && + (lpval = nt_get_resource (env_vars[i], &dwType)) != NULL) + { + if (dwType == REG_EXPAND_SZ) + { + char buf1[500], buf2[500]; + + ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500); + _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1); + putenv (strdup (buf2)); + } + else if (dwType == REG_SZ) + { + char buf[500]; + + _snprintf (buf, 499, "%s=%s", env_vars[i], lpval); + putenv (strdup (buf)); + } + + xfree (lpval); + } + } + } + + init_user_info (); +} /* 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 @@ -518,478 +625,13 @@ /* Let oem be "*" until we figure out how to decode the OEM field. */ oem = "*"; -#ifdef WINDOWS95 - os = "win"; -#else - os = "nt"; -#endif + os = (GetVersion () & 0x80000000) ? "win95" : "nt"; sprintf (configuration_buffer, "%s-%s-%s%d.%d", arch, oem, os, get_nt_major_version (), get_nt_minor_version ()); return configuration_buffer; } -/* Conjure up inode and device numbers that will serve the purpose - of Emacs. Return 1 upon success, 0 upon failure. */ -int -get_inode_and_device_vals (Lisp_Object filename, Lisp_Object *p_inode, - Lisp_Object *p_device) -{ - /* File uids on NT are found using a handle to a file, which - implies that it has been opened. Since we want to be able - to stat an arbitrary file, we must open it, get the info, - and then close it. - - Also, NT file uids are 64-bits. This is a problem. */ - - HANDLE handle; - BOOL result; - DWORD attrs; - BY_HANDLE_FILE_INFORMATION info; - - /* We have to stat files and directories differently, so check - to see what filename references. */ - attrs = GetFileAttributes (XSTRING (filename)->data); - if (attrs == 0xFFFFFFFF) { - return 0; - } - if (attrs & FILE_ATTRIBUTE_DIRECTORY) { - /* Conjure up bogus, but unique, values. */ - attrs = GetTickCount (); - *p_inode = make_number (attrs); - *p_device = make_number (attrs); - return 1; - } - - /* FIXME: It shouldn't be opened without READ access, but NT on x86 - doesn't allow GetFileInfo in that case (NT on mips does). */ - - handle = CreateFile (XSTRING (filename)->data, - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); - if (handle == INVALID_HANDLE_VALUE) - return 0; - - result = GetFileInformationByHandle (handle, &info); - CloseHandle (handle); - if (!result) - return 0; - - *p_inode = make_number (info.nFileIndexLow); /* use the low value */ - *p_device = make_number (info.dwVolumeSerialNumber); - - return 1; -} - -/* The following pipe routines are used to support our fork emulation. - Since NT's crt dup always creates inherited handles, we - must be careful in setting up pipes. First create - non-inherited pipe handles, then create an inherited handle - to the write end by dup-ing it, and then close the non-inherited - end that was just duped. This gives us one non-inherited handle - on the read end and one inherited handle to the write end. As - the parent, we close the inherited handle to the write end after - spawning the child. */ - -/* From callproc.c */ -extern Lisp_Object Vbinary_process_input; -extern Lisp_Object Vbinary_process_output; - -void -pipe_with_inherited_out (int fds[2]) -{ - int inherit_out; - unsigned int flags = _O_NOINHERIT; - - if (!NILP (Vbinary_process_output)) - flags |= _O_BINARY; - - _pipe (fds, 0, flags); - inherit_out = dup (fds[1]); - close (fds[1]); - fds[1] = inherit_out; -} - -void -pipe_with_inherited_in (int fds[2]) -{ - int inherit_in; - unsigned int flags = _O_NOINHERIT; - - if (!NILP (Vbinary_process_input)) - flags |= _O_BINARY; - - _pipe (fds, 0, flags); - inherit_in = dup (fds[0]); - close (fds[0]); - fds[0] = inherit_in; -} - -/* The following two routines are used to manipulate stdin, stdout, and - stderr of our child processes. - - Assuming that in, out, and err are inherited, we make them stdin, - stdout, and stderr of the child as follows: - - - Save the parent's current standard handles. - - Set the parent's standard handles to the handles being passed in. - (Note that _get_osfhandle is an io.h procedure that - maps crt file descriptors to NT file handles.) - - Spawn the child, which inherits in, out, and err as stdin, - stdout, and stderr. (see Spawnve) - - Reset the parent's standard handles to the saved handles. - (see reset_standard_handles) - We assume that the caller closes in, out, and err after calling us. */ - -void -prepare_standard_handles (int in, int out, int err, HANDLE handles[4]) -{ - HANDLE parent, stdin_save, stdout_save, stderr_save, err_handle; - -#ifdef WINDOWS95 - /* The Win95 beta doesn't set the standard handles correctly. - Handicap subprocesses until we get a version that works correctly. - Undefining the subprocesses macro reveals other incompatibilities, - so, since we're expecting subprocs to work in the near future, - disable them here. */ - report_file_error ("Subprocesses currently disabled on Win95", Qnil); -#endif - - parent = GetCurrentProcess (); - stdin_save = GetStdHandle (STD_INPUT_HANDLE); - stdout_save = GetStdHandle (STD_OUTPUT_HANDLE); - stderr_save = GetStdHandle (STD_ERROR_HANDLE); - -#ifndef HAVE_NTGUI - if (!DuplicateHandle (parent, - GetStdHandle (STD_INPUT_HANDLE), - parent, - &stdin_save, - 0, - FALSE, - DUPLICATE_SAME_ACCESS)) - report_file_error ("Duplicating parent's input handle", Qnil); - - if (!DuplicateHandle (parent, - GetStdHandle (STD_OUTPUT_HANDLE), - parent, - &stdout_save, - 0, - FALSE, - DUPLICATE_SAME_ACCESS)) - report_file_error ("Duplicating parent's output handle", Qnil); - - if (!DuplicateHandle (parent, - GetStdHandle (STD_ERROR_HANDLE), - parent, - &stderr_save, - 0, - FALSE, - DUPLICATE_SAME_ACCESS)) - report_file_error ("Duplicating parent's error handle", Qnil); -#endif /* !HAVE_NTGUI */ - - if (!SetStdHandle (STD_INPUT_HANDLE, (HANDLE) _get_osfhandle (in))) - report_file_error ("Changing stdin handle", Qnil); - - if (!SetStdHandle (STD_OUTPUT_HANDLE, (HANDLE) _get_osfhandle (out))) - report_file_error ("Changing stdout handle", Qnil); - - /* We lose data if we use the same handle to the pipe for stdout and - stderr, so make a duplicate. This took a while to find. */ - if (out == err) - { - if (!DuplicateHandle (parent, - (HANDLE) _get_osfhandle (err), - parent, - &err_handle, - 0, - TRUE, - DUPLICATE_SAME_ACCESS)) - report_file_error ("Duplicating out handle to make err handle.", - Qnil); - } - else - { - err_handle = (HANDLE) _get_osfhandle (err); - } - - if (!SetStdHandle (STD_ERROR_HANDLE, err_handle)) - report_file_error ("Changing stderr handle", Qnil); - - handles[0] = stdin_save; - handles[1] = stdout_save; - handles[2] = stderr_save; - handles[3] = err_handle; -} - -void -reset_standard_handles (int in, int out, int err, HANDLE handles[4]) -{ - HANDLE stdin_save = handles[0]; - HANDLE stdout_save = handles[1]; - HANDLE stderr_save = handles[2]; - HANDLE err_handle = handles[3]; - int i; - -#ifndef HAVE_NTGUI - if (!SetStdHandle (STD_INPUT_HANDLE, stdin_save)) - report_file_error ("Resetting input handle", Qnil); - - if (!SetStdHandle (STD_OUTPUT_HANDLE, stdout_save)) - { - i = GetLastError (); - report_file_error ("Resetting output handle", Qnil); - } - - if (!SetStdHandle (STD_ERROR_HANDLE, stderr_save)) - report_file_error ("Resetting error handle", Qnil); -#endif /* !HAVE_NTGUI */ - - if (out == err) - { - /* If out and err are the same handle, then we duplicated out - and stuck it in err_handle. Close the duplicate to clean up. */ - if (!CloseHandle (err_handle)) - report_file_error ("Closing error handle duplicated from out.", - Qnil); - } -} - -int -random () -{ - /* rand () on NT gives us 15 random bits...hack together 30 bits. */ - return ((rand () << 15) | rand ()); -} - -void -srandom (int seed) -{ - srand (seed); -} - -/* Destructively turn backslashes into slashes. */ -void -dostounix_filename (p) - register char *p; -{ - while (*p) - { - if (*p == '\\') - *p = '/'; - p++; - } -} - -/* Routines that are no-ops on NT but are defined to get Emacs to compile. */ - - -int -sigsetmask (int signal_mask) -{ - return 0; -} - -int -sigblock (int sig) -{ - return 0; -} - -int -kill (int pid, int signal) -{ - return 0; -} - -int -setpgrp (int pid, int gid) -{ - return 0; -} - -int -alarm (int seconds) -{ - return 0; -} - -int -unrequest_sigio (void) -{ - return 0; -} - -int -request_sigio (void) -{ - return 0; -} - -int -getuid () -{ - char buffer[256]; - int size = 256; - - if (!GetUserName (buffer, &size)) - /* Assume all powers upon failure. */ - return 0; - - if (!stricmp ("administrator", buffer)) - return 0; - else - /* A complete fabrication...is there anything to base it on? */ - return 123; -} - -int -geteuid () -{ - /* I could imagine arguing for checking to see whether the user is - in the Administrators group and returning a UID of 0 for that - case, but I don't know how wise that would be in the long run. */ - return getuid (); -} - -/* Remove all CR's that are followed by a LF. - (From msdos.c...probably should figure out a way to share it, - although this code isn't going to ever change.) */ -int -crlf_to_lf (n, buf) - register int n; - register unsigned char *buf; -{ - unsigned char *np = buf; - unsigned char *startp = buf; - unsigned char *endp = buf + n; - - if (n == 0) - return n; - while (buf < endp - 1) - { - if (*buf == 0x0d) - { - if (*(++buf) != 0x0a) - *np++ = 0x0d; - } - else - *np++ = *buf++; - } - if (buf < endp) - *np++ = *buf++; - return np - startp; -} - -#define REG_ROOT "SOFTWARE\\GNU\\Emacs\\" - -LPBYTE -nt_get_resource (key, lpdwtype) - char *key; - LPDWORD lpdwtype; -{ - LPBYTE lpvalue; - HKEY hrootkey = NULL; - DWORD cbData; - BOOL ok = FALSE; - - /* Check both the current user and the local machine to see if - we have any resources. */ - - if (RegOpenKeyEx (HKEY_CURRENT_USER, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) - { - lpvalue = NULL; - - if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS - && (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL - && RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS) - { - return (lpvalue); - } - - if (lpvalue) xfree (lpvalue); - - RegCloseKey (hrootkey); - } - - if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, REG_ROOT, 0, KEY_READ, &hrootkey) == ERROR_SUCCESS) - { - lpvalue = NULL; - - if (RegQueryValueEx (hrootkey, key, NULL, NULL, NULL, &cbData) == ERROR_SUCCESS && - (lpvalue = (LPBYTE) xmalloc (cbData)) != NULL && - RegQueryValueEx (hrootkey, key, NULL, lpdwtype, lpvalue, &cbData) == ERROR_SUCCESS) - { - return (lpvalue); - } - - if (lpvalue) xfree (lpvalue); - - RegCloseKey (hrootkey); - } - - return (NULL); -} - -void -init_environment () -{ - /* Open a console window to display messages during dumping. */ - if (!initialized) - AllocConsole (); - - /* Check for environment variables and use registry if they don't exist */ - { - int i; - LPBYTE lpval; - DWORD dwType; - - static char * env_vars[] = - { - "emacs_path", - "EMACSLOADPATH", - "SHELL", - "EMACSDATA", - "EMACSPATH", - "EMACSLOCKDIR", - "INFOPATH", - "EMACSDOC", - "TERM", - }; - - for (i = 0; i < (sizeof (env_vars) / sizeof (env_vars[0])); i++) - { - if (!getenv (env_vars[i]) && - (lpval = nt_get_resource (env_vars[i], &dwType)) != NULL) - { - if (dwType == REG_EXPAND_SZ) - { - char buf1[500], buf2[500]; - - ExpandEnvironmentStrings ((LPSTR) lpval, buf1, 500); - _snprintf (buf2, 499, "%s=%s", env_vars[i], buf1); - putenv (strdup (buf2)); - } - else if (dwType == REG_SZ) - { - char buf[500]; - - _snprintf (buf, 499, "%s=%s", env_vars[i], lpval); - putenv (strdup (buf)); - } - - xfree (lpval); - } - } - } -} - -#ifdef HAVE_TIMEVAL #include <sys/timeb.h> /* Emulate gettimeofday (Ulrich Leodolter, 1/11/95). */ @@ -1007,44 +649,1381 @@ tz->tz_dsttime = tb.dstflag; /* type of dst correction */ } } -#endif /* HAVE_TIMEVAL */ + +/* ------------------------------------------------------------------------- */ +/* IO support and wrapper functions for Win32 API. */ +/* ------------------------------------------------------------------------- */ + +/* Place a wrapper around the MSVC version of ctime. It returns NULL + on network directories, so we handle that case here. + (Ulrich Leodolter, 1/11/95). */ +char * +sys_ctime (const time_t *t) +{ + char *str = (char *) ctime (t); + return (str ? str : "Sun Jan 01 00:00:00 1970"); +} + +/* Emulate sleep...we could have done this with a define, but that + would necessitate including windows.h in the files that used it. + This is much easier. */ +void +sys_sleep (int seconds) +{ + Sleep (seconds * 1000); +} + +/* Internal MSVC data and functions for low-level descriptor munging */ +#if (_MSC_VER == 900) +extern char _osfile[]; +#endif +extern int __cdecl _set_osfhnd (int fd, long h); +extern int __cdecl _free_osfhnd (int fd); + +/* parallel array of private info on file handles */ +filedesc fd_info [ MAXDESC ]; + +static struct { + DWORD serialnum; + DWORD maxcomp; + DWORD flags; + char name[32]; + char type[32]; +} volume_info; + +/* Get information on the volume where name is held; set path pointer to + start of pathname in name (past UNC header\volume header if present). */ +int +get_volume_info (const char * name, const char ** pPath) +{ + char temp[MAX_PATH]; + char *rootname = NULL; /* default to current volume */ + + 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])) + { + char *str = temp; + int slashes = 4; + rootname = temp; + do + { + if (IS_DIRECTORY_SEP (*name) && --slashes == 0) + break; + *str++ = *name++; + } + while ( *name ); + + if (slashes > 1) + return FALSE; + + *str++ = '\\'; + *str = 0; + } + + if (pPath) + *pPath = name; + + if (GetVolumeInformation (rootname, + volume_info.name, 32, + &volume_info.serialnum, + &volume_info.maxcomp, + &volume_info.flags, + volume_info.type, 32)) + { + return TRUE; + } + return FALSE; +} + +/* Determine if volume is FAT format (ie. only supports short 8.3 + names); also set path pointer to start of pathname in name. */ +int +is_fat_volume (const char * name, const char ** pPath) +{ + if (get_volume_info (name, pPath)) + return (volume_info.maxcomp == 12); + return FALSE; +} + +/* Map filename to a legal 8.3 name if necessary. */ +const char * +map_win32_filename (const char * name, const char ** pPath) +{ + static char shortname[MAX_PATH]; + char * str = shortname; + char c; + const char * orig_name = name; + char * path; + + if (is_fat_volume (name, &path)) /* truncate to 8.3 */ + { + register int left = 8; /* maximum number of chars in part */ + register int extn = 0; /* extension added? */ + register int dots = 2; /* maximum number of dots allowed */ + + while (name < path) + *str++ = *name++; /* skip past UNC header */ + + while ((c = *name++)) + { + switch ( c ) + { + case '\\': + case '/': + *str++ = '\\'; + extn = 0; /* reset extension flags */ + dots = 2; /* max 2 dots */ + left = 8; /* max length 8 for main part */ + break; + case ':': + *str++ = ':'; + extn = 0; /* reset extension flags */ + dots = 2; /* max 2 dots */ + left = 8; /* max length 8 for main part */ + break; + case '.': + if ( dots ) + { + /* Convert path components of the form .xxx to _xxx, + but leave . and .. as they are. This allows .emacs + to be read as _emacs, for example. */ + + if (! *name || + *name == '.' || + IS_DIRECTORY_SEP (*name)) + { + *str++ = '.'; + dots--; + } + else + { + *str++ = '_'; + left--; + dots = 0; + } + } + else if ( !extn ) + { + *str++ = '.'; + extn = 1; /* we've got an extension */ + left = 3; /* 3 chars in extension */ + } + else + { + /* any embedded dots after the first are converted to _ */ + *str++ = '_'; + } + break; + case '~': + case '#': /* don't lose these, they're important */ + if ( ! left ) + str[-1] = c; /* replace last character of part */ + /* FALLTHRU */ + default: + if ( left ) + { + *str++ = tolower (c); /* map to lower case (looks nicer) */ + left--; + dots = 0; /* started a path component */ + } + break; + } + } + *str = '\0'; + + name = shortname; + } + + if (pPath) + *pPath = name + (path - orig_name); + + return name; +} -#ifdef PIGSFLY -Keep this around...we might need it later. -#ifdef WINDOWSNT +/* 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 + long file names. */ + +int +sys_access (const char * path, int mode) +{ + return _access (map_win32_filename (path, NULL), mode); +} + +int +sys_chdir (const char * path) +{ + return _chdir (map_win32_filename (path, NULL)); +} + +int +sys_chmod (const char * path, int mode) +{ + return _chmod (map_win32_filename (path, NULL), mode); +} + +int +sys_creat (const char * path, int mode) +{ + return _creat (map_win32_filename (path, NULL), mode); +} + +FILE * +sys_fopen(const char * path, const char * mode) +{ + int fd; + int oflag; + const char * mode_save = mode; + + /* Force all file handles to be non-inheritable. This is necessary to + ensure child processes don't unwittingly inherit handles that might + prevent future file access. */ + + if (mode[0] == 'r') + oflag = O_RDONLY; + else if (mode[0] == 'w' || mode[0] == 'a') + oflag = O_WRONLY | O_CREAT | O_TRUNC; + else + return NULL; + + /* Only do simplistic option parsing. */ + while (*++mode) + if (mode[0] == '+') + { + oflag &= ~(O_RDONLY | O_WRONLY); + oflag |= O_RDWR; + } + else if (mode[0] == 'b') + { + oflag &= ~O_TEXT; + oflag |= O_BINARY; + } + else if (mode[0] == 't') + { + oflag &= ~O_BINARY; + oflag |= O_TEXT; + } + else break; + + fd = _open (map_win32_filename (path, NULL), oflag | _O_NOINHERIT, 0644); + if (fd < 0) + return NULL; + + return fdopen (fd, mode_save); +} + +int +sys_link (const char * path1, const char * path2) { - /* - * Find the user's real name by opening the process token and looking - * up the name associated with the user-sid in that token. - */ + errno = EINVAL; + return -1; +} + +int +sys_mkdir (const char * path) +{ + return _mkdir (map_win32_filename (path, NULL)); +} + +char * +sys_mktemp (char * template) +{ + return (char *) map_win32_filename ((const char *) _mktemp (template), NULL); +} + +int +sys_open (const char * path, int oflag, int mode) +{ + /* Force all file handles to be non-inheritable. */ + return _open (map_win32_filename (path, NULL), oflag | _O_NOINHERIT, mode); +} + +int +sys_rename (const char * oldname, const char * newname) +{ + char temp[MAX_PATH]; + + /* MoveFile on Win95 doesn't correctly change the short file name + alias when oldname has a three char extension and newname has the + same first three chars in its extension. To avoid problems, on + Win95 we rename to a temporary name first. */ + + strcpy (temp, map_win32_filename (oldname, NULL)); + + if (GetVersion () & 0x80000000) + { + char * p; - char b[256], Name[256], RefD[256]; - DWORD length = 256, rlength = 256, trash; - HANDLE Token; - SID_NAME_USE User; + unixtodos_filename (temp); + if (p = strrchr (temp, '\\')) + p++; + else + p = temp; + strcpy (p, "__XXXXXX"); + _mktemp (temp); + if (rename (map_win32_filename (oldname, NULL), temp) < 0) + return -1; + } + + /* Emulate Unix behaviour - newname is deleted if it already exists + (at least if it is a file; don't do this for directories). */ + newname = map_win32_filename (newname, NULL); + if (GetFileAttributes (newname) != -1) + { + _chmod (newname, 0666); + _unlink (newname); + } + + return rename (temp, newname); +} + +int +sys_rmdir (const char * path) +{ + return _rmdir (map_win32_filename (path, NULL)); +} + +int +sys_unlink (const char * path) +{ + return _unlink (map_win32_filename (path, NULL)); +} + +static FILETIME utc_base_ft; +static long double utc_base; +static int init = 0; + +static time_t +convert_time (FILETIME ft) +{ + long double ret; - if (1) - Vuser_real_login_name = build_string ("foo"); - else if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &Token)) + if (!init) { - Vuser_real_login_name = build_string ("unknown"); + /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ + SYSTEMTIME st; + + st.wYear = 1970; + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + + SystemTimeToFileTime (&st, &utc_base_ft); + utc_base = (long double) utc_base_ft.dwHighDateTime + * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; + init = 1; } - else if (!GetTokenInformation (Token, TokenUser, (PVOID)b, 256, - &trash)) + + if (CompareFileTime (&ft, &utc_base_ft) < 0) + return 0; + + ret = (long double) ft.dwHighDateTime * 4096 * 1024 * 1024 + ft.dwLowDateTime; + ret -= utc_base; + return (time_t) (ret * 1e-7); +} + +#if 0 +/* in case we ever have need of this */ +void +convert_from_time_t (time_t time, FILETIME * pft) +{ + long double tmp; + + if (!init) + { + /* Determine the delta between 1-Jan-1601 and 1-Jan-1970. */ + SYSTEMTIME st; + + st.wYear = 1970; + st.wMonth = 1; + st.wDay = 1; + st.wHour = 0; + st.wMinute = 0; + st.wSecond = 0; + st.wMilliseconds = 0; + + SystemTimeToFileTime (&st, &utc_base_ft); + utc_base = (long double) utc_base_ft.dwHighDateTime + * 4096 * 1024 * 1024 + utc_base_ft.dwLowDateTime; + init = 1; + } + + /* time in 100ns units since 1-Jan-1601 */ + tmp = (long double) time * 1e7 + utc_base; + pft->dwHighDateTime = (DWORD) (tmp / (4096.0 * 1024 * 1024)); + pft->dwLowDateTime = (DWORD) (tmp - pft->dwHighDateTime); +} +#endif + +/* "PJW" algorithm (see the "Dragon" compiler book). */ +static unsigned +hashval (const char * str) +{ + unsigned h = 0; + unsigned g; + while (*str) { - CloseHandle (Token); - Vuser_real_login_name = build_string ("unknown"); + h = (h << 4) + *str++; + if ((g = h & 0xf0000000) != 0) + h = (h ^ (g >> 24)) & 0x0fffffff; + } + return h; +} + +/* Return the hash value of the canonical pathname, excluding the + drive/UNC header, to get a hopefully unique inode number. */ +static _ino_t +generate_inode_val (const char * name) +{ + char fullname[ MAX_PATH ]; + char * p; + unsigned hash; + + GetFullPathName (name, sizeof (fullname), fullname, &p); + get_volume_info (fullname, &p); + /* Normal Win32 filesystems are still case insensitive. */ + _strlwr (p); + hash = hashval (p); + return (_ino_t) (hash ^ (hash >> 16)); +} + +/* MSVC stat function can't cope with UNC names and has other bugs, so + replace it with our own. This also allows us to calculate consistent + inode values without hacks in the main Emacs code. */ +int +stat (const char * path, struct stat * buf) +{ + char * name; + WIN32_FIND_DATA wfd; + HANDLE fh; + int permission; + int len; + int rootdir = FALSE; + + if (path == NULL || buf == NULL) + { + errno = EFAULT; + return -1; } - else if (!LookupAccountSid ((void *)0, (PSID)b, Name, &length, RefD, - &rlength, &User)) + + name = (char *) map_win32_filename (path, &path); + /* must be valid filename, no wild cards */ + if (strchr (name, '*') || strchr (name, '?')) + { + errno = ENOENT; + return -1; + } + + /* Remove trailing directory separator, unless name is the root + directory of a drive or UNC volume in which case ensure there + is a trailing separator. */ + len = strlen (name); + rootdir = (path >= name + len - 1 + && (IS_DIRECTORY_SEP (*path) || *path == 0)); + name = strcpy (alloca (len + 2), name); + + if (rootdir) { - CloseHandle (Token); - Vuser_real_login_name = build_string ("unknown"); + if (!IS_DIRECTORY_SEP (name[len-1])) + strcat (name, "\\"); + if (GetDriveType (name) < 2) + { + errno = ENOENT; + return -1; + } + memset (&wfd, 0, sizeof (wfd)); + wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY; + wfd.ftCreationTime = utc_base_ft; + wfd.ftLastAccessTime = utc_base_ft; + wfd.ftLastWriteTime = utc_base_ft; + strcpy (wfd.cFileName, name); + } + else + { + if (IS_DIRECTORY_SEP (name[len-1])) + name[len - 1] = 0; + fh = FindFirstFile (name, &wfd); + if (fh == INVALID_HANDLE_VALUE) + { + errno = ENOENT; + return -1; + } + FindClose (fh); + } + + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + buf->st_mode = _S_IFDIR; + buf->st_nlink = 2; /* doesn't really matter */ } else - Vuser_real_login_name = build_string (Name); + { +#if 0 + /* This is more accurate in terms of gettting the correct number + of links, but is quite slow (it is noticable when Emacs is + making a list of file name completions). */ + BY_HANDLE_FILE_INFORMATION info; + + fh = CreateFile (name, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (GetFileInformationByHandle (fh, &info)) + { + 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; + } + buf->st_nlink = info.nNumberOfLinks; + /* Could use file index, but this is not guaranteed to be + unique unless we keep a handle open all the time. */ + /* buf->st_ino = info.nFileIndexLow ^ info.nFileIndexHigh; */ + CloseHandle (fh); + } + else + { + errno = EACCES; + return -1; + } +#else + buf->st_mode = _S_IFREG; + buf->st_nlink = 1; +#endif + } + + /* consider files to belong to current user */ + buf->st_uid = the_passwd.pw_uid; + buf->st_gid = the_passwd.pw_gid; + + /* volume_info is set indirectly by map_win32_filename */ + buf->st_dev = volume_info.serialnum; + buf->st_rdev = volume_info.serialnum; + + buf->st_ino = generate_inode_val (name); + + buf->st_size = wfd.nFileSizeLow; + + /* Convert timestamps to Unix format. */ + buf->st_mtime = convert_time (wfd.ftLastWriteTime); + buf->st_atime = convert_time (wfd.ftLastAccessTime); + if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; + buf->st_ctime = convert_time (wfd.ftCreationTime); + if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; + + /* determine rwx permissions */ + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + permission = _S_IREAD; + else + permission = _S_IREAD | _S_IWRITE; + + if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + permission |= _S_IEXEC; + else + { + char * p = strrchr (name, '.'); + if (p != NULL && + (stricmp (p, ".exe") == 0 || + stricmp (p, ".com") == 0 || + stricmp (p, ".bat") == 0 || + stricmp (p, ".cmd") == 0)) + permission |= _S_IEXEC; + } + + buf->st_mode |= permission | (permission >> 3) | (permission >> 6); + + return 0; +} + +#ifdef HAVE_SOCKETS + +/* Wrappers for winsock functions to map between our file descriptors + and winsock's handles; also set h_errno for convenience. + + To allow Emacs to run on systems which don't have winsock support + installed, we dynamically link to winsock on startup if present, and + otherwise provide the minimum necessary functionality + (eg. gethostname). */ + +/* function pointers for relevant socket functions */ +int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData); +void (PASCAL *pfn_WSASetLastError) (int iError); +int (PASCAL *pfn_WSAGetLastError) (void); +int (PASCAL *pfn_socket) (int af, int type, int protocol); +int (PASCAL *pfn_bind) (SOCKET s, const struct sockaddr *addr, int namelen); +int (PASCAL *pfn_connect) (SOCKET s, const struct sockaddr *addr, int namelen); +int (PASCAL *pfn_ioctlsocket) (SOCKET s, long cmd, u_long *argp); +int (PASCAL *pfn_recv) (SOCKET s, char * buf, int len, int flags); +int (PASCAL *pfn_send) (SOCKET s, const char * buf, int len, int flags); +int (PASCAL *pfn_closesocket) (SOCKET s); +int (PASCAL *pfn_shutdown) (SOCKET s, int how); +int (PASCAL *pfn_WSACleanup) (void); + +u_short (PASCAL *pfn_htons) (u_short hostshort); +u_short (PASCAL *pfn_ntohs) (u_short netshort); +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); + +static int have_winsock; +static HANDLE winsock_lib; + +static void +term_winsock (void) +{ + if (have_winsock) + { + pfn_WSACleanup (); + FreeLibrary (winsock_lib); + } +} + +static void +init_winsock () +{ + WSADATA winsockData; + + winsock_lib = LoadLibrary ("wsock32.dll"); + + if (winsock_lib != NULL) + { + /* dynamically link to socket functions */ + +#define LOAD_PROC(fn) \ + if ((pfn_##fn = (void *) GetProcAddress (winsock_lib, #fn)) == NULL) \ + goto fail; + + LOAD_PROC( WSAStartup ); + LOAD_PROC( WSASetLastError ); + LOAD_PROC( WSAGetLastError ); + LOAD_PROC( socket ); + LOAD_PROC( bind ); + LOAD_PROC( connect ); + LOAD_PROC( ioctlsocket ); + LOAD_PROC( recv ); + LOAD_PROC( send ); + LOAD_PROC( closesocket ); + LOAD_PROC( shutdown ); + LOAD_PROC( htons ); + LOAD_PROC( ntohs ); + LOAD_PROC( inet_addr ); + LOAD_PROC( gethostname ); + LOAD_PROC( gethostbyname ); + LOAD_PROC( getservbyname ); + LOAD_PROC( WSACleanup ); + + /* specify version 1.1 of winsock */ + if (pfn_WSAStartup (0x101, &winsockData) == 0) + { + have_winsock = TRUE; + return; + } + + fail: + FreeLibrary (winsock_lib); + } + have_winsock = FALSE; +} + + +int h_errno = 0; + +/* function to set h_errno for compatability; map winsock error codes to + normal system codes where they overlap (non-overlapping definitions + are already in <sys/socket.h> */ +static void set_errno () +{ + if (!have_winsock) + h_errno = EINVAL; + else + h_errno = pfn_WSAGetLastError (); + + switch (h_errno) + { + case WSAEACCES: h_errno = EACCES; break; + case WSAEBADF: h_errno = EBADF; break; + case WSAEFAULT: h_errno = EFAULT; break; + case WSAEINTR: h_errno = EINTR; break; + case WSAEINVAL: h_errno = EINVAL; break; + case WSAEMFILE: h_errno = EMFILE; break; + case WSAENAMETOOLONG: h_errno = ENAMETOOLONG; break; + case WSAENOTEMPTY: h_errno = ENOTEMPTY; break; + } + errno = h_errno; +} + +static void check_errno () +{ + if (h_errno == 0 && have_winsock) + pfn_WSASetLastError (0); +} + +/* [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. */ + +//#define SOCK_REPLACE_HANDLE + +#ifdef SOCK_REPLACE_HANDLE +#define SOCK_HANDLE(fd) ((SOCKET) _get_osfhandle (fd)) +#else +#define SOCK_HANDLE(fd) ((SOCKET) fd_info[fd].hnd) +#endif + +int +sys_socket(int af, int type, int protocol) +{ + int fd; + long s; + child_process * cp; + + if (!have_winsock) + { + h_errno = ENETDOWN; + return INVALID_SOCKET; + } + + check_errno (); + + /* call the real socket function */ + s = (long) 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. */ + + /* 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); */ +#else + /* Make a non-inheritable copy of the socket handle. */ + { + HANDLE parent; + HANDLE new_s = INVALID_HANDLE_VALUE; + + parent = GetCurrentProcess (); + + DuplicateHandle (parent, + (HANDLE) s, + parent, + &new_s, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + pfn_closesocket (s); + fd_info[fd].hnd = new_s; + s = (SOCKET) new_s; + } +#endif + + /* 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; + + /* success! */ + return fd; + } + + /* clean up */ + _close (fd); + } + pfn_closesocket (s); + h_errno = EMFILE; + } + set_errno (); + + return -1; +} + + +int +sys_bind (int s, const struct sockaddr * addr, int namelen) +{ + if (!have_winsock) + { + h_errno = ENOTSOCK; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_bind (SOCK_HANDLE (s), addr, namelen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + + +int +sys_connect (int s, const struct sockaddr * name, int namelen) +{ + if (!have_winsock) + { + h_errno = ENOTSOCK; + return SOCKET_ERROR; + } + + check_errno (); + if (fd_info[s].flags & FILE_SOCKET) + { + int rc = pfn_connect (SOCK_HANDLE (s), name, namelen); + if (rc == SOCKET_ERROR) + set_errno (); + return rc; + } + h_errno = ENOTSOCK; + return SOCKET_ERROR; +} + +u_short +sys_htons (u_short hostshort) +{ + return (have_winsock) ? + pfn_htons (hostshort) : hostshort; +} + +u_short +sys_ntohs (u_short netshort) +{ + return (have_winsock) ? + pfn_ntohs (netshort) : netshort; +} + +unsigned long +sys_inet_addr (const char * cp) +{ + return (have_winsock) ? + pfn_inet_addr (cp) : INADDR_NONE; +} + +int +sys_gethostname (char * name, int namelen) +{ + if (have_winsock) + return pfn_gethostname (name, namelen); + + if (namelen > MAX_COMPUTERNAME_LENGTH) + return !GetComputerName (name, &namelen); + + h_errno = EFAULT; + return SOCKET_ERROR; +} + +struct hostent * +sys_gethostbyname(const char * name) +{ + struct hostent * host; + + if (!have_winsock) + { + h_errno = ENETDOWN; + return NULL; + } + + check_errno (); + host = pfn_gethostbyname (name); + if (!host) + set_errno (); + return host; +} + +struct servent * +sys_getservbyname(const char * name, const char * proto) +{ + struct servent * serv; + + if (!have_winsock) + { + h_errno = ENETDOWN; + return NULL; + } + + check_errno (); + serv = pfn_getservbyname (name, proto); + if (!serv) + set_errno (); + return serv; } -#else /* not WINDOWSNT */ -#endif /* not WINDOWSNT */ -#endif /* PIGSFLY */ + +#endif /* HAVE_SOCKETS */ + + +/* Shadow main io functions: we need to handle pipes and sockets more + intelligently, and implement non-blocking mode as well. */ + +int +sys_close (int fd) +{ + int rc; + + if (fd < 0 || fd >= MAXDESC) + { + errno = EBADF; + return -1; + } + + if (fd_info[fd].cp) + { + child_process * cp = fd_info[fd].cp; + + fd_info[fd].cp = NULL; + + if (CHILD_ACTIVE (cp)) + { + /* if last descriptor to active child_process then cleanup */ + int i; + for (i = 0; i < MAXDESC; i++) + { + if (i == fd) + continue; + if (fd_info[i].cp == cp) + break; + } + if (i == MAXDESC) + { +#if defined (HAVE_SOCKETS) && !defined (SOCK_REPLACE_HANDLE) + if (fd_info[fd].flags & FILE_SOCKET) + { + if (!have_winsock) abort (); + + pfn_shutdown (SOCK_HANDLE (fd), 2); + rc = pfn_closesocket (SOCK_HANDLE (fd)); + } +#endif + delete_child (cp); + } + } + } + + /* Note that sockets do not need special treatment here (at least on + NT and Win95 using the standard tcp/ip stacks) - it appears that + closesocket is equivalent to CloseHandle, which is to be expected + because socket handles are fully fledged kernel handles. */ + rc = _close (fd); + + if (rc == 0) + fd_info[fd].flags = 0; + + return rc; +} + +int +sys_dup (int fd) +{ + int new_fd; + + new_fd = _dup (fd); + if (new_fd >= 0) + { + /* duplicate our internal info as well */ + fd_info[new_fd] = fd_info[fd]; + } + return new_fd; +} + + +int +sys_dup2 (int src, int dst) +{ + int rc; + + if (dst < 0 || dst >= MAXDESC) + { + errno = EBADF; + return -1; + } + + /* make sure we close the destination first if it's a pipe or socket */ + if (src != dst && fd_info[dst].flags != 0) + sys_close (dst); + + rc = _dup2 (src, dst); + if (rc == 0) + { + /* duplicate our internal info as well */ + fd_info[dst] = fd_info[src]; + } + return rc; +} + +/* From callproc.c */ +extern Lisp_Object Vbinary_process_input; +extern Lisp_Object Vbinary_process_output; + +/* Unix pipe() has only one arg */ +int +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. */ + rc = _pipe (phandles, 0, _O_NOINHERIT); + + if (rc == 0) + { + /* set internal flags, and put read and write handles into binary + mode as necessary; if not in binary mode, set the MSVC internal + FDEV (0x40) flag to prevent _read from treating ^Z as eof (this + could otherwise allow Emacs to hang because it then waits + indefinitely for the child process to exit, when it might not be + finished). */ + flags = FILE_PIPE | FILE_READ; + if (!NILP (Vbinary_process_output)) + { + flags |= FILE_BINARY; + setmode (phandles[0], _O_BINARY); + } +#if (_MSC_VER == 900) + else + _osfile[phandles[0]] |= 0x40; +#endif + + fd_info[phandles[0]].flags = flags; + + flags = FILE_PIPE | FILE_WRITE; + if (!NILP (Vbinary_process_input)) + { + flags |= FILE_BINARY; + setmode (phandles[1], _O_BINARY); + } +#if (_MSC_VER == 900) + else + _osfile[phandles[1]] |= 0x40; +#endif + + fd_info[phandles[1]].flags = flags; + } + + return rc; +} + +/* Function to do blocking read of one byte, needed to implement + select. It is only allowed on sockets and pipes. */ +int +_sys_read_ahead (int fd) +{ + child_process * cp; + int rc; + + if (fd < 0 || fd >= MAXDESC) + return STATUS_READ_ERROR; + + cp = fd_info[fd].cp; + + if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) + return STATUS_READ_ERROR; + + if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0 + || (fd_info[fd].flags & FILE_READ) == 0) + { + DebPrint (("_sys_read_ahead: internal error: fd %d is not a pipe or socket!\n", fd)); + abort (); + } + + cp->status = STATUS_READ_IN_PROGRESS; + + if (fd_info[fd].flags & FILE_PIPE) + /* Use read to get CRLF translation */ + rc = _read (fd, &cp->chr, sizeof (char)); +#ifdef HAVE_SOCKETS + else if (fd_info[fd].flags & FILE_SOCKET) + rc = pfn_recv (SOCK_HANDLE (fd), &cp->chr, sizeof (char), 0); +#endif + + if (rc == sizeof (char)) + cp->status = STATUS_READ_SUCCEEDED; + else + cp->status = STATUS_READ_FAILED; + + return cp->status; +} + +int +sys_read (int fd, char * buffer, unsigned int count) +{ + int nchars; + int extra = 0; + int to_read; + DWORD waiting; + + if (fd < 0 || fd >= MAXDESC) + { + errno = EBADF; + return -1; + } + + if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + { + child_process *cp = fd_info[fd].cp; + + if ((fd_info[fd].flags & FILE_READ) == 0) + { + errno = EBADF; + return -1; + } + + /* presence of a child_process structure means we are operating in + non-blocking mode - otherwise we just call _read directly. + Note that the child_process structure might be missing because + reap_subprocess has been called; in this case the pipe is + already broken, so calling _read on it is okay. */ + if (cp) + { + int current_status = cp->status; + + switch (current_status) + { + case STATUS_READ_FAILED: + case STATUS_READ_ERROR: + /* report normal EOF */ + return 0; + + case STATUS_READ_READY: + case STATUS_READ_IN_PROGRESS: + DebPrint (("sys_read called when read is in progress\n")); + errno = EWOULDBLOCK; + return -1; + + case STATUS_READ_SUCCEEDED: + /* consume read-ahead char */ + *buffer++ = cp->chr; + count--; + extra = 1; + cp->status = STATUS_READ_ACKNOWLEDGED; + ResetEvent (cp->char_avail); + + case STATUS_READ_ACKNOWLEDGED: + break; + + default: + DebPrint (("sys_read: bad status %d\n", current_status)); + errno = EBADF; + return -1; + } + + if (fd_info[fd].flags & FILE_PIPE) + { + PeekNamedPipe ((HANDLE) _get_osfhandle (fd), NULL, 0, NULL, &waiting, NULL); + to_read = min (waiting, (DWORD) count); + + /* Use read to get CRLF translation */ + nchars = _read (fd, buffer, to_read); + } +#ifdef HAVE_SOCKETS + else /* FILE_SOCKET */ + { + if (!have_winsock) abort (); + + /* do the equivalent of a non-blocking read */ + pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting); + if (waiting == 0 && extra == 0) + { + h_errno = errno = EWOULDBLOCK; + return -1; + } + + nchars = 0; + if (waiting) + { + /* always use binary mode for sockets */ + nchars = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0); + if (nchars == SOCKET_ERROR) + { + DebPrint(("sys_read.recv failed with error %d on socket %ld\n", + pfn_WSAGetLastError (), SOCK_HANDLE (fd))); + if (extra == 0) + { + set_errno (); + return -1; + } + nchars = 0; + } + } + } +#endif + } + else + nchars = _read (fd, buffer, count); + } + else + nchars = _read (fd, buffer, count); + + return nchars + extra; +} + +/* For now, don't bother with a non-blocking mode */ +int +sys_write (int fd, const void * buffer, unsigned int count) +{ + int nchars; + + if (fd < 0 || fd >= MAXDESC) + { + errno = EBADF; + return -1; + } + + if (fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) + if ((fd_info[fd].flags & FILE_WRITE) == 0) + { + errno = EBADF; + return -1; + } +#ifdef HAVE_SOCKETS + if (fd_info[fd].flags & FILE_SOCKET) + { + if (!have_winsock) abort (); + nchars = pfn_send (SOCK_HANDLE (fd), buffer, count, 0); + if (nchars == SOCKET_ERROR) + { + DebPrint(("sys_read.send failed with error %d on socket %ld\n", + pfn_WSAGetLastError (), SOCK_HANDLE (fd))); + set_errno (); + } + } + else +#endif + nchars = _write (fd, buffer, count); + + return nchars; +} + + +void +term_ntproc () +{ +#ifdef HAVE_SOCKETS + /* shutdown the socket interface if necessary */ + term_winsock (); +#endif +} + +void +init_ntproc () +{ +#ifdef HAVE_SOCKETS + /* initialise the socket interface if available */ + init_winsock (); +#endif + + /* Initial preparation for subprocess support: replace our standard + handles with non-inheritable versions. */ + { + HANDLE parent; + HANDLE stdin_save = INVALID_HANDLE_VALUE; + HANDLE stdout_save = INVALID_HANDLE_VALUE; + HANDLE stderr_save = INVALID_HANDLE_VALUE; + + parent = GetCurrentProcess (); + + /* ignore errors when duplicating and closing; typically the + handles will be invalid when running as a gui program. */ + DuplicateHandle (parent, + GetStdHandle (STD_INPUT_HANDLE), + parent, + &stdin_save, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + DuplicateHandle (parent, + GetStdHandle (STD_OUTPUT_HANDLE), + parent, + &stdout_save, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + DuplicateHandle (parent, + GetStdHandle (STD_ERROR_HANDLE), + parent, + &stderr_save, + 0, + FALSE, + DUPLICATE_SAME_ACCESS); + + fclose (stdin); + fclose (stdout); + fclose (stderr); + + if (stdin_save != INVALID_HANDLE_VALUE) + _open_osfhandle ((long) stdin_save, O_TEXT); + else + open ("nul", O_TEXT | O_NOINHERIT | O_RDONLY); + fdopen (0, "r"); + + if (stdout_save != INVALID_HANDLE_VALUE) + _open_osfhandle ((long) stdout_save, O_TEXT); + else + open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); + fdopen (1, "w"); + + if (stderr_save != INVALID_HANDLE_VALUE) + _open_osfhandle ((long) stderr_save, O_TEXT); + else + open ("nul", O_TEXT | O_NOINHERIT | O_WRONLY); + fdopen (2, "w"); + } + + /* unfortunately, atexit depends on implementation of malloc */ + /* atexit (term_ntproc); */ + signal (SIGABRT, term_ntproc); +} + +/* end of nt.c */