/* Utility and Unix shadow routines for GNU Emacs on the Microsoft Windows API.
- Copyright (C) 1994-1995, 2000-2012 Free Software Foundation, Inc.
+ Copyright (C) 1994-1995, 2000-2013 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include <ctype.h>
#include <signal.h>
#include <sys/file.h>
+#include <time.h> /* must be before nt/inc/sys/time.h, for MinGW64 */
#include <sys/time.h>
#include <sys/utime.h>
#include <math.h>
-#include <time.h>
/* must include CRT headers *before* config.h */
#include <config.h>
-#include <mbstring.h> /* for _mbspbrk */
+#include <mbstring.h> /* for _mbspbrk, _mbslwr, _mbsrchr, ... */
#undef access
#undef chdir
#include <pwd.h>
#include <grp.h>
-#ifdef __GNUC__
+/* MinGW64 (_W64) defines these in its _mingw.h. */
+#if defined(__GNUC__) && !defined(_W64)
#define _ANONYMOUS_UNION
#define _ANONYMOUS_STRUCT
#endif
#ifndef _MSC_VER
#include <w32api.h>
#endif
+#if _WIN32_WINNT < 0x0500
#if !defined (__MINGW32__) || __W32API_MAJOR_VERSION < 3 || (__W32API_MAJOR_VERSION == 3 && __W32API_MINOR_VERSION < 15)
/* This either is not in psapi.h or guarded by higher value of
_WIN32_WINNT than what we use. w32api supplied with MinGW 3.15
SIZE_T PrivateUsage;
} PROCESS_MEMORY_COUNTERS_EX,*PPROCESS_MEMORY_COUNTERS_EX;
#endif
+#endif
#include <winioctl.h>
#include <aclapi.h>
#define SDDL_REVISION_1 1
#endif /* SDDL_REVISION_1 */
-#ifdef _MSC_VER
-/* MSVC doesn't provide the definition of REPARSE_DATA_BUFFER and the
- associated macros, except on ntifs.h, which cannot be included
- because it triggers conflicts with other Windows API headers. So
- we define it here by hand. */
+#if defined(_MSC_VER) || defined(_W64)
+/* MSVC and MinGW64 don't provide the definition of
+ REPARSE_DATA_BUFFER and the associated macros, except on ntifs.h,
+ which cannot be included because it triggers conflicts with other
+ Windows API headers. So we define it here by hand. */
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
#ifndef CTL_CODE
#define CTL_CODE(t,f,m,a) (((t)<<16)|((a)<<14)|((f)<<2)|(m))
#endif
+/* MinGW64 defines FSCTL_GET_REPARSE_POINT on winioctl.h. */
+#ifndef FSCTL_GET_REPARSE_POINT
#define FSCTL_GET_REPARSE_POINT \
CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
#endif
+#endif
/* TCP connection support. */
#include <sys/socket.h>
srand (seed);
}
+/* Current codepage for encoding file names. */
+static int file_name_codepage;
+
+/* Return the maximum length in bytes of a multibyte character
+ sequence encoded in the current ANSI codepage. This is required to
+ correctly walk the encoded file names one character at a time. */
+static int
+max_filename_mbslen (void)
+{
+ /* A simple cache to avoid calling GetCPInfo every time we need to
+ normalize a file name. The file-name encoding is not supposed to
+ be changed too frequently, if ever. */
+ static Lisp_Object last_file_name_encoding;
+ static int last_max_mbslen;
+ Lisp_Object current_encoding;
+
+ current_encoding = Vfile_name_coding_system;
+ if (NILP (current_encoding))
+ current_encoding = Vdefault_file_name_coding_system;
+
+ if (!EQ (last_file_name_encoding, current_encoding))
+ {
+ CPINFO cp_info;
+
+ last_file_name_encoding = current_encoding;
+ /* Default to the current ANSI codepage. */
+ file_name_codepage = w32_ansi_code_page;
+ if (!NILP (current_encoding))
+ {
+ char *cpname = SDATA (SYMBOL_NAME (current_encoding));
+ char *cp = NULL, *end;
+ int cpnum;
+
+ if (strncmp (cpname, "cp", 2) == 0)
+ cp = cpname + 2;
+ else if (strncmp (cpname, "windows-", 8) == 0)
+ cp = cpname + 8;
+
+ if (cp)
+ {
+ end = cp;
+ cpnum = strtol (cp, &end, 10);
+ if (cpnum && *end == '\0' && end - cp >= 2)
+ file_name_codepage = cpnum;
+ }
+ }
+
+ if (!file_name_codepage)
+ file_name_codepage = CP_ACP; /* CP_ACP = 0, but let's not assume that */
+
+ if (!GetCPInfo (file_name_codepage, &cp_info))
+ {
+ file_name_codepage = CP_ACP;
+ if (!GetCPInfo (file_name_codepage, &cp_info))
+ emacs_abort ();
+ }
+ last_max_mbslen = cp_info.MaxCharSize;
+ }
+
+ return last_max_mbslen;
+}
/* Normalize filename by converting all path separators to
the specified separator. Also conditionally convert upper
case path name components to lower case. */
static void
-normalize_filename (register char *fp, char path_sep)
+normalize_filename (register char *fp, char path_sep, int multibyte)
{
char sep;
- char *elem;
+ char *elem, *p2;
+ int dbcs_p = max_filename_mbslen () > 1;
+
+ /* Multibyte file names are in the Emacs internal representation, so
+ we can traverse them by bytes with no problems. */
+ if (multibyte)
+ dbcs_p = 0;
/* Always lower-case drive letters a-z, even if the filesystem
preserves case in filenames.
This is so filenames can be compared by string comparison
functions that are case-sensitive. Even case-preserving filesystems
do not distinguish case in drive letters. */
- if (fp[1] == ':' && *fp >= 'A' && *fp <= 'Z')
+ if (dbcs_p)
+ p2 = CharNextExA (file_name_codepage, fp, 0);
+ else
+ p2 = fp + 1;
+
+ if (*p2 == ':' && *fp >= 'A' && *fp <= 'Z')
{
*fp += 'a' - 'A';
fp += 2;
}
- if (NILP (Vw32_downcase_file_names))
+ if (multibyte || NILP (Vw32_downcase_file_names))
{
while (*fp)
{
if (*fp == '/' || *fp == '\\')
*fp = path_sep;
- fp++;
+ if (!dbcs_p)
+ fp++;
+ else
+ fp = CharNextExA (file_name_codepage, fp, 0);
}
return;
}
if (elem && elem != fp)
{
*fp = 0; /* temporary end of string */
- _strlwr (elem); /* while we convert to lower case */
+ _mbslwr (elem); /* while we convert to lower case */
}
*fp = sep; /* convert (or restore) path separator */
elem = fp + 1; /* next element starts after separator */
sep = path_sep;
}
- } while (*fp++);
+ if (*fp)
+ {
+ if (!dbcs_p)
+ fp++;
+ else
+ fp = CharNextExA (file_name_codepage, fp, 0);
+ }
+ } while (*fp);
}
-/* Destructively turn backslashes into slashes. */
+/* Destructively turn backslashes into slashes. MULTIBYTE non-zero
+ means the file name is a multibyte string in Emacs's internal
+ representation. */
void
-dostounix_filename (register char *p)
+dostounix_filename (register char *p, int multibyte)
{
- normalize_filename (p, '/');
+ normalize_filename (p, '/', multibyte);
}
/* Destructively turn slashes into backslashes. */
void
unixtodos_filename (register char *p)
{
- normalize_filename (p, '\\');
+ normalize_filename (p, '\\', 0);
}
/* Remove all CR's that are followed by a LF.
else if (IS_DIRECTORY_SEP (name[0]) && IS_DIRECTORY_SEP (name[1]))
{
int slashes = 2;
+ int dbcs_p = max_filename_mbslen () > 1;
+
name += 2;
do
{
if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
break;
- name++;
+ if (dbcs_p)
+ name = CharNextExA (file_name_codepage, name, 0);
+ else
+ name++;
}
while ( *name );
if (IS_DIRECTORY_SEP (name[0]))
while (p != NULL && *p)
{
q = p;
- p = strchr (q, '\\');
+ p = _mbschr (q, '\\');
if (p) *p = '\0';
len = get_long_basename (full, o, size);
if (len > 0)
/* It is safe to use 'alloca' with 32K size, since the stack is at
least 2MB, and we set it to 8MB in the link command line. */
var = alloca (name_len + 2);
+ strncpy (var, name, name_len);
var[name_len++] = '=';
var[name_len] = '\0';
return _putenv (var);
if (!GetModuleFileName (NULL, modname, MAX_PATH))
emacs_abort ();
- if ((p = strrchr (modname, '\\')) == NULL)
+ if ((p = _mbsrchr (modname, '\\')) == NULL)
emacs_abort ();
*p = 0;
- if ((p = strrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
+ if ((p = _mbsrchr (modname, '\\')) && xstrcasecmp (p, "\\bin") == 0)
{
char buf[SET_ENV_BUF_SIZE];
*p = 0;
- for (p = modname; *p; p++)
+ for (p = modname; *p; p = CharNext (p))
if (*p == '\\') *p = '/';
_snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
|| xstrcasecmp (p, "\\AMD64") == 0))
{
*p = 0;
- p = strrchr (modname, '\\');
+ p = _mbsrchr (modname, '\\');
if (p != NULL)
{
*p = 0;
- p = strrchr (modname, '\\');
+ p = _mbsrchr (modname, '\\');
if (p && xstrcasecmp (p, "\\src") == 0)
{
char buf[SET_ENV_BUF_SIZE];
*p = 0;
- for (p = modname; *p; p++)
+ for (p = modname; *p; p = CharNext (p))
if (*p == '\\') *p = '/';
_snprintf (buf, sizeof (buf)-1, "emacs_dir=%s", modname);
emacs_abort ();
strcpy (root_dir, p);
root_dir[parse_root (root_dir, NULL)] = '\0';
- dostounix_filename (root_dir);
+ dostounix_filename (root_dir, 0);
return root_dir;
}
{
char *str = temp;
int slashes = 4;
+ int dbcs_p = max_filename_mbslen () > 1;
+
rootname = temp;
do
{
if (IS_DIRECTORY_SEP (*name) && --slashes == 0)
break;
- *str++ = *name++;
+ if (!dbcs_p)
+ *str++ = *name++;
+ else
+ {
+ const char *p = name;
+
+ name = CharNextExA (file_name_codepage, name, 0);
+ memcpy (str, p, name - p);
+ str += name - p;
+ }
}
while ( *name );
static void close_unc_volume (HANDLE);
DIR *
-opendir (char *filename)
+opendir (const char *filename)
{
DIR *dirp;
{
char filename[MAXNAMLEN + 3];
int ln;
+ int dbcs_p = max_filename_mbslen () > 1;
strcpy (filename, dir_pathname);
ln = strlen (filename) - 1;
- if (!IS_DIRECTORY_SEP (filename[ln]))
- strcat (filename, "\\");
+ if (!dbcs_p)
+ {
+ if (!IS_DIRECTORY_SEP (filename[ln]))
+ strcat (filename, "\\");
+ }
+ else
+ {
+ char *end = filename + ln + 1;
+ char *last_char = CharPrevExA (file_name_codepage, filename, end, 0);
+
+ if (!IS_DIRECTORY_SEP (*last_char))
+ strcat (filename, "\\");
+ }
strcat (filename, "*");
/* Note: No need to resolve symlinks in FILENAME, because
strcpy (dir_static.d_name, dir_find_data.cFileName);
dir_static.d_namlen = strlen (dir_static.d_name);
if (dir_is_fat)
- _strlwr (dir_static.d_name);
+ _mbslwr (dir_static.d_name);
else if (downcase)
{
register char *p;
- for (p = dir_static.d_name; *p; p++)
- if (*p >= 'a' && *p <= 'z')
- break;
+ int dbcs_p = max_filename_mbslen () > 1;
+ for (p = dir_static.d_name; *p; )
+ {
+ if (*p >= 'a' && *p <= 'z')
+ break;
+ if (dbcs_p)
+ p = CharNextExA (file_name_codepage, p, 0);
+ else
+ p++;
+ }
if (!*p)
- _strlwr (dir_static.d_name);
+ _mbslwr (dir_static.d_name);
}
return &dir_static;
DWORD bufsize = 512;
char *buffer;
char *ptr;
+ int dbcs_p = max_filename_mbslen () > 1;
count = 1;
buffer = alloca (bufsize);
/* WNetEnumResource returns \\resource\share...skip forward to "share". */
ptr = ((LPNETRESOURCE) buffer)->lpRemoteName;
ptr += 2;
- while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ if (!dbcs_p)
+ while (*ptr && !IS_DIRECTORY_SEP (*ptr)) ptr++;
+ else
+ {
+ while (*ptr && !IS_DIRECTORY_SEP (*ptr))
+ ptr = CharNextExA (file_name_codepage, ptr, 0);
+ }
ptr++;
strncpy (readbuf, ptr, size);
{
NETRESOURCE resource;
char share[MAX_PATH];
- int i, n_slashes;
+ int n_slashes;
char drive[4];
UINT drvtype;
+ char *p;
+ int dbcs_p;
if (IS_DIRECTORY_SEP (path[0]) && IS_DIRECTORY_SEP (path[1]))
drvtype = DRIVE_REMOTE;
n_slashes = 2;
strncpy (share, path, MAX_PATH);
/* Truncate to just server and share name. */
- for (i = 2; i < MAX_PATH; i++)
+ dbcs_p = max_filename_mbslen () > 1;
+ for (p = share + 2; *p && p < share + MAX_PATH; )
{
- if (IS_DIRECTORY_SEP (share[i]) && ++n_slashes > 3)
+ if (IS_DIRECTORY_SEP (*p) && ++n_slashes > 3)
{
- share[i] = '\0';
+ *p = '\0';
break;
}
+ if (dbcs_p)
+ p = CharNextExA (file_name_codepage, p, 0);
+ else
+ p++;
}
resource.dwType = RESOURCETYPE_DISK;
return _chmod (path, mode);
}
-int
-sys_chown (const char *path, uid_t owner, gid_t group)
-{
- if (sys_chmod (path, S_IREAD) == -1) /* check if file exists */
- return -1;
- return 0;
-}
-
int
sys_creat (const char * path, int mode)
{
sys_open (const char * path, int oflag, int 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 res = -1;
+
+ /* If possible, try to open file without _O_CREAT, to be able to
+ write to existing hidden and system files. Force all file
+ handles to be non-inheritable. */
+ if ((oflag & (_O_CREAT | _O_EXCL)) != (_O_CREAT | _O_EXCL))
+ res = _open (mpath, (oflag & ~_O_CREAT) | _O_NOINHERIT, mode);
+ if (res < 0)
+ res = _open (mpath, oflag | _O_NOINHERIT, mode);
+
+ return res;
}
int
-sys_rename (const char * oldname, const char * newname)
+fchmod (int fd, mode_t mode)
+{
+ return 0;
+}
+
+int
+sys_rename_replace (const char *oldname, const char *newname, BOOL force)
{
BOOL result;
char temp[MAX_PATH];
return -1;
}
- /* Emulate Unix behavior - newname is deleted if it already exists
+ /* If FORCE, emulate Unix behavior - newname is deleted if it already exists
(at least if it is a file; don't do this for directories).
Since we mustn't do this if we are just changing the case of the
result = rename (temp, newname);
- if (result < 0)
+ if (result < 0 && force)
{
DWORD w32err = GetLastError ();
return result;
}
+int
+sys_rename (char const *old, char const *new)
+{
+ return sys_rename_replace (old, new, TRUE);
+}
+
int
sys_rmdir (const char * path)
{
get_name_and_id (PSECURITY_DESCRIPTOR psd, unsigned *id, char *nm, int what)
{
PSID sid = NULL;
- char machine[MAX_COMPUTERNAME_LENGTH+1];
BOOL dflt;
SID_NAME_USE ignore;
char name[UNLEN+1];
DWORD access_rights = 0;
DWORD fattrs = 0, serialnum = 0, fs_high = 0, fs_low = 0, nlinks = 1;
FILETIME ctime, atime, wtime;
+ int dbcs_p;
if (path == NULL || buf == NULL)
{
did not ask for extra precision, resolving symlinks will fly
in the face of that request, since the user then wants the
lightweight version of the code. */
+ dbcs_p = max_filename_mbslen () > 1;
rootdir = (path >= save_name + len - 1
&& (IS_DIRECTORY_SEP (*path) || *path == 0));
}
else if (rootdir)
{
- if (!IS_DIRECTORY_SEP (name[len-1]))
- strcat (name, "\\");
+ if (!dbcs_p)
+ {
+ if (!IS_DIRECTORY_SEP (name[len-1]))
+ strcat (name, "\\");
+ }
+ else
+ {
+ char *end = name + len;
+ char *n = CharPrevExA (file_name_codepage, name, end, 0);
+
+ if (!IS_DIRECTORY_SEP (*n))
+ strcat (name, "\\");
+ }
if (GetDriveType (name) < 2)
{
errno = ENOENT;
}
else
{
- if (IS_DIRECTORY_SEP (name[len-1]))
- name[len - 1] = 0;
+ if (!dbcs_p)
+ {
+ if (IS_DIRECTORY_SEP (name[len-1]))
+ name[len - 1] = 0;
+ }
+ else
+ {
+ char *end = name + len;
+ char *n = CharPrevExA (file_name_codepage, name, end, 0);
+
+ if (IS_DIRECTORY_SEP (*n))
+ *n = 0;
+ }
/* (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 (!dbcs_p)
+ {
+ if (IS_DIRECTORY_SEP (dir_pathname[len-1]))
+ len--;
+ }
+ else
+ {
+ char *end = dir_pathname + len;
+ char *n = CharPrevExA (file_name_codepage, dir_pathname, end, 0);
+
+ if (IS_DIRECTORY_SEP (*n))
+ len--;
+ }
if (dir_find_handle != INVALID_HANDLE_VALUE
&& !(is_a_symlink && follow_symlinks)
&& strnicmp (save_name, dir_pathname, len) == 0
return stat_worker (path, buf, 0);
}
+int
+fstatat (int fd, char const *name, struct stat *st, int flags)
+{
+ /* Rely on a hack: an open directory is modeled as file descriptor 0.
+ This is good enough for the current usage in Emacs, but is fragile.
+
+ FIXME: Add proper support for fdopendir, fstatat, readlinkat.
+ Gnulib does this and can serve as a model. */
+ char fullname[MAX_PATH];
+
+ if (fd != AT_FDCWD)
+ {
+ if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+ < 0)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ name = fullname;
+ }
+
+ return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW));
+}
+
/* Provide fstat and utime as well as stat for consistent handling of
file timestamps. */
int
else
buf->st_ino = fake_inode;
- /* Consider files to belong to current user.
- FIXME: this should use GetSecurityInfo API, but it is only
- available for _WIN32_WINNT >= 0x501. */
- buf->st_uid = dflt_passwd.pw_uid;
- buf->st_gid = dflt_passwd.pw_gid;
- strcpy (buf->st_uname, dflt_passwd.pw_name);
- strcpy (buf->st_gname, dflt_group.gr_name);
+ /* If the caller so requested, get the true file owner and group.
+ Otherwise, consider the file to belong to the current user. */
+ if (!w32_stat_get_owner_group || is_windows_9x () == TRUE)
+ get_file_owner_and_group (NULL, buf);
+ else
+ {
+ PSECURITY_DESCRIPTOR psd = NULL;
+
+ psd = get_file_security_desc_by_handle (fh);
+ if (psd)
+ {
+ get_file_owner_and_group (psd, buf);
+ LocalFree (psd);
+ }
+ else
+ get_file_owner_and_group (NULL, buf);
+ }
buf->st_dev = info.dwVolumeSerialNumber;
buf->st_rdev = info.dwVolumeSerialNumber;
char linkfn[MAX_PATH], *tgtfn;
DWORD flags = 0;
int dir_access, filename_ends_in_slash;
+ int dbcs_p;
/* Diagnostics follows Posix as much as possible. */
if (filename == NULL || linkname == NULL)
return -1;
}
+ dbcs_p = max_filename_mbslen () > 1;
+
/* Note: since empty FILENAME was already rejected, we can safely
refer to FILENAME[1]. */
if (!(IS_DIRECTORY_SEP (filename[0]) || IS_DEVICE_SEP (filename[1])))
char tem[MAX_PATH];
char *p = linkfn + strlen (linkfn);
- while (p > linkfn && !IS_ANY_SEP (p[-1]))
- p--;
+ if (!dbcs_p)
+ {
+ while (p > linkfn && !IS_ANY_SEP (p[-1]))
+ p--;
+ }
+ else
+ {
+ char *p1 = CharPrevExA (file_name_codepage, linkfn, p, 0);
+
+ while (p > linkfn && !IS_ANY_SEP (*p1))
+ {
+ p = p1;
+ p1 = CharPrevExA (file_name_codepage, linkfn, p1, 0);
+ }
+ }
if (p > linkfn)
strncpy (tem, linkfn, p - linkfn);
tem[p - linkfn] = '\0';
exist, but ends in a slash, we create a symlink to directory. If
FILENAME exists and is a directory, we always create a symlink to
directory. */
- filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
+ if (!dbcs_p)
+ filename_ends_in_slash = IS_DIRECTORY_SEP (filename[strlen (filename) - 1]);
+ else
+ {
+ const char *end = filename + strlen (filename);
+ const char *n = CharPrevExA (file_name_codepage, filename, end, 0);
+
+ filename_ends_in_slash = IS_DIRECTORY_SEP (*n);
+ }
if (dir_access == 0 || filename_ends_in_slash)
flags = SYMBOLIC_LINK_FLAG_DIRECTORY;
WCHAR *lwname_src =
reparse_data->SymbolicLinkReparseBuffer.PathBuffer
+ reparse_data->SymbolicLinkReparseBuffer.PrintNameOffset/sizeof(WCHAR);
+ /* This updates file_name_codepage which we need below. */
+ int dbcs_p = max_filename_mbslen () > 1;
/* According to MSDN, PrintNameLength does not include the
terminating null character. */
memcpy (lwname, lwname_src, lwname_len);
lwname[lwname_len/sizeof(WCHAR)] = 0; /* null-terminate */
- /* FIXME: Should we use the current file-name coding system
- instead of the fixed value of the ANSI codepage? */
- lname_len = WideCharToMultiByte (w32_ansi_code_page, 0, lwname, -1,
+ lname_len = WideCharToMultiByte (file_name_codepage, 0, lwname, -1,
lname, MAX_PATH, NULL, NULL);
if (!lname_len)
{
else
{
size_t size_to_copy = buf_size;
- BYTE *p = lname;
+ BYTE *p = lname, *p2;
BYTE *pend = p + lname_len;
/* Normalize like dostounix_filename does, but we don't
want to assume that lname is null-terminated. */
- if (*p && p[1] == ':' && *p >= 'A' && *p <= 'Z')
- *p += 'a' - 'A';
+ if (dbcs_p)
+ p2 = CharNextExA (file_name_codepage, p, 0);
+ else
+ p2 = p + 1;
+ if (*p && *p2 == ':' && *p >= 'A' && *p <= 'Z')
+ {
+ *p += 'a' - 'A';
+ p += 2;
+ }
while (p <= pend)
{
if (*p == '\\')
*p = '/';
- ++p;
+ if (dbcs_p)
+ {
+ p = CharNextExA (file_name_codepage, p, 0);
+ /* CharNextExA doesn't advance at null character. */
+ if (!*p)
+ break;
+ }
+ else
+ ++p;
}
/* Testing for null-terminated LNAME is paranoia:
WideCharToMultiByte should always return a
return retval;
}
+ssize_t
+readlinkat (int fd, char const *name, char *buffer,
+ size_t buffer_size)
+{
+ /* Rely on a hack: an open directory is modeled as file descriptor 0,
+ as in fstatat. FIXME: Add proper support for readlinkat. */
+ char fullname[MAX_PATH];
+
+ if (fd != AT_FDCWD)
+ {
+ if (_snprintf (fullname, sizeof fullname, "%s/%s", dir_pathname, name)
+ < 0)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ name = fullname;
+ }
+
+ return readlink (name, buffer, buffer_size);
+}
+
/* If FILE is a symlink, return its target (stored in a static
buffer); otherwise return FILE.
char link[MAX_PATH];
ssize_t res, link_len;
int loop_count = 0;
+ int dbcs_p;
if (is_windows_9x () == TRUE || !is_symlink (file))
return (char *)file;
if ((link_len = GetFullPathName (file, MAX_PATH, link, NULL)) == 0)
return (char *)file;
+ dbcs_p = max_filename_mbslen () > 1;
target[0] = '\0';
do {
/* Remove trailing slashes, as we want to resolve the last
non-trivial part of the link name. */
- while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
- link[link_len--] = '\0';
+ if (!dbcs_p)
+ {
+ while (link_len > 3 && IS_DIRECTORY_SEP (link[link_len-1]))
+ link[link_len--] = '\0';
+ }
+ else if (link_len > 3)
+ {
+ char *n = CharPrevExA (file_name_codepage, link, link + link_len, 0);
+
+ while (n >= link + 2 && IS_DIRECTORY_SEP (*n))
+ {
+ n[1] = '\0';
+ n = CharPrevExA (file_name_codepage, link, n, 0);
+ }
+ }
res = readlink (link, target, MAX_PATH);
if (res > 0)
the symlink, then copy the result back to target. */
char *p = link + link_len;
- while (p > link && !IS_ANY_SEP (p[-1]))
- p--;
+ if (!dbcs_p)
+ {
+ while (p > link && !IS_ANY_SEP (p[-1]))
+ p--;
+ }
+ else
+ {
+ char *p1 = CharPrevExA (file_name_codepage, link, p, 0);
+
+ while (p > link && !IS_ANY_SEP (*p1))
+ {
+ p = p1;
+ p1 = CharPrevExA (file_name_codepage, link, p1, 0);
+ }
+ }
strcpy (p, target);
strcpy (target, link);
}
{
TOKEN_PRIVILEGES old1, old2;
DWORD err;
- BOOL res;
int st = 0, retval = -1;
SECURITY_INFORMATION flags = 0;
PSID psid;
e = errno;
errno = 0;
- set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl);
- err = GetLastError ();
- if (st)
+ if (!set_file_security ((char *)fname, flags, (PSECURITY_DESCRIPTOR)acl))
{
- if (st >= 2)
- restore_privilege (&old2);
- restore_privilege (&old1);
- revert_to_self ();
- }
-
- if (errno == ENOTSUP)
- ;
- else if (err == ERROR_SUCCESS)
- {
- retval = 0;
- errno = e;
- }
- else if (err == ERROR_INVALID_OWNER || err == ERROR_NOT_ALL_ASSIGNED)
- {
- /* Maybe the requested ACL and the one the file already has are
- identical, in which case we can silently ignore the
- failure. (And no, Windows doesn't.) */
- acl_t current_acl = acl_get_file (fname, ACL_TYPE_ACCESS);
+ err = GetLastError ();
- errno = EPERM;
- if (current_acl)
+ if (errno == ENOTSUP)
+ ;
+ else if (err == ERROR_INVALID_OWNER
+ || err == ERROR_NOT_ALL_ASSIGNED
+ || err == ERROR_ACCESS_DENIED)
{
- char *acl_from = acl_to_text (current_acl, NULL);
- char *acl_to = acl_to_text (acl, NULL);
+ /* Maybe the requested ACL and the one the file already has
+ are identical, in which case we can silently ignore the
+ failure. (And no, Windows doesn't.) */
+ acl_t current_acl = acl_get_file (fname, ACL_TYPE_ACCESS);
- if (acl_from && acl_to && xstrcasecmp (acl_from, acl_to) == 0)
+ errno = EPERM;
+ if (current_acl)
{
- retval = 0;
- errno = e;
+ char *acl_from = acl_to_text (current_acl, NULL);
+ char *acl_to = acl_to_text (acl, NULL);
+
+ if (acl_from && acl_to && xstrcasecmp (acl_from, acl_to) == 0)
+ {
+ retval = 0;
+ errno = e;
+ }
+ if (acl_from)
+ acl_free (acl_from);
+ if (acl_to)
+ acl_free (acl_to);
+ acl_free (current_acl);
}
- if (acl_from)
- acl_free (acl_from);
- if (acl_to)
- acl_free (acl_to);
- acl_free (current_acl);
}
+ else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
+ errno = ENOENT;
+ else
+ errno = EACCES;
+ }
+ else
+ {
+ retval = 0;
+ errno = e;
+ }
+
+ if (st)
+ {
+ if (st >= 2)
+ restore_privilege (&old2);
+ restore_privilege (&old1);
+ revert_to_self ();
}
- else if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND)
- errno = ENOENT;
return retval;
}
char linkname[MAX_PATH];
ssize_t link_size;
- if (fd != AT_FDCWD)
- {
- errno = EINVAL;
- return NULL;
- }
-
link_size = preadlinkat (fd, filename, linkname, sizeof(linkname));
if (link_size > 0)
return NULL;
}
-ssize_t
-careadlinkatcwd (int fd, char const *filename, char *buffer,
- size_t buffer_size)
-{
- (void) fd;
- return readlink (filename, buffer, buffer_size);
-}
-
\f
/* Support for browsing other processes and their attributes. See
process.c for the Lisp bindings. */
{
ULONGLONG time_sec = time_100ns / 10000000;
int subsec = time_100ns % 10000000;
- return list4 (make_number (time_sec >> 16),
- make_number (time_sec & 0xffff),
- make_number (subsec / 10),
- make_number (subsec % 10 * 100000));
+ return list4i (time_sec >> 16, time_sec & 0xffff,
+ subsec / 10, subsec % 10 * 100000);
}
#define U64_TO_LISP_TIME(time) ltime (time)
int h_errno = 0;
-/* function to set h_errno for compatibility; map winsock error codes to
- normal system codes where they overlap (non-overlapping definitions
- are already in <sys/socket.h> */
+/* Function to map winsock error codes to errno codes for those errno
+ code defined in errno.h (errno values not defined by errno.h are
+ already in nt/inc/sys/socket.h). */
static void
set_errno (void)
{
+ int wsa_err;
+
+ h_errno = 0;
if (winsock_lib == NULL)
- h_errno = EINVAL;
+ wsa_err = EINVAL;
else
- h_errno = pfn_WSAGetLastError ();
+ wsa_err = pfn_WSAGetLastError ();
- switch (h_errno)
+ switch (wsa_err)
{
- 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;
+ case WSAEACCES: errno = EACCES; break;
+ case WSAEBADF: errno = EBADF; break;
+ case WSAEFAULT: errno = EFAULT; break;
+ case WSAEINTR: errno = EINTR; break;
+ case WSAEINVAL: errno = EINVAL; break;
+ case WSAEMFILE: errno = EMFILE; break;
+ case WSAENAMETOOLONG: errno = ENAMETOOLONG; break;
+ case WSAENOTEMPTY: errno = ENOTEMPTY; break;
+ default: errno = wsa_err; break;
}
- errno = h_errno;
}
static void
check_errno (void)
{
- if (h_errno == 0 && winsock_lib != NULL)
+ h_errno = 0;
+ if (winsock_lib != NULL)
pfn_WSASetLastError (0);
}
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return INVALID_SOCKET;
}
}
}
}
+ eassert (fd < MAXDESC);
fd_info[fd].hnd = (HANDLE) s;
/* set our own internal flags */
/* clean up */
_close (fd);
}
+ else
pfn_closesocket (s);
- h_errno = EMFILE;
+ errno = EMFILE;
return -1;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
sys_gethostname (char * name, int namelen)
{
if (winsock_lib != NULL)
- return pfn_gethostname (name, namelen);
+ {
+ int retval;
+
+ check_errno ();
+ retval = pfn_gethostname (name, namelen);
+ if (retval == SOCKET_ERROR)
+ set_errno ();
+ return retval;
+ }
if (namelen > MAX_COMPUTERNAME_LENGTH)
return !GetComputerName (name, (DWORD *)&namelen);
- h_errno = EFAULT;
+ errno = EFAULT;
return SOCKET_ERROR;
}
sys_gethostbyname (const char * name)
{
struct hostent * host;
+ int h_err = h_errno;
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ h_errno = NO_RECOVERY;
+ errno = ENETDOWN;
return NULL;
}
check_errno ();
host = pfn_gethostbyname (name);
if (!host)
- set_errno ();
+ {
+ set_errno ();
+ h_errno = errno;
+ }
+ else
+ h_errno = h_err;
return host;
}
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return NULL;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
fd_info[s].flags |= FILE_LISTEN;
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return -1;
}
else
fd = socket_to_fd (t);
- fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
- ResetEvent (fd_info[s].cp->char_avail);
+ if (fd >= 0)
+ {
+ fd_info[s].cp->status = STATUS_READ_ACKNOWLEDGED;
+ ResetEvent (fd_info[s].cp->char_avail);
+ }
return fd;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return -1;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return SOCKET_ERROR;
}
set_errno ();
return rc;
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
{
if (winsock_lib == NULL)
{
- h_errno = ENETDOWN;
+ errno = ENETDOWN;
return -1;
}
}
else
{
- h_errno = EINVAL;
+ errno = EINVAL;
return SOCKET_ERROR;
}
}
- h_errno = ENOTSOCK;
+ errno = ENOTSOCK;
return SOCKET_ERROR;
}
}
}
+ if (fd >= 0 && fd < MAXDESC)
+ fd_info[fd].flags = 0;
+
/* Note that sockets do not need special treatment here (at least on
NT and Windows 95 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 < MAXDESC)
- fd_info[fd].flags = 0;
-
return rc;
}
{
_close (phandles[0]);
_close (phandles[1]);
+ errno = EMFILE;
rc = -1;
}
else
/* Configure timeouts for blocking read. */
if (!GetCommTimeouts (hnd, &ct))
- return STATUS_READ_ERROR;
+ {
+ cp->status = STATUS_READ_ERROR;
+ return STATUS_READ_ERROR;
+ }
ct.ReadIntervalTimeout = 0;
ct.ReadTotalTimeoutMultiplier = 0;
ct.ReadTotalTimeoutConstant = 0;
if (!SetCommTimeouts (hnd, &ct))
- return STATUS_READ_ERROR;
+ {
+ cp->status = STATUS_READ_ERROR;
+ return STATUS_READ_ERROR;
+ }
if (!ReadFile (hnd, &cp->chr, sizeof (char), (DWORD*) &rc, ovl))
{
if (GetLastError () != ERROR_IO_PENDING)
- return STATUS_READ_ERROR;
+ {
+ cp->status = STATUS_READ_ERROR;
+ return STATUS_READ_ERROR;
+ }
if (!GetOverlappedResult (hnd, ovl, (DWORD*) &rc, TRUE))
- return STATUS_READ_ERROR;
+ {
+ cp->status = STATUS_READ_ERROR;
+ return STATUS_READ_ERROR;
+ }
}
}
else if (fd_info[fd].flags & FILE_SOCKET)
pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting);
if (waiting == 0 && nchars == 0)
{
- h_errno = errno = EWOULDBLOCK;
+ errno = EWOULDBLOCK;
return -1;
}
ssize_t
emacs_gnutls_pull (gnutls_transport_ptr_t p, void* buf, size_t sz)
{
- int n, sc, err;
+ int n, err;
SELECT_TYPE fdset;
EMACS_TIME timeout;
struct Lisp_Process *process = (struct Lisp_Process *)p;
int fd = process->infd;
- for (;;)
- {
- n = sys_read (fd, (char*)buf, sz);
+ n = sys_read (fd, (char*)buf, sz);
- if (n >= 0)
- return n;
+ if (n >= 0)
+ return n;
- err = errno;
+ err = errno;
- if (err == EWOULDBLOCK)
- {
- /* Set a small timeout. */
- timeout = make_emacs_time (1, 0);
- FD_ZERO (&fdset);
- FD_SET ((int)fd, &fdset);
-
- /* Use select with the timeout to poll the selector. */
- sc = select (fd + 1, &fdset, (SELECT_TYPE *)0, (SELECT_TYPE *)0,
- &timeout, NULL);
-
- if (sc > 0)
- continue; /* Try again. */
-
- /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN.
- Also accept select return 0 as an indicator to EAGAIN. */
- if (sc == 0 || errno == EWOULDBLOCK)
- err = EAGAIN;
- else
- err = errno; /* Other errors are just passed on. */
- }
+ /* Translate the WSAEWOULDBLOCK alias EWOULDBLOCK to EAGAIN. */
+ if (err == EWOULDBLOCK)
+ err = EAGAIN;
- emacs_gnutls_transport_set_errno (process->gnutls_state, err);
+ emacs_gnutls_transport_set_errno (process->gnutls_state, err);
- return -1;
- }
+ return -1;
}
ssize_t