summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rw-r--r--compat/mingw.c248
-rw-r--r--compat/mingw.h16
-rw-r--r--compat/nedmalloc/malloc.c.h6
-rw-r--r--compat/obstack.h2
-rw-r--r--compat/regex/regcomp.c2
-rw-r--r--compat/regex/regex.h2
-rw-r--r--compat/regex/regex_internal.c2
-rw-r--r--compat/regex/regexec.c4
-rw-r--r--compat/vcbuild/find_vs_env.bat2
-rw-r--r--compat/win32/path-utils.c24
-rw-r--r--compat/win32/path-utils.h5
-rw-r--r--compat/winansi.c12
12 files changed, 290 insertions, 35 deletions
diff --git a/compat/mingw.c b/compat/mingw.c
index fe609239dd..c2a4835104 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -114,6 +114,7 @@ int err_win_to_posix(DWORD winerr)
case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
case ERROR_SHARING_VIOLATION: error = EACCES; break;
case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
+ case ERROR_SUCCESS: BUG("err_win_to_posix() called without an error!");
case ERROR_SWAPERROR: error = ENOENT; break;
case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
@@ -212,6 +213,7 @@ enum hide_dotfiles_type {
HIDE_DOTFILES_DOTGITONLY
};
+static int core_restrict_inherited_handles = -1;
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
static char *unset_environment_variables;
@@ -231,6 +233,15 @@ int mingw_core_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "core.restrictinheritedhandles")) {
+ if (value && !strcasecmp(value, "auto"))
+ core_restrict_inherited_handles = -1;
+ else
+ core_restrict_inherited_handles =
+ git_config_bool(var, value);
+ return 0;
+ }
+
return 0;
}
@@ -392,6 +403,12 @@ int mingw_mkdir(const char *path, int mode)
{
int ret;
wchar_t wpath[MAX_PATH];
+
+ if (!is_valid_win32_path(path)) {
+ errno = EINVAL;
+ return -1;
+ }
+
if (xutftowcs_path(wpath, path) < 0)
return -1;
ret = _wmkdir(wpath);
@@ -465,7 +482,7 @@ int mingw_open (const char *filename, int oflags, ...)
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
va_list args;
unsigned mode;
- int fd;
+ int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
wchar_t wfilename[MAX_PATH];
open_fn_t open_fn;
@@ -473,6 +490,11 @@ int mingw_open (const char *filename, int oflags, ...)
mode = va_arg(args, int);
va_end(args);
+ if (!is_valid_win32_path(filename)) {
+ errno = create ? EINVAL : ENOENT;
+ return -1;
+ }
+
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
@@ -539,6 +561,11 @@ FILE *mingw_fopen (const char *filename, const char *otype)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
+ if (!is_valid_win32_path(filename)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ }
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -561,6 +588,11 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
int hide = needs_hiding(filename);
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
+ if (!is_valid_win32_path(filename)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ }
if (filename && !strcmp(filename, "/dev/null"))
filename = "nul";
if (xutftowcs_path(wfilename, filename) < 0 ||
@@ -1054,7 +1086,7 @@ static const char *quote_arg_msvc(const char *arg)
p++;
len++;
}
- if (*p == '"')
+ if (*p == '"' || !*p)
n += count*2 + 1;
continue;
}
@@ -1076,16 +1108,19 @@ static const char *quote_arg_msvc(const char *arg)
count++;
*d++ = *arg++;
}
- if (*arg == '"') {
+ if (*arg == '"' || !*arg) {
while (count-- > 0)
*d++ = '\\';
+ /* don't escape the surrounding end quote */
+ if (!*arg)
+ break;
*d++ = '\\';
}
}
*d++ = *arg++;
}
*d++ = '"';
- *d++ = 0;
+ *d++ = '\0';
return q;
}
@@ -1098,13 +1133,14 @@ static const char *quote_arg_msys2(const char *arg)
for (p = arg; *p; p++) {
int ws = isspace(*p);
- if (!ws && *p != '\\' && *p != '"' && *p != '{')
+ if (!ws && *p != '\\' && *p != '"' && *p != '{' && *p != '\'' &&
+ *p != '?' && *p != '*' && *p != '~')
continue;
if (!buf.len)
strbuf_addch(&buf, '"');
if (p != p2)
strbuf_add(&buf, p2, p - p2);
- if (!ws && *p != '{')
+ if (*p == '\\' || *p == '"')
strbuf_addch(&buf, '\\');
p2 = p;
}
@@ -1114,7 +1150,7 @@ static const char *quote_arg_msys2(const char *arg)
else if (!buf.len)
return arg;
else
- strbuf_add(&buf, p2, p - p2),
+ strbuf_add(&buf, p2, p - p2);
strbuf_addch(&buf, '"');
return strbuf_detach(&buf, 0);
@@ -1371,7 +1407,10 @@ static inline int match_last_path_component(const char *path, size_t *len,
static int is_msys2_sh(const char *cmd)
{
- if (cmd && !strcmp(cmd, "sh")) {
+ if (!cmd)
+ return 0;
+
+ if (!strcmp(cmd, "sh")) {
static int ret = -1;
char *p;
@@ -1391,6 +1430,16 @@ static int is_msys2_sh(const char *cmd)
}
return ret;
}
+
+ if (ends_with(cmd, "\\sh.exe")) {
+ static char *sh;
+
+ if (!sh)
+ sh = path_lookup("sh", 0);
+
+ return !fspathcmp(cmd, sh);
+ }
+
return 0;
}
@@ -1398,15 +1447,34 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
const char *dir,
int prepend_cmd, int fhin, int fhout, int fherr)
{
- STARTUPINFOW si;
+ static int restrict_handle_inheritance = -1;
+ STARTUPINFOEXW si;
PROCESS_INFORMATION pi;
+ LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
+ HANDLE stdhandles[3];
+ DWORD stdhandles_count = 0;
+ SIZE_T size;
struct strbuf args;
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
BOOL ret;
HANDLE cons;
const char *(*quote_arg)(const char *arg) =
- is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc;
+ is_msys2_sh(cmd ? cmd : *argv) ?
+ quote_arg_msys2 : quote_arg_msvc;
+
+ /* Make sure to override previous errors, if any */
+ errno = 0;
+
+ if (restrict_handle_inheritance < 0)
+ restrict_handle_inheritance = core_restrict_inherited_handles;
+ /*
+ * The following code to restrict which handles are inherited seems
+ * to work properly only on Windows 7 and later, so let's disable it
+ * on Windows Vista and 2008.
+ */
+ if (restrict_handle_inheritance < 0)
+ restrict_handle_inheritance = GetVersion() >> 16 >= 7601;
do_unset_environment_variables();
@@ -1435,11 +1503,23 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
CloseHandle(cons);
}
memset(&si, 0, sizeof(si));
- si.cb = sizeof(si);
- si.dwFlags = STARTF_USESTDHANDLES;
- si.hStdInput = winansi_get_osfhandle(fhin);
- si.hStdOutput = winansi_get_osfhandle(fhout);
- si.hStdError = winansi_get_osfhandle(fherr);
+ si.StartupInfo.cb = sizeof(si);
+ si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
+ si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
+ si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
+
+ /* The list of handles cannot contain duplicates */
+ if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
+ stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
+ if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
+ si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
+ stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
+ if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
+ si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
+ si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
+ stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
+ if (stdhandles_count)
+ si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
if (*argv && !strcmp(cmd, *argv))
wcmd[0] = L'\0';
@@ -1472,16 +1552,98 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
wenvblk = make_environment_block(deltaenv);
memset(&pi, 0, sizeof(pi));
- ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL, TRUE,
- flags, wenvblk, dir ? wdir : NULL, &si, &pi);
+ if (restrict_handle_inheritance && stdhandles_count &&
+ (InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
+ GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
+ (attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
+ (HeapAlloc(GetProcessHeap(), 0, size))) &&
+ InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
+ UpdateProcThreadAttribute(attr_list, 0,
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ stdhandles,
+ stdhandles_count * sizeof(HANDLE),
+ NULL, NULL)) {
+ si.lpAttributeList = attr_list;
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+ }
+
+ ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
+ stdhandles_count ? TRUE : FALSE,
+ flags, wenvblk, dir ? wdir : NULL,
+ &si.StartupInfo, &pi);
+
+ /*
+ * On Windows 2008 R2, it seems that specifying certain types of handles
+ * (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
+ * error. Rather than playing finicky and fragile games, let's just try
+ * to detect this situation and simply try again without restricting any
+ * handle inheritance. This is still better than failing to create
+ * processes.
+ */
+ if (!ret && restrict_handle_inheritance && stdhandles_count) {
+ DWORD err = GetLastError();
+ struct strbuf buf = STRBUF_INIT;
+
+ if (err != ERROR_NO_SYSTEM_RESOURCES &&
+ /*
+ * On Windows 7 and earlier, handles on pipes and character
+ * devices are inherited automatically, and cannot be
+ * specified in the thread handle list. Rather than trying
+ * to catch each and every corner case (and running the
+ * chance of *still* forgetting a few), let's just fall
+ * back to creating the process without trying to limit the
+ * handle inheritance.
+ */
+ !(err == ERROR_INVALID_PARAMETER &&
+ GetVersion() >> 16 < 9200) &&
+ !getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
+ DWORD fl = 0;
+ int i;
+
+ setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
+
+ for (i = 0; i < stdhandles_count; i++) {
+ HANDLE h = stdhandles[i];
+ strbuf_addf(&buf, "handle #%d: %p (type %lx, "
+ "handle info (%d) %lx\n", i, h,
+ GetFileType(h),
+ GetHandleInformation(h, &fl),
+ fl);
+ }
+ strbuf_addstr(&buf, "\nThis is a bug; please report it "
+ "at\nhttps://github.com/git-for-windows/"
+ "git/issues/new\n\n"
+ "To suppress this warning, please set "
+ "the environment variable\n\n"
+ "\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
+ "\n");
+ }
+ restrict_handle_inheritance = 0;
+ flags &= ~EXTENDED_STARTUPINFO_PRESENT;
+ ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
+ TRUE, flags, wenvblk, dir ? wdir : NULL,
+ &si.StartupInfo, &pi);
+ if (!ret)
+ errno = err_win_to_posix(GetLastError());
+ if (ret && buf.len) {
+ warning("failed to restrict file handles (%ld)\n\n%s",
+ err, buf.buf);
+ }
+ strbuf_release(&buf);
+ } else if (!ret)
+ errno = err_win_to_posix(GetLastError());
+
+ if (si.lpAttributeList)
+ DeleteProcThreadAttributeList(si.lpAttributeList);
+ if (attr_list)
+ HeapFree(GetProcessHeap(), 0, attr_list);
free(wenvblk);
free(wargs);
- if (!ret) {
- errno = ENOENT;
+ if (!ret)
return -1;
- }
+
CloseHandle(pi.hThread);
/*
@@ -1566,7 +1728,7 @@ static int try_shell_exec(const char *cmd, char *const *argv)
while (argv[argc]) argc++;
ALLOC_ARRAY(argv2, argc + 1);
argv2[0] = (char *)cmd; /* full path to the script file */
- memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
+ COPY_ARRAY(&argv2[1], &argv[1], argc);
exec_id = trace2_exec(prog, argv2);
pid = mingw_spawnv(prog, argv2, 1);
if (pid >= 0) {
@@ -2367,6 +2529,50 @@ static void setup_windows_environment(void)
}
}
+int is_valid_win32_path(const char *path)
+{
+ int preceding_space_or_period = 0, i = 0, periods = 0;
+
+ if (!protect_ntfs)
+ return 1;
+
+ skip_dos_drive_prefix((char **)&path);
+
+ for (;;) {
+ char c = *(path++);
+ switch (c) {
+ case '\0':
+ case '/': case '\\':
+ /* cannot end in ` ` or `.`, except for `.` and `..` */
+ if (preceding_space_or_period &&
+ (i != periods || periods > 2))
+ return 0;
+ if (!c)
+ return 1;
+
+ i = periods = preceding_space_or_period = 0;
+ continue;
+ case '.':
+ periods++;
+ /* fallthru */
+ case ' ':
+ preceding_space_or_period = 1;
+ i++;
+ continue;
+ case ':': /* DOS drive prefix was already skipped */
+ case '<': case '>': case '"': case '|': case '?': case '*':
+ /* illegal character */
+ return 0;
+ default:
+ if (c > '\0' && c < '\x20')
+ /* illegal character */
+ return 0;
+ }
+ preceding_space_or_period = 0;
+ i++;
+ }
+}
+
#if !defined(_MSC_VER)
/*
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
diff --git a/compat/mingw.h b/compat/mingw.h
index 9ad204c57c..3ec9fc36a5 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -453,6 +453,20 @@ char *mingw_query_user_email(void);
#endif
/**
+ * Verifies that the given path is a valid one on Windows.
+ *
+ * In particular, path segments are disallowed which
+ *
+ * - end in a period or a space (except the special directories `.` and `..`).
+ *
+ * - contain any of the reserved characters, e.g. `:`, `;`, `*`, etc
+ *
+ * Returns 1 upon success, otherwise 0.
+ */
+int is_valid_win32_path(const char *path);
+#define is_valid_path(path) is_valid_win32_path(path)
+
+/**
* Converts UTF-8 encoded string to UTF-16LE.
*
* To support repositories with legacy-encoded file names, invalid UTF-8 bytes
@@ -558,7 +572,7 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen);
/*
* A critical section used in the implementation of the spawn
- * functions (mingw_spawnv[p]e()) and waitpid(). Intialised in
+ * functions (mingw_spawnv[p]e()) and waitpid(). Initialised in
* the replacement main() macro below.
*/
extern CRITICAL_SECTION pinfo_cs;
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
index 9134349590..814845d4b3 100644
--- a/compat/nedmalloc/malloc.c.h
+++ b/compat/nedmalloc/malloc.c.h
@@ -1564,7 +1564,7 @@ static FORCEINLINE void* win32direct_mmap(size_t size) {
return (ptr != 0)? ptr: MFAIL;
}
-/* This function supports releasing coalesed segments */
+/* This function supports releasing coalesced segments */
static FORCEINLINE int win32munmap(void* ptr, size_t size) {
MEMORY_BASIC_INFORMATION minfo;
char* cptr = (char*)ptr;
@@ -1655,7 +1655,7 @@ static FORCEINLINE int win32munmap(void* ptr, size_t size) {
#define CALL_MREMAP(addr, osz, nsz, mv) MFAIL
#endif /* HAVE_MMAP && HAVE_MREMAP */
-/* mstate bit set if continguous morecore disabled or failed */
+/* mstate bit set if contiguous morecore disabled or failed */
#define USE_NONCONTIGUOUS_BIT (4U)
/* segment bit set in create_mspace_with_base */
@@ -2485,7 +2485,7 @@ typedef struct malloc_segment* msegmentptr;
Trim support
Fields holding the amount of unused topmost memory that should trigger
- timming, and a counter to force periodic scanning to release unused
+ timing, and a counter to force periodic scanning to release unused
non-topmost segments.
Locking
diff --git a/compat/obstack.h b/compat/obstack.h
index ae36ed6a66..01e7c81840 100644
--- a/compat/obstack.h
+++ b/compat/obstack.h
@@ -79,7 +79,7 @@ change its address during its lifetime.
When the chars burst over a chunk boundary, we allocate a larger
chunk, and then copy the partly formed object from the end of the old
chunk to the beginning of the new larger chunk. We then carry on
-accreting characters to the end of the object as we normally would.
+accrediting characters to the end of the object as we normally would.
A special macro is provided to add a single char at a time to a
growing object. This allows the use of register variables, which
diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c
index c0d838834a..d1bc09e49b 100644
--- a/compat/regex/regcomp.c
+++ b/compat/regex/regcomp.c
@@ -3462,7 +3462,7 @@ build_equiv_class (bitset_t sbcset, const unsigned char *name)
/* This isn't a valid character. */
return REG_ECOLLATE;
- /* Build single byte matcing table for this equivalence class. */
+ /* Build single byte matching table for this equivalence class. */
char_buf[1] = (unsigned char) '\0';
len = weights[idx1 & 0xffffff];
for (ch = 0; ch < SBC_MAX; ++ch)
diff --git a/compat/regex/regex.h b/compat/regex/regex.h
index 4d81358a83..08a2609663 100644
--- a/compat/regex/regex.h
+++ b/compat/regex/regex.h
@@ -322,7 +322,7 @@ typedef enum
/* POSIX regcomp return error codes. (In the order listed in the
standard.) */
REG_BADPAT, /* Invalid pattern. */
- REG_ECOLLATE, /* Inalid collating element. */
+ REG_ECOLLATE, /* Invalid collating element. */
REG_ECTYPE, /* Invalid character class name. */
REG_EESCAPE, /* Trailing backslash. */
REG_ESUBREG, /* Invalid back reference. */
diff --git a/compat/regex/regex_internal.c b/compat/regex/regex_internal.c
index 59bf151336..ec51cf3446 100644
--- a/compat/regex/regex_internal.c
+++ b/compat/regex/regex_internal.c
@@ -1616,7 +1616,7 @@ free_state (re_dfastate_t *state)
re_free (state);
}
-/* Create the new state which is independ of contexts.
+/* Create the new state which is independent of contexts.
Return the new state if succeeded, otherwise return NULL. */
static re_dfastate_t *
diff --git a/compat/regex/regexec.c b/compat/regex/regexec.c
index 1b5d89fd5e..49358ae475 100644
--- a/compat/regex/regexec.c
+++ b/compat/regex/regexec.c
@@ -2420,7 +2420,7 @@ find_recover_state (reg_errcode_t *err, re_match_context_t *mctx)
/* From the node set CUR_NODES, pick up the nodes whose types are
OP_OPEN_SUBEXP and which have corresponding back references in the regular
expression. And register them to use them later for evaluating the
- correspoding back references. */
+ corresponding back references. */
static reg_errcode_t
internal_function
@@ -3347,7 +3347,7 @@ build_trtable (const re_dfa_t *dfa, re_dfastate_t *state)
dests_node = dests_alloc->dests_node;
dests_ch = dests_alloc->dests_ch;
- /* Initialize transiton table. */
+ /* Initialize transition table. */
state->word_trtable = state->trtable = NULL;
/* At first, group all nodes belonging to `state' into several
diff --git a/compat/vcbuild/find_vs_env.bat b/compat/vcbuild/find_vs_env.bat
index 40194dd230..b35d264c0e 100644
--- a/compat/vcbuild/find_vs_env.bat
+++ b/compat/vcbuild/find_vs_env.bat
@@ -18,7 +18,7 @@ REM and MAKE, we must blend these two different worlds. This script
REM attempts to do that.
REM ================================================================
REM This BAT file starts in a plain (non-developer) command prompt,
-REM searches for the "best" commmand prompt setup script, installs
+REM searches for the "best" command prompt setup script, installs
REM it into the current CMD process, and exports the various MSVC
REM environment variables for use by MAKE.
REM
diff --git a/compat/win32/path-utils.c b/compat/win32/path-utils.c
index d9d3641de8..ebf2f12eb6 100644
--- a/compat/win32/path-utils.c
+++ b/compat/win32/path-utils.c
@@ -1,5 +1,29 @@
#include "../../git-compat-util.h"
+int win32_has_dos_drive_prefix(const char *path)
+{
+ int i;
+
+ /*
+ * Does it start with an ASCII letter (i.e. highest bit not set),
+ * followed by a colon?
+ */
+ if (!(0x80 & (unsigned char)*path))
+ return *path && path[1] == ':' ? 2 : 0;
+
+ /*
+ * While drive letters must be letters of the English alphabet, it is
+ * possible to assign virtually _any_ Unicode character via `subst` as
+ * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
+ * like this:
+ *
+ * subst ֍: %USERPROFILE%\Desktop
+ */
+ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
+ ; /* skip first UTF-8 character */
+ return path[i] == ':' ? i + 1 : 0;
+}
+
int win32_skip_dos_drive_prefix(char **path)
{
int ret = has_dos_drive_prefix(*path);
diff --git a/compat/win32/path-utils.h b/compat/win32/path-utils.h
index 8ed062a6b7..f2e70872cd 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -1,8 +1,9 @@
#ifndef WIN32_PATH_UTILS_H
#define WIN32_PATH_UTILS_H
-#define has_dos_drive_prefix(path) \
- (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+int win32_has_dos_drive_prefix(const char *path);
+#define has_dos_drive_prefix win32_has_dos_drive_prefix
+
int win32_skip_dos_drive_prefix(char **path);
#define skip_dos_drive_prefix win32_skip_dos_drive_prefix
static inline int win32_is_dir_sep(int c)
diff --git a/compat/winansi.c b/compat/winansi.c
index 54fd701cbf..c27b20a79d 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -662,10 +662,20 @@ void winansi_init(void)
*/
HANDLE winansi_get_osfhandle(int fd)
{
+ HANDLE ret;
+
if (fd == 1 && (fd_is_interactive[1] & FD_SWAPPED))
return hconsole1;
if (fd == 2 && (fd_is_interactive[2] & FD_SWAPPED))
return hconsole2;
- return (HANDLE)_get_osfhandle(fd);
+ ret = (HANDLE)_get_osfhandle(fd);
+
+ /*
+ * There are obviously circumstances under which _get_osfhandle()
+ * returns (HANDLE)-2. This is not documented anywhere, but that is so
+ * clearly an invalid handle value that we can just work around this
+ * and return the correct value for invalid handles.
+ */
+ return ret == (HANDLE)-2 ? INVALID_HANDLE_VALUE : ret;
}