/* Utility and Unix shadow routines for GNU Emacs support programs on NT. Copyright (C) 1994, 2001-2021 Free Software Foundation, Inc. Author: Geoff Voelker (voelker@cs.washington.edu) Created: 10-8-94 This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ntlib.h" char *sys_ctime (const time_t *); FILE *sys_fopen (const char *, const char *); int sys_mkdir (const char *, mode_t); int sys_chdir (const char *); int mkostemp (char *, int); int sys_rename (const char *, const char *); int sys_open (const char *, int, int); /* MinGW64 defines _TIMEZONE_DEFINED and defines 'struct timespec' in its system headers. */ #ifndef _TIMEZONE_DEFINED struct timezone { int tz_minuteswest; /* minutes west of Greenwich */ int tz_dsttime; /* type of dst correction */ }; #endif #define MAXPATHLEN _MAX_PATH /* 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. */ unsigned sleep (unsigned seconds) { Sleep (seconds * 1000); return 0; } /* Get the current working directory. */ char * getwd (char *dir) { if (GetCurrentDirectory (MAXPATHLEN, dir) > 0) return dir; return NULL; } static HANDLE getppid_parent; static int getppid_ppid; int getppid (void) { char *ppid; DWORD result; ppid = getenv ("EM_PARENT_PROCESS_ID"); if (!ppid) { printf ("no pid.\n"); return 0; } else { getppid_ppid = atoi (ppid); } if (!getppid_parent) { getppid_parent = OpenProcess (SYNCHRONIZE, FALSE, atoi (ppid)); if (!getppid_parent) { printf ("Failed to open handle to parent process: %lu\n", GetLastError ()); exit (1); } } result = WaitForSingleObject (getppid_parent, 0); switch (result) { case WAIT_TIMEOUT: /* The parent is still alive. */ return getppid_ppid; case WAIT_OBJECT_0: /* The parent is gone. Return the pid of Unix init (1). */ return 1; case WAIT_FAILED: default: printf ("Checking parent status failed: %lu\n", GetLastError ()); exit (1); } } char * getlogin (void) { static char user_name[256]; DWORD length = sizeof (user_name); if (GetUserName (user_name, &length)) return user_name; return NULL; } char * cuserid (char * s) { char * name = getlogin (); if (s) return strcpy (s, name ? name : ""); return name; } unsigned getuid (void) { return 0; } unsigned geteuid (void) { return getuid (); } unsigned getgid (void) { return 0; } unsigned getegid (void) { return 0; } int setuid (unsigned uid) { return 0; } int setregid (unsigned rgid, unsigned gid) { return 0; } struct passwd * getpwuid (unsigned uid) { return NULL; } char * getpass (const char * prompt) { static char input[256]; HANDLE in; HANDLE err; DWORD count; in = GetStdHandle (STD_INPUT_HANDLE); err = GetStdHandle (STD_ERROR_HANDLE); if (in == INVALID_HANDLE_VALUE || err == INVALID_HANDLE_VALUE) return NULL; if (WriteFile (err, prompt, strlen (prompt), &count, NULL)) { int istty = (GetFileType (in) == FILE_TYPE_CHAR); DWORD old_flags; int rc; if (istty) { if (GetConsoleMode (in, &old_flags)) SetConsoleMode (in, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); else istty = 0; } rc = ReadFile (in, input, sizeof (input), &count, NULL); if (count >= 2 && input[count - 2] == '\r') input[count - 2] = '\0'; else { char buf[256]; while (ReadFile (in, buf, sizeof (buf), &count, NULL) > 0) if (count >= 2 && buf[count - 2] == '\r') break; } WriteFile (err, "\r\n", 2, &count, NULL); if (istty) SetConsoleMode (in, old_flags); if (rc) return input; } return NULL; } int fchown (int fd, unsigned uid, unsigned gid) { return 0; } FILE * sys_fopen (const char * path, const char * mode) { return fopen (path, mode); } int sys_chdir (const char * path) { return _chdir (path); } int sys_mkdir (const char * path, mode_t mode) { return _mkdir (path); } 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 (CompareFileTime (&ft, &utc_base_ft) < 0) return 0; ret = (long double) ft.dwHighDateTime * 4096.0L * 1024.0L * 1024.0L + ft.dwLowDateTime; ret -= utc_base; return (time_t) (ret * 1e-7L); } static int is_exec (const char * name) { char * p = strrchr (name, '.'); return (p != NULL && (stricmp (p, ".exe") == 0 || stricmp (p, ".com") == 0 || stricmp (p, ".bat") == 0 || stricmp (p, ".cmd") == 0)); } /* FIXME? This is in configure.ac now - is this still needed? */ #define IS_DIRECTORY_SEP(x) ((x) == '/' || (x) == '\\') /* We need stat/fsfat below because nt/inc/sys/stat.h defines struct stat that is incompatible with the MS run-time libraries. */ int stat (const char * path, struct stat * buf) { WIN32_FIND_DATA wfd; HANDLE fh; int permission; int len; int rootdir = FALSE; char *name = alloca (FILENAME_MAX); 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.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime; init = 1; } if (path == NULL || buf == NULL || *path == '\0') { errno = EFAULT; return -1; } if (_mbspbrk (path, "*?|<>\"")) { errno = ENOENT; return -1; } strcpy (name, path); /* Remove trailing directory separator, unless name is the root directory of a drive in which case ensure there is a trailing separator. */ len = strlen (name); rootdir = IS_DIRECTORY_SEP (name[0]) || (len == 3 && name[1] == ':' && IS_DIRECTORY_SEP (name[2])); if (rootdir) { 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); } buf->st_mode = (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? S_IFDIR : S_IFREG; buf->st_nlink = 1; buf->st_ino = 0; if (name[0] && name[1] == ':') buf->st_dev = tolower (name[0]) - 'a' + 1; else buf->st_dev = _getdrive (); buf->st_rdev = buf->st_dev; buf->st_size = wfd.nFileSizeHigh; buf->st_size <<= 32; 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 if (is_exec (name)) permission |= S_IEXEC; buf->st_mode |= permission | (permission >> 3) | (permission >> 6); return 0; } int lstat (const char * path, struct stat * buf) { return stat (path, buf); } int fstat (int desc, struct stat * buf) { HANDLE fh = (HANDLE) _get_osfhandle (desc); BY_HANDLE_FILE_INFORMATION info; unsigned __int64 fake_inode; int permission; 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.0L * 1024.0L * 1024.0L + utc_base_ft.dwLowDateTime; init = 1; } switch (GetFileType (fh) & ~FILE_TYPE_REMOTE) { case FILE_TYPE_DISK: buf->st_mode = S_IFREG; if (!GetFileInformationByHandle (fh, &info)) { errno = EACCES; return -1; } break; case FILE_TYPE_PIPE: buf->st_mode = S_IFIFO; goto non_disk; case FILE_TYPE_CHAR: case FILE_TYPE_UNKNOWN: default: buf->st_mode = S_IFCHR; non_disk: memset (&info, 0, sizeof (info)); info.dwFileAttributes = 0; info.ftCreationTime = utc_base_ft; info.ftLastAccessTime = utc_base_ft; info.ftLastWriteTime = utc_base_ft; } if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) buf->st_mode = S_IFDIR; 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. */ fake_inode = info.nFileIndexHigh; fake_inode <<= 32; fake_inode += info.nFileIndexLow; buf->st_ino = fake_inode; buf->st_dev = info.dwVolumeSerialNumber; buf->st_rdev = info.dwVolumeSerialNumber; buf->st_size = info.nFileSizeHigh; buf->st_size <<= 32; buf->st_size += info.nFileSizeLow; /* Convert timestamps to Unix format. */ buf->st_mtime = convert_time (info.ftLastWriteTime); buf->st_atime = convert_time (info.ftLastAccessTime); if (buf->st_atime == 0) buf->st_atime = buf->st_mtime; buf->st_ctime = convert_time (info.ftCreationTime); if (buf->st_ctime == 0) buf->st_ctime = buf->st_mtime; /* determine rwx permissions */ if (info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) permission = S_IREAD; else permission = S_IREAD | S_IWRITE; if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) permission |= S_IEXEC; buf->st_mode |= permission | (permission >> 3) | (permission >> 6); return 0; } /* On Windows, you cannot rename into an existing file. */ int sys_rename (const char *from, const char *to) { int retval = rename (from, to); if (retval < 0 && errno == EEXIST) { if (unlink (to) == 0) retval = rename (from, to); } return retval; } int sys_open (const char * path, int oflag, int mode) { return _open (path, oflag, mode); } /* Emulation of nl_langinfo that supports only CODESET. Used in Gnulib regex.c. */ char * nl_langinfo (nl_item item) { switch (item) { case CODESET: { /* Shamelessly stolen from Gnulib's nl_langinfo.c, modulo CPP directives. */ static char buf[2 + 10 + 1]; char const *locale = setlocale (LC_CTYPE, NULL); char *codeset = buf; size_t codesetlen; codeset[0] = '\0'; if (locale && locale[0]) { /* If the locale name contains an encoding after the dot, return it. */ char *dot = strchr (locale, '.'); if (dot) { /* Look for the possible @... trailer and remove it, if any. */ char *codeset_start = dot + 1; char const *modifier = strchr (codeset_start, '@'); if (! modifier) codeset = codeset_start; else { codesetlen = modifier - codeset_start; if (codesetlen < sizeof buf) { codeset = memcpy (buf, codeset_start, codesetlen); codeset[codesetlen] = '\0'; } } } } /* If setlocale is successful, it returns the number of the codepage, as a string. Otherwise, fall back on Windows API GetACP, which returns the locale's codepage as a number (although this doesn't change according to what the 'setlocale' call specified). Either way, prepend "CP" to make it a valid codeset name. */ codesetlen = strlen (codeset); if (0 < codesetlen && codesetlen < sizeof buf - 2) memmove (buf + 2, codeset, codesetlen + 1); else sprintf (buf + 2, "%u", GetACP ()); codeset = memcpy (buf, "CP", 2); return codeset; } default: return (char *) ""; } }