summaryrefslogtreecommitdiff
path: root/lib-src/ntlib.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib-src/ntlib.c')
-rw-r--r--lib-src/ntlib.c167
1 files changed, 164 insertions, 3 deletions
diff --git a/lib-src/ntlib.c b/lib-src/ntlib.c
index 5e1742a3174..d992f54a0fa 100644
--- a/lib-src/ntlib.c
+++ b/lib-src/ntlib.c
@@ -31,6 +31,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <ctype.h>
#include <sys/timeb.h>
#include <mbstring.h>
+#include <locale.h>
+
+#include <nl_types.h>
+#include <langinfo.h>
#include "ntlib.h"
@@ -286,8 +290,8 @@ is_exec (const char * name)
/* FIXME? This is in configure.ac now - is this still needed? */
#define IS_DIRECTORY_SEP(x) ((x) == '/' || (x) == '\\')
-/* We need this because nt/inc/sys/stat.h defines struct stat that is
- incompatible with the MS run-time libraries. */
+/* 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)
{
@@ -373,7 +377,9 @@ stat (const char * path, struct stat * buf)
buf->st_dev = _getdrive ();
buf->st_rdev = buf->st_dev;
- buf->st_size = wfd.nFileSizeLow;
+ 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);
@@ -404,6 +410,98 @@ 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)
@@ -423,3 +521,66 @@ 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 *) "";
+ }
+}