summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
Diffstat (limited to 'compat')
-rw-r--r--compat/access.c31
-rw-r--r--compat/bswap.h5
-rw-r--r--compat/fileno.c2
-rw-r--r--compat/mingw.c741
-rw-r--r--compat/mingw.h72
-rw-r--r--compat/msvc.h10
-rw-r--r--compat/nedmalloc/malloc.c.h12
-rw-r--r--compat/obstack.h10
-rw-r--r--compat/poll/poll.c4
-rw-r--r--compat/qsort.c62
-rw-r--r--compat/regex/regcomp.c2
-rw-r--r--compat/regex/regex.h7
-rw-r--r--compat/regex/regex_internal.c2
-rw-r--r--compat/regex/regexec.c4
-rw-r--r--compat/terminal.c249
-rw-r--r--compat/terminal.h3
-rw-r--r--compat/vcbuild/.gitignore3
-rw-r--r--compat/vcbuild/README62
-rw-r--r--compat/vcbuild/find_vs_env.bat168
-rwxr-xr-xcompat/vcbuild/scripts/clink.pl93
-rw-r--r--compat/vcbuild/vcpkg_copy_dlls.bat39
-rw-r--r--compat/vcbuild/vcpkg_install.bat80
-rw-r--r--compat/win32/git.manifest25
-rw-r--r--compat/win32/path-utils.c24
-rw-r--r--compat/win32/path-utils.h10
-rw-r--r--compat/win32/pthread.h8
-rw-r--r--compat/win32/trace2_win32_process_info.c191
-rw-r--r--compat/winansi.c49
28 files changed, 1580 insertions, 388 deletions
diff --git a/compat/access.c b/compat/access.c
new file mode 100644
index 0000000000..19fda3e877
--- /dev/null
+++ b/compat/access.c
@@ -0,0 +1,31 @@
+#define COMPAT_CODE_ACCESS
+#include "../git-compat-util.h"
+
+/* Do the same thing access(2) does, but use the effective uid,
+ * and don't make the mistake of telling root that any file is
+ * executable. This version uses stat(2).
+ */
+int git_access(const char *path, int mode)
+{
+ struct stat st;
+
+ /* do not interfere a normal user */
+ if (geteuid())
+ return access(path, mode);
+
+ if (stat(path, &st) < 0)
+ return -1;
+
+ /* Root can read or write any file. */
+ if (!(mode & X_OK))
+ return 0;
+
+ /* Root can execute any file that has any one of the execute
+ * bits set.
+ */
+ if (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))
+ return 0;
+
+ errno = EACCES;
+ return -1;
+}
diff --git a/compat/bswap.h b/compat/bswap.h
index 5078ce5ecc..e4e25735ce 100644
--- a/compat/bswap.h
+++ b/compat/bswap.h
@@ -1,3 +1,6 @@
+#ifndef COMPAT_BSWAP_H
+#define COMPAT_BSWAP_H
+
/*
* Let's make sure we always have a sane definition for ntohl()/htonl().
* Some libraries define those as a function call, just to perform byte
@@ -210,3 +213,5 @@ static inline void put_be64(void *ptr, uint64_t value)
}
#endif
+
+#endif /* COMPAT_BSWAP_H */
diff --git a/compat/fileno.c b/compat/fileno.c
index 7b105f4cd7..8e80ef335d 100644
--- a/compat/fileno.c
+++ b/compat/fileno.c
@@ -1,4 +1,4 @@
-#define COMPAT_CODE
+#define COMPAT_CODE_FILENO
#include "../git-compat-util.h"
int git_fileno(FILE *stream)
diff --git a/compat/mingw.c b/compat/mingw.c
index 8141f77189..d14065d60e 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -13,6 +13,19 @@
static const int delay[] = { 0, 1, 10, 20, 40 };
+void open_in_gdb(void)
+{
+ static struct child_process cp = CHILD_PROCESS_INIT;
+ extern char *_pgmptr;
+
+ argv_array_pushl(&cp.args, "mintty", "gdb", NULL);
+ argv_array_pushf(&cp.args, "--pid=%d", getpid());
+ cp.clean_on_exit = 1;
+ if (start_command(&cp) < 0)
+ die_errno("Could not start gdb");
+ sleep(1);
+}
+
int err_win_to_posix(DWORD winerr)
{
int error = ENOSYS;
@@ -114,6 +127,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 +226,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 +246,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;
}
@@ -363,6 +387,8 @@ static inline int needs_hiding(const char *path)
/* ignore trailing slashes */
if (*path)
basename = path;
+ else
+ break;
}
if (hide_dotfiles == HIDE_DOTFILES_TRUE)
@@ -390,6 +416,12 @@ int mingw_mkdir(const char *path, int mode)
{
int ret;
wchar_t wpath[MAX_PATH];
+
+ if (!is_valid_win32_path(path, 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+
if (xutftowcs_path(wpath, path) < 0)
return -1;
ret = _wmkdir(wpath);
@@ -463,7 +495,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;
@@ -471,16 +503,21 @@ int mingw_open (const char *filename, int oflags, ...)
mode = va_arg(args, int);
va_end(args);
- if (filename && !strcmp(filename, "/dev/null"))
- filename = "nul";
+ if (!is_valid_win32_path(filename, !create)) {
+ errno = create ? EINVAL : ENOENT;
+ return -1;
+ }
if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
open_fn = mingw_open_append;
else
open_fn = _wopen;
- if (xutftowcs_path(wfilename, filename) < 0)
+ if (filename && !strcmp(filename, "/dev/null"))
+ wcscpy(wfilename, L"nul");
+ else if (xutftowcs_path(wfilename, filename) < 0)
return -1;
+
fd = open_fn(wfilename, oflags, mode);
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
@@ -538,10 +575,17 @@ FILE *mingw_fopen (const char *filename, const char *otype)
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
- filename = "nul";
- if (xutftowcs_path(wfilename, filename) < 0 ||
- xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+ wcscpy(wfilename, L"nul");
+ else if (!is_valid_win32_path(filename, 1)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ } else if (xutftowcs_path(wfilename, filename) < 0)
+ return NULL;
+
+ if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
error("could not unhide %s", filename);
return NULL;
@@ -560,10 +604,17 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
FILE *file;
wchar_t wfilename[MAX_PATH], wotype[4];
if (filename && !strcmp(filename, "/dev/null"))
- filename = "nul";
- if (xutftowcs_path(wfilename, filename) < 0 ||
- xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+ wcscpy(wfilename, L"nul");
+ else if (!is_valid_win32_path(filename, 1)) {
+ int create = otype && strchr(otype, 'w');
+ errno = create ? EINVAL : ENOENT;
+ return NULL;
+ } else if (xutftowcs_path(wfilename, filename) < 0)
+ return NULL;
+
+ if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
return NULL;
+
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
error("could not unhide %s", filename);
return NULL;
@@ -984,16 +1035,16 @@ int pipe(int filedes[2])
struct tm *gmtime_r(const time_t *timep, struct tm *result)
{
- /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
- memcpy(result, gmtime(timep), sizeof(struct tm));
- return result;
+ if (gmtime_s(result, timep) == 0)
+ return result;
+ return NULL;
}
struct tm *localtime_r(const time_t *timep, struct tm *result)
{
- /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
- memcpy(result, localtime(timep), sizeof(struct tm));
- return result;
+ if (localtime_s(result, timep) == 0)
+ return result;
+ return NULL;
}
char *mingw_getcwd(char *pointer, int len)
@@ -1052,7 +1103,7 @@ static const char *quote_arg_msvc(const char *arg)
p++;
len++;
}
- if (*p == '"')
+ if (*p == '"' || !*p)
n += count*2 + 1;
continue;
}
@@ -1074,16 +1125,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;
}
@@ -1096,13 +1150,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;
}
@@ -1112,7 +1167,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);
@@ -1161,14 +1216,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
int isexe, int exe_only)
{
char path[MAX_PATH];
+ wchar_t wpath[MAX_PATH];
snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
- if (!isexe && access(path, F_OK) == 0)
+ if (xutftowcs_path(wpath, path) < 0)
+ return NULL;
+
+ if (!isexe && _waccess(wpath, F_OK) == 0)
return xstrdup(path);
- path[strlen(path)-4] = '\0';
- if ((!exe_only || isexe) && access(path, F_OK) == 0)
- if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
+ wpath[wcslen(wpath)-4] = '\0';
+ if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) {
+ if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) {
+ path[strlen(path)-4] = '\0';
return xstrdup(path);
+ }
+ }
return NULL;
}
@@ -1183,7 +1245,7 @@ static char *path_lookup(const char *cmd, int exe_only)
int len = strlen(cmd);
int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
- if (strchr(cmd, '/') || strchr(cmd, '\\'))
+ if (strpbrk(cmd, "/\\"))
return xstrdup(cmd);
path = mingw_getenv("PATH");
@@ -1229,11 +1291,6 @@ static int wenvcmp(const void *a, const void *b)
return _wcsnicmp(p, q, p_len);
}
-/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
-#ifndef INTERNAL_QSORT
-#include "qsort.c"
-#endif
-
/*
* Build an environment block combining the inherited environment
* merged with the given list of settings.
@@ -1265,15 +1322,15 @@ static wchar_t *make_environment_block(char **deltaenv)
}
ALLOC_ARRAY(result, size);
- memcpy(result, wenv, size * sizeof(*wenv));
+ COPY_ARRAY(result, wenv, size);
FreeEnvironmentStringsW(wenv);
return result;
}
/*
* If there is a deltaenv, let's accumulate all keys into `array`,
- * sort them using the stable git_qsort() and then copy, skipping
- * duplicate keys
+ * sort them using the stable git_stable_qsort() and then copy,
+ * skipping duplicate keys
*/
for (p = wenv; p && *p; ) {
ALLOC_GROW(array, nr + 1, alloc);
@@ -1296,7 +1353,7 @@ static wchar_t *make_environment_block(char **deltaenv)
p += wlen + 1;
}
- git_qsort(array, nr, sizeof(*array), wenvcmp);
+ git_stable_qsort(array, nr, sizeof(*array), wenvcmp);
ALLOC_ARRAY(result, size + delta_size);
for (p = result, i = 0; i < nr; i++) {
@@ -1309,7 +1366,7 @@ static wchar_t *make_environment_block(char **deltaenv)
continue;
size = wcslen(array[i]) + 1;
- memcpy(p, array[i], size * sizeof(*p));
+ COPY_ARRAY(p, array[i], size);
p += size;
}
*p = L'\0';
@@ -1367,7 +1424,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;
@@ -1387,6 +1447,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;
}
@@ -1394,20 +1464,39 @@ 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();
/* Determine whether or not we are associated to a console */
- cons = CreateFile("CONOUT$", GENERIC_WRITE,
+ cons = CreateFileW(L"CONOUT$", GENERIC_WRITE,
FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if (cons == INVALID_HANDLE_VALUE) {
@@ -1431,13 +1520,27 @@ 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);
-
- if (xutftowcs_path(wcmd, cmd) < 0)
+ 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';
+ else if (xutftowcs_path(wcmd, cmd) < 0)
return -1;
if (dir && xutftowcs_path(wdir, dir) < 0)
return -1;
@@ -1466,16 +1569,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, 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);
/*
@@ -1551,19 +1736,26 @@ static int try_shell_exec(const char *cmd, char *const *argv)
return 0;
prog = path_lookup(interpr, 1);
if (prog) {
+ int exec_id;
int argc = 0;
- const char **argv2;
+#ifndef _MSC_VER
+ const
+#endif
+ char **argv2;
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) {
int status;
if (waitpid(pid, &status, 0) < 0)
status = 255;
+ trace2_exec_result(exec_id, status);
exit(status);
}
+ trace2_exec_result(exec_id, -1);
pid = 1; /* indicate that we tried but failed */
free(prog);
free(argv2);
@@ -1576,12 +1768,17 @@ int mingw_execv(const char *cmd, char *const *argv)
/* check if git_command is a shell script */
if (!try_shell_exec(cmd, argv)) {
int pid, status;
+ int exec_id;
+ exec_id = trace2_exec(cmd, (const char **)argv);
pid = mingw_spawnv(cmd, (const char **)argv, 0);
- if (pid < 0)
+ if (pid < 0) {
+ trace2_exec_result(exec_id, -1);
return -1;
+ }
if (waitpid(pid, &status, 0) < 0)
status = 255;
+ trace2_exec_result(exec_id, status);
exit(status);
}
return -1;
@@ -1649,6 +1846,8 @@ char *mingw_getenv(const char *name)
if (!w_key)
die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
xutftowcs(w_key, name, len_key);
+ /* GetEnvironmentVariableW() only sets the last error upon failure */
+ SetLastError(ERROR_SUCCESS);
len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
free(w_key);
@@ -1705,142 +1904,10 @@ int mingw_putenv(const char *namevalue)
return result ? 0 : -1;
}
-/*
- * Note, this isn't a complete replacement for getaddrinfo. It assumes
- * that service contains a numerical port, or that it is null. It
- * does a simple search using gethostbyname, and returns one IPv4 host
- * if one was found.
- */
-static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
- const struct addrinfo *hints,
- struct addrinfo **res)
-{
- struct hostent *h = NULL;
- struct addrinfo *ai;
- struct sockaddr_in *sin;
-
- if (node) {
- h = gethostbyname(node);
- if (!h)
- return WSAGetLastError();
- }
-
- ai = xmalloc(sizeof(struct addrinfo));
- *res = ai;
- ai->ai_flags = 0;
- ai->ai_family = AF_INET;
- ai->ai_socktype = hints ? hints->ai_socktype : 0;
- switch (ai->ai_socktype) {
- case SOCK_STREAM:
- ai->ai_protocol = IPPROTO_TCP;
- break;
- case SOCK_DGRAM:
- ai->ai_protocol = IPPROTO_UDP;
- break;
- default:
- ai->ai_protocol = 0;
- break;
- }
- ai->ai_addrlen = sizeof(struct sockaddr_in);
- if (hints && (hints->ai_flags & AI_CANONNAME))
- ai->ai_canonname = h ? xstrdup(h->h_name) : NULL;
- else
- ai->ai_canonname = NULL;
-
- sin = xcalloc(1, ai->ai_addrlen);
- sin->sin_family = AF_INET;
- /* Note: getaddrinfo is supposed to allow service to be a string,
- * which should be looked up using getservbyname. This is
- * currently not implemented */
- if (service)
- sin->sin_port = htons(atoi(service));
- if (h)
- sin->sin_addr = *(struct in_addr *)h->h_addr;
- else if (hints && (hints->ai_flags & AI_PASSIVE))
- sin->sin_addr.s_addr = INADDR_ANY;
- else
- sin->sin_addr.s_addr = INADDR_LOOPBACK;
- ai->ai_addr = (struct sockaddr *)sin;
- ai->ai_next = NULL;
- return 0;
-}
-
-static void WSAAPI freeaddrinfo_stub(struct addrinfo *res)
-{
- free(res->ai_canonname);
- free(res->ai_addr);
- free(res);
-}
-
-static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen,
- char *host, DWORD hostlen,
- char *serv, DWORD servlen, int flags)
-{
- const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
- if (sa->sa_family != AF_INET)
- return EAI_FAMILY;
- if (!host && !serv)
- return EAI_NONAME;
-
- if (host && hostlen > 0) {
- struct hostent *ent = NULL;
- if (!(flags & NI_NUMERICHOST))
- ent = gethostbyaddr((const char *)&sin->sin_addr,
- sizeof(sin->sin_addr), AF_INET);
-
- if (ent)
- snprintf(host, hostlen, "%s", ent->h_name);
- else if (flags & NI_NAMEREQD)
- return EAI_NONAME;
- else
- snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
- }
-
- if (serv && servlen > 0) {
- struct servent *ent = NULL;
- if (!(flags & NI_NUMERICSERV))
- ent = getservbyport(sin->sin_port,
- flags & NI_DGRAM ? "udp" : "tcp");
-
- if (ent)
- snprintf(serv, servlen, "%s", ent->s_name);
- else
- snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
- }
-
- return 0;
-}
-
-static HMODULE ipv6_dll = NULL;
-static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res);
-static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service,
- const struct addrinfo *hints,
- struct addrinfo **res);
-static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
- char *host, DWORD hostlen,
- char *serv, DWORD servlen, int flags);
-/*
- * gai_strerror is an inline function in the ws2tcpip.h header, so we
- * don't need to try to load that one dynamically.
- */
-
-static void socket_cleanup(void)
-{
- WSACleanup();
- if (ipv6_dll)
- FreeLibrary(ipv6_dll);
- ipv6_dll = NULL;
- ipv6_freeaddrinfo = freeaddrinfo_stub;
- ipv6_getaddrinfo = getaddrinfo_stub;
- ipv6_getnameinfo = getnameinfo_stub;
-}
-
static void ensure_socket_initialization(void)
{
WSADATA wsa;
static int initialized = 0;
- const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL };
- const char **name;
if (initialized)
return;
@@ -1849,35 +1916,7 @@ static void ensure_socket_initialization(void)
die("unable to initialize winsock subsystem, error %d",
WSAGetLastError());
- for (name = libraries; *name; name++) {
- ipv6_dll = LoadLibraryExA(*name, NULL,
- LOAD_LIBRARY_SEARCH_SYSTEM32);
- if (!ipv6_dll)
- continue;
-
- ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *))
- GetProcAddress(ipv6_dll, "freeaddrinfo");
- ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *,
- const struct addrinfo *,
- struct addrinfo **))
- GetProcAddress(ipv6_dll, "getaddrinfo");
- ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *,
- socklen_t, char *, DWORD,
- char *, DWORD, int))
- GetProcAddress(ipv6_dll, "getnameinfo");
- if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
- FreeLibrary(ipv6_dll);
- ipv6_dll = NULL;
- } else
- break;
- }
- if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
- ipv6_freeaddrinfo = freeaddrinfo_stub;
- ipv6_getaddrinfo = getaddrinfo_stub;
- ipv6_getnameinfo = getnameinfo_stub;
- }
-
- atexit(socket_cleanup);
+ atexit((void(*)(void)) WSACleanup);
initialized = 1;
}
@@ -1895,24 +1934,12 @@ struct hostent *mingw_gethostbyname(const char *host)
return gethostbyname(host);
}
-void mingw_freeaddrinfo(struct addrinfo *res)
-{
- ipv6_freeaddrinfo(res);
-}
-
+#undef getaddrinfo
int mingw_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res)
{
ensure_socket_initialization();
- return ipv6_getaddrinfo(node, service, hints, res);
-}
-
-int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
- char *host, DWORD hostlen, char *serv, DWORD servlen,
- int flags)
-{
- ensure_socket_initialization();
- return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+ return getaddrinfo(node, service, hints, res);
}
int mingw_socket(int domain, int type, int protocol)
@@ -2109,13 +2136,19 @@ struct passwd *getpwuid(int uid)
static unsigned initialized;
static char user_name[100];
static struct passwd *p;
+ wchar_t buf[100];
DWORD len;
if (initialized)
return p;
- len = sizeof(user_name);
- if (!GetUserName(user_name, &len)) {
+ len = ARRAY_SIZE(buf);
+ if (!GetUserNameW(buf, &len)) {
+ initialized = 1;
+ return NULL;
+ }
+
+ if (xwcstoutf(user_name, buf, sizeof(user_name)) < 0) {
initialized = 1;
return NULL;
}
@@ -2279,8 +2312,33 @@ int mingw_raise(int sig)
sigint_fn(SIGINT);
return 0;
+#if defined(_MSC_VER)
+ case SIGILL:
+ case SIGFPE:
+ case SIGSEGV:
+ case SIGTERM:
+ case SIGBREAK:
+ case SIGABRT:
+ case SIGABRT_COMPAT:
+ /*
+ * The <signal.h> header in the MS C Runtime defines 8 signals
+ * as being supported on the platform. Anything else causes an
+ * "Invalid signal or error" (which in DEBUG builds causes the
+ * Abort/Retry/Ignore dialog). We by-pass the CRT for things we
+ * already know will fail.
+ */
+ return raise(sig);
+ default:
+ errno = EINVAL;
+ return -1;
+
+#else
+
default:
return raise(sig);
+
+#endif
+
}
}
@@ -2462,20 +2520,161 @@ static void setup_windows_environment(void)
/* simulate TERM to enable auto-color (see color.c) */
if (!getenv("TERM"))
setenv("TERM", "cygwin", 1);
+
+ /* calculate HOME if not set */
+ if (!getenv("HOME")) {
+ /*
+ * try $HOMEDRIVE$HOMEPATH - the home share may be a network
+ * location, thus also check if the path exists (i.e. is not
+ * disconnected)
+ */
+ if ((tmp = getenv("HOMEDRIVE"))) {
+ struct strbuf buf = STRBUF_INIT;
+ strbuf_addstr(&buf, tmp);
+ if ((tmp = getenv("HOMEPATH"))) {
+ strbuf_addstr(&buf, tmp);
+ if (is_directory(buf.buf))
+ setenv("HOME", buf.buf, 1);
+ else
+ tmp = NULL; /* use $USERPROFILE */
+ }
+ strbuf_release(&buf);
+ }
+ /* use $USERPROFILE if the home share is not available */
+ if (!tmp && (tmp = getenv("USERPROFILE")))
+ setenv("HOME", tmp, 1);
+ }
}
+int is_valid_win32_path(const char *path, int allow_literal_nul)
+{
+ const char *p = path;
+ int preceding_space_or_period = 0, i = 0, periods = 0;
+
+ if (!protect_ntfs)
+ return 1;
+
+ skip_dos_drive_prefix((char **)&path);
+ goto segment_start;
+
+ 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;
+
+segment_start:
+ switch (*path) {
+ case 'a': case 'A': /* AUX */
+ if (((c = path[++i]) != 'u' && c != 'U') ||
+ ((c = path[++i]) != 'x' && c != 'X')) {
+not_a_reserved_name:
+ path += i;
+ continue;
+ }
+ break;
+ case 'c': case 'C': /* COM<N>, CON, CONIN$, CONOUT$ */
+ if ((c = path[++i]) != 'o' && c != 'O')
+ goto not_a_reserved_name;
+ c = path[++i];
+ if (c == 'm' || c == 'M') { /* COM<N> */
+ if (!isdigit(path[++i]))
+ goto not_a_reserved_name;
+ } else if (c == 'n' || c == 'N') { /* CON */
+ c = path[i + 1];
+ if ((c == 'i' || c == 'I') &&
+ ((c = path[i + 2]) == 'n' ||
+ c == 'N') &&
+ path[i + 3] == '$')
+ i += 3; /* CONIN$ */
+ else if ((c == 'o' || c == 'O') &&
+ ((c = path[i + 2]) == 'u' ||
+ c == 'U') &&
+ ((c = path[i + 3]) == 't' ||
+ c == 'T') &&
+ path[i + 4] == '$')
+ i += 4; /* CONOUT$ */
+ } else
+ goto not_a_reserved_name;
+ break;
+ case 'l': case 'L': /* LPT<N> */
+ if (((c = path[++i]) != 'p' && c != 'P') ||
+ ((c = path[++i]) != 't' && c != 'T') ||
+ !isdigit(path[++i]))
+ goto not_a_reserved_name;
+ break;
+ case 'n': case 'N': /* NUL */
+ if (((c = path[++i]) != 'u' && c != 'U') ||
+ ((c = path[++i]) != 'l' && c != 'L') ||
+ (allow_literal_nul &&
+ !path[i + 1] && p == path))
+ goto not_a_reserved_name;
+ break;
+ case 'p': case 'P': /* PRN */
+ if (((c = path[++i]) != 'r' && c != 'R') ||
+ ((c = path[++i]) != 'n' && c != 'N'))
+ goto not_a_reserved_name;
+ break;
+ default:
+ continue;
+ }
+
+ /*
+ * So far, this looks like a reserved name. Let's see
+ * whether it actually is one: trailing spaces, a file
+ * extension, or an NTFS Alternate Data Stream do not
+ * matter, the name is still reserved if any of those
+ * follow immediately after the actual name.
+ */
+ i++;
+ if (path[i] == ' ') {
+ preceding_space_or_period = 1;
+ while (path[++i] == ' ')
+ ; /* skip all spaces */
+ }
+
+ c = path[i];
+ if (c && c != '.' && c != ':' && c != '/' && c != '\\')
+ goto not_a_reserved_name;
+
+ /* contains reserved name */
+ return 0;
+ 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
* mingw startup code, see init.c in mingw runtime).
*/
int _CRT_glob = 0;
-
-typedef struct {
- int newmode;
-} _startupinfo;
-
-extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
- _startupinfo *si);
+#endif
static NORETURN void die_startup(void)
{
@@ -2553,19 +2752,40 @@ static void maybe_redirect_std_handles(void)
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
}
-void mingw_startup(void)
+#ifdef _MSC_VER
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+#endif
+
+/*
+ * We implement wmain() and compile with -municode, which would
+ * normally ignore main(), but we call the latter from the former
+ * so that we can handle non-ASCII command-line parameters
+ * appropriately.
+ *
+ * To be more compatible with the core git code, we convert
+ * argv into UTF8 and pass them directly to main().
+ */
+int wmain(int argc, const wchar_t **wargv)
{
- int i, maxlen, argc;
- char *buffer;
- wchar_t **wenv, **wargv;
- _startupinfo si;
+ int i, maxlen, exit_status;
+ char *buffer, **save;
+ const char **argv;
- maybe_redirect_std_handles();
+ trace2_initialize_clock();
- /* get wide char arguments and environment */
- si.newmode = 0;
- if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
- die_startup();
+#ifdef _MSC_VER
+#ifdef _DEBUG
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
+#endif
+
+#ifdef USE_MSVC_CRTDBG
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
+#endif
+
+ maybe_redirect_std_handles();
/* determine size of argv and environ conversion buffer */
maxlen = wcslen(wargv[0]);
@@ -2576,9 +2796,16 @@ void mingw_startup(void)
maxlen = 3 * maxlen + 1;
buffer = malloc_startup(maxlen);
- /* convert command line arguments and environment to UTF-8 */
+ /*
+ * Create a UTF-8 version of w_argv. Also create a "save" copy
+ * to remember all the string pointers because parse_options()
+ * will remove claimed items from the argv that we pass down.
+ */
+ ALLOC_ARRAY(argv, argc + 1);
+ ALLOC_ARRAY(save, argc + 1);
for (i = 0; i < argc; i++)
- __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+ argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+ argv[i] = save[i] = NULL;
free(buffer);
/* fix Windows specific environment settings */
@@ -2597,6 +2824,16 @@ void mingw_startup(void)
/* initialize Unicode console */
winansi_init();
+
+ /* invoke the real main() using our utf8 version of argv. */
+ exit_status = main(argc, argv);
+
+ for (i = 0; i < argc; i++)
+ free(save[i]);
+ free(save);
+ free(argv);
+
+ return exit_status;
}
int uname(struct utsname *buf)
diff --git a/compat/mingw.h b/compat/mingw.h
index 30d9fb3e36..e6fe810ba9 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -11,7 +11,7 @@ typedef _sigset_t sigset_t;
#undef _POSIX_THREAD_SAFE_FUNCTIONS
#endif
-extern int mingw_core_config(const char *var, const char *value, void *cb);
+int mingw_core_config(const char *var, const char *value, void *cb);
#define platform_core_config mingw_core_config
/*
@@ -147,8 +147,7 @@ static inline int fcntl(int fd, int cmd, ...)
errno = EINVAL;
return -1;
}
-/* bash cannot reliably detect negative return codes as failure */
-#define exit(code) exit((code) & 0xff)
+
#define sigemptyset(x) (void)0
static inline int sigaddset(sigset_t *set, int signum)
{ return 0; }
@@ -296,18 +295,10 @@ int mingw_gethostname(char *host, int namelen);
struct hostent *mingw_gethostbyname(const char *host);
#define gethostbyname mingw_gethostbyname
-void mingw_freeaddrinfo(struct addrinfo *res);
-#define freeaddrinfo mingw_freeaddrinfo
-
int mingw_getaddrinfo(const char *node, const char *service,
const struct addrinfo *hints, struct addrinfo **res);
#define getaddrinfo mingw_getaddrinfo
-int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
- char *host, DWORD hostlen, char *serv, DWORD servlen,
- int flags);
-#define getnameinfo mingw_getnameinfo
-
int mingw_socket(int domain, int type, int protocol);
#define socket mingw_socket
@@ -361,11 +352,13 @@ static inline int getrlimit(int resource, struct rlimit *rlp)
#ifndef __MINGW64_VERSION_MAJOR
#define off_t off64_t
#define lseek _lseeki64
+#ifndef _MSC_VER
struct timespec {
time_t tv_sec;
long tv_nsec;
};
#endif
+#endif
struct mingw_stat {
_dev_t st_dev;
@@ -450,7 +443,7 @@ static inline void convert_slashes(char *path)
*path = '/';
}
#define PATH_SEP ';'
-extern char *mingw_query_user_email(void);
+char *mingw_query_user_email(void);
#define query_user_email mingw_query_user_email
#if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
#define PRIuMAX "I64u"
@@ -460,6 +453,27 @@ extern 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
+ *
+ * - correspond to reserved names (such as `AUX`, `PRN`, etc)
+ *
+ * The `allow_literal_nul` parameter controls whether the path `NUL` should
+ * be considered valid (this makes sense e.g. before opening files, as it is
+ * perfectly legitimate to open `NUL` on Windows, just as it is to open
+ * `/dev/null` on Unix/Linux).
+ *
+ * Returns 1 upon success, otherwise 0.
+ */
+int is_valid_win32_path(const char *path, int allow_literal_nul);
+#define is_valid_path(path) is_valid_win32_path(path, 0)
+
+/**
* Converts UTF-8 encoded string to UTF-16LE.
*
* To support repositories with legacy-encoded file names, invalid UTF-8 bytes
@@ -565,26 +579,36 @@ 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;
/*
- * A replacement of main() that adds win32 specific initialization.
+ * Git, like most portable C applications, implements a main() function. On
+ * Windows, this main() function would receive parameters encoded in the
+ * current locale, but Git for Windows would prefer UTF-8 encoded parameters.
+ *
+ * To make that happen, we still declare main() here, and then declare and
+ * implement wmain() (which is the Unicode variant of main()) and compile with
+ * -municode. This wmain() function reencodes the parameters from UTF-16 to
+ * UTF-8 format, sets up a couple of other things as required on Windows, and
+ * then hands off to the main() function.
*/
+int wmain(int argc, const wchar_t **w_argv);
+int main(int argc, const char **argv);
-void mingw_startup(void);
-#define main(c,v) dummy_decl_mingw_main(void); \
-static int mingw_main(c,v); \
-int main(int argc, const char **argv) \
-{ \
- mingw_startup(); \
- return mingw_main(__argc, (void *)__argv); \
-} \
-static int mingw_main(c,v)
+/*
+ * For debugging: if a problem occurs, say, in a Git process that is spawned
+ * from another Git process which in turn is spawned from yet another Git
+ * process, it can be quite daunting to figure out what is going on.
+ *
+ * Call this function to open a new MinTTY (this assumes you are in Git for
+ * Windows' SDK) with a GDB that attaches to the current process right away.
+ */
+extern void open_in_gdb(void);
/*
* Used by Pthread API implementation for Windows
*/
-extern int err_win_to_posix(DWORD winerr);
+int err_win_to_posix(DWORD winerr);
diff --git a/compat/msvc.h b/compat/msvc.h
index 29a8ce8204..1d7a8c6145 100644
--- a/compat/msvc.h
+++ b/compat/msvc.h
@@ -6,6 +6,10 @@
#include <malloc.h>
#include <io.h>
+#pragma warning(disable: 4018) /* signed/unsigned comparison */
+#pragma warning(disable: 4244) /* type conversion, possible loss of data */
+#pragma warning(disable: 4090) /* 'function' : different 'const' qualifiers (ALLOC_GROW etc.)*/
+
/* porting function */
#define inline __inline
#define __inline__ __inline
@@ -18,6 +22,12 @@
#undef ERROR
+#define ftello _ftelli64
+
+typedef int sigset_t;
+/* open for reading, writing, or both (not in fcntl.h) */
+#define O_ACCMODE (_O_RDONLY | _O_WRONLY | _O_RDWR)
+
#include "compat/mingw.h"
#endif
diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h
index b833ff9225..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 */
@@ -1755,10 +1755,10 @@ static FORCEINLINE void pthread_release_lock (MLOCK_T *sl) {
assert(sl->l != 0);
assert(sl->threadid == CURRENT_THREAD);
if (--sl->c == 0) {
- sl->threadid = 0;
volatile unsigned int* lp = &sl->l;
int prev = 0;
int ret;
+ sl->threadid = 0;
__asm__ __volatile__ ("lock; xchgl %0, %1"
: "=r" (ret)
: "m" (*(lp)), "0"(prev)
@@ -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
@@ -3066,7 +3066,7 @@ static int init_mparams(void) {
#if !ONLY_MSPACES
/* Set up lock for main malloc area */
gm->mflags = mparams.default_mflags;
- INITIAL_LOCK(&gm->mutex);
+ (void)INITIAL_LOCK(&gm->mutex);
#endif
#if (FOOTERS && !INSECURE)
@@ -5017,7 +5017,7 @@ static mstate init_user_mstate(char* tbase, size_t tsize) {
mchunkptr msp = align_as_chunk(tbase);
mstate m = (mstate)(chunk2mem(msp));
memset(m, 0, msize);
- INITIAL_LOCK(&m->mutex);
+ (void)INITIAL_LOCK(&m->mutex);
msp->head = (msize|PINUSE_BIT|CINUSE_BIT);
m->seg.base = m->least_addr = tbase;
m->seg.size = m->footprint = m->max_footprint = tsize;
diff --git a/compat/obstack.h b/compat/obstack.h
index ced94d0118..f90a46d9b9 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
@@ -135,8 +135,10 @@ extern "C" {
alignment relative to 0. */
#define __PTR_ALIGN(B, P, A) \
- __BPTR_ALIGN (sizeof (PTR_INT_TYPE) < sizeof (void *) ? (B) : (char *) 0, \
- P, A)
+ (sizeof (PTR_INT_TYPE) < sizeof(void *) ? \
+ __BPTR_ALIGN((B), (P), (A)) : \
+ (void *)__BPTR_ALIGN((PTR_INT_TYPE)(void *)0, (PTR_INT_TYPE)(P), (A)) \
+ )
#include <string.h>
@@ -496,7 +498,7 @@ __extension__ \
( (h)->temp.tempint = (char *) (obj) - (char *) (h)->chunk, \
((((h)->temp.tempint > 0 \
&& (h)->temp.tempint < (h)->chunk_limit - (char *) (h)->chunk)) \
- ? (int) ((h)->next_free = (h)->object_base \
+ ? (ptrdiff_t) ((h)->next_free = (h)->object_base \
= (h)->temp.tempint + (char *) (h)->chunk) \
: (((obstack_free) ((h), (h)->temp.tempint + (char *) (h)->chunk), 0), 0)))
diff --git a/compat/poll/poll.c b/compat/poll/poll.c
index 4459408c7d..0e95dd493c 100644
--- a/compat/poll/poll.c
+++ b/compat/poll/poll.c
@@ -149,8 +149,8 @@ win32_compute_revents (HANDLE h, int *p_sought)
case FILE_TYPE_PIPE:
if (!once_only)
{
- NtQueryInformationFile = (PNtQueryInformationFile)
- GetProcAddress (GetModuleHandle ("ntdll.dll"),
+ NtQueryInformationFile = (PNtQueryInformationFile)(void (*)(void))
+ GetProcAddress (GetModuleHandleW (L"ntdll.dll"),
"NtQueryInformationFile");
once_only = TRUE;
}
diff --git a/compat/qsort.c b/compat/qsort.c
deleted file mode 100644
index 7d071afb70..0000000000
--- a/compat/qsort.c
+++ /dev/null
@@ -1,62 +0,0 @@
-#include "../git-compat-util.h"
-
-/*
- * A merge sort implementation, simplified from the qsort implementation
- * by Mike Haertel, which is a part of the GNU C Library.
- */
-
-static void msort_with_tmp(void *b, size_t n, size_t s,
- int (*cmp)(const void *, const void *),
- char *t)
-{
- char *tmp;
- char *b1, *b2;
- size_t n1, n2;
-
- if (n <= 1)
- return;
-
- n1 = n / 2;
- n2 = n - n1;
- b1 = b;
- b2 = (char *)b + (n1 * s);
-
- msort_with_tmp(b1, n1, s, cmp, t);
- msort_with_tmp(b2, n2, s, cmp, t);
-
- tmp = t;
-
- while (n1 > 0 && n2 > 0) {
- if (cmp(b1, b2) <= 0) {
- memcpy(tmp, b1, s);
- tmp += s;
- b1 += s;
- --n1;
- } else {
- memcpy(tmp, b2, s);
- tmp += s;
- b2 += s;
- --n2;
- }
- }
- if (n1 > 0)
- memcpy(tmp, b1, n1 * s);
- memcpy(b, t, (n - n2) * s);
-}
-
-void git_qsort(void *b, size_t n, size_t s,
- int (*cmp)(const void *, const void *))
-{
- const size_t size = st_mult(n, s);
- char buf[1024];
-
- if (size < sizeof(buf)) {
- /* The temporary array fits on the small on-stack buffer. */
- msort_with_tmp(b, n, s, cmp, buf);
- } else {
- /* It's somewhat large, so malloc it. */
- char *tmp = xmalloc(size);
- msort_with_tmp(b, n, s, cmp, tmp);
- free(tmp);
- }
-}
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..2d3412860d 100644
--- a/compat/regex/regex.h
+++ b/compat/regex/regex.h
@@ -41,6 +41,11 @@
extern "C" {
#endif
+#define regcomp git_regcomp
+#define regexec git_regexec
+#define regerror git_regerror
+#define regfree git_regfree
+
/* The following two types have to be signed and unsigned integer type
wide enough to hold a value of a pointer. For most ANSI compilers
ptrdiff_t and size_t should be likely OK. Still size of these two
@@ -322,7 +327,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/terminal.c b/compat/terminal.c
index fa13ee672d..35bca03d14 100644
--- a/compat/terminal.c
+++ b/compat/terminal.c
@@ -2,6 +2,9 @@
#include "compat/terminal.h"
#include "sigchain.h"
#include "strbuf.h"
+#include "run-command.h"
+#include "string-list.h"
+#include "hashmap.h"
#if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
@@ -32,7 +35,7 @@ static void restore_term(void)
term_fd = -1;
}
-static int disable_echo(void)
+static int disable_bits(tcflag_t bits)
{
struct termios t;
@@ -43,7 +46,7 @@ static int disable_echo(void)
old_term = t;
sigchain_push_common(restore_term_on_signal);
- t.c_lflag &= ~ECHO;
+ t.c_lflag &= ~bits;
if (!tcsetattr(term_fd, TCSAFLUSH, &t))
return 0;
@@ -53,17 +56,44 @@ error:
return -1;
}
+static int disable_echo(void)
+{
+ return disable_bits(ECHO);
+}
+
+static int enable_non_canonical(void)
+{
+ return disable_bits(ICANON | ECHO);
+}
+
#elif defined(GIT_WINDOWS_NATIVE)
#define INPUT_PATH "CONIN$"
#define OUTPUT_PATH "CONOUT$"
#define FORCE_TEXT "t"
+static int use_stty = 1;
+static struct string_list stty_restore = STRING_LIST_INIT_DUP;
static HANDLE hconin = INVALID_HANDLE_VALUE;
static DWORD cmode;
static void restore_term(void)
{
+ if (use_stty) {
+ int i;
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ if (stty_restore.nr == 0)
+ return;
+
+ argv_array_push(&cp.args, "stty");
+ for (i = 0; i < stty_restore.nr; i++)
+ argv_array_push(&cp.args, stty_restore.items[i].string);
+ run_command(&cp);
+ string_list_clear(&stty_restore, 0);
+ return;
+ }
+
if (hconin == INVALID_HANDLE_VALUE)
return;
@@ -72,8 +102,39 @@ static void restore_term(void)
hconin = INVALID_HANDLE_VALUE;
}
-static int disable_echo(void)
+static int disable_bits(DWORD bits)
{
+ if (use_stty) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+
+ argv_array_push(&cp.args, "stty");
+
+ if (bits & ENABLE_LINE_INPUT) {
+ string_list_append(&stty_restore, "icanon");
+ argv_array_push(&cp.args, "-icanon");
+ }
+
+ if (bits & ENABLE_ECHO_INPUT) {
+ string_list_append(&stty_restore, "echo");
+ argv_array_push(&cp.args, "-echo");
+ }
+
+ if (bits & ENABLE_PROCESSED_INPUT) {
+ string_list_append(&stty_restore, "-ignbrk");
+ string_list_append(&stty_restore, "intr");
+ string_list_append(&stty_restore, "^c");
+ argv_array_push(&cp.args, "ignbrk");
+ argv_array_push(&cp.args, "intr");
+ argv_array_push(&cp.args, "");
+ }
+
+ if (run_command(&cp) == 0)
+ return 0;
+
+ /* `stty` could not be executed; access the Console directly */
+ use_stty = 0;
+ }
+
hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
@@ -82,7 +143,7 @@ static int disable_echo(void)
GetConsoleMode(hconin, &cmode);
sigchain_push_common(restore_term_on_signal);
- if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
+ if (!SetConsoleMode(hconin, cmode & ~bits)) {
CloseHandle(hconin);
hconin = INVALID_HANDLE_VALUE;
return -1;
@@ -91,6 +152,47 @@ static int disable_echo(void)
return 0;
}
+static int disable_echo(void)
+{
+ return disable_bits(ENABLE_ECHO_INPUT);
+}
+
+static int enable_non_canonical(void)
+{
+ return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
+}
+
+/*
+ * Override `getchar()`, as the default implementation does not use
+ * `ReadFile()`.
+ *
+ * This poses a problem when we want to see whether the standard
+ * input has more characters, as the default of Git for Windows is to start the
+ * Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case
+ * our `poll()` emulation calls `PeekNamedPipe()`, which seems to require
+ * `ReadFile()` to be called first to work properly (it only reports 0
+ * available bytes, otherwise).
+ *
+ * So let's just override `getchar()` with a version backed by `ReadFile()` and
+ * go our merry ways from here.
+ */
+static int mingw_getchar(void)
+{
+ DWORD read = 0;
+ unsigned char ch;
+
+ if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
+ return EOF;
+
+ if (!read) {
+ error("Unexpected 0 read");
+ return EOF;
+ }
+
+ return ch;
+}
+#define getchar mingw_getchar
+
#endif
#ifndef FORCE_TEXT
@@ -137,6 +239,126 @@ char *git_terminal_prompt(const char *prompt, int echo)
return buf.buf;
}
+/*
+ * The `is_known_escape_sequence()` function returns 1 if the passed string
+ * corresponds to an Escape sequence that the terminal capabilities contains.
+ *
+ * To avoid depending on ncurses or other platform-specific libraries, we rely
+ * on the presence of the `infocmp` executable to do the job for us (failing
+ * silently if the program is not available or refused to run).
+ */
+struct escape_sequence_entry {
+ struct hashmap_entry entry;
+ char sequence[FLEX_ARRAY];
+};
+
+static int sequence_entry_cmp(const void *hashmap_cmp_fn_data,
+ const struct escape_sequence_entry *e1,
+ const struct escape_sequence_entry *e2,
+ const void *keydata)
+{
+ return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
+}
+
+static int is_known_escape_sequence(const char *sequence)
+{
+ static struct hashmap sequences;
+ static int initialized;
+
+ if (!initialized) {
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ char *p, *eol;
+
+ hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
+ NULL, 0);
+
+ argv_array_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
+ if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
+ strbuf_setlen(&buf, 0);
+
+ for (eol = p = buf.buf; *p; p = eol + 1) {
+ p = strchr(p, '=');
+ if (!p)
+ break;
+ p++;
+ eol = strchrnul(p, '\n');
+
+ if (starts_with(p, "\\E")) {
+ char *comma = memchr(p, ',', eol - p);
+ struct escape_sequence_entry *e;
+
+ p[0] = '^';
+ p[1] = '[';
+ FLEX_ALLOC_MEM(e, sequence, p, comma - p);
+ hashmap_entry_init(&e->entry,
+ strhash(e->sequence));
+ hashmap_add(&sequences, &e->entry);
+ }
+ if (!*eol)
+ break;
+ }
+ initialized = 1;
+ }
+
+ return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence);
+}
+
+int read_key_without_echo(struct strbuf *buf)
+{
+ static int warning_displayed;
+ int ch;
+
+ if (warning_displayed || enable_non_canonical() < 0) {
+ if (!warning_displayed) {
+ warning("reading single keystrokes not supported on "
+ "this platform; reading line instead");
+ warning_displayed = 1;
+ }
+
+ return strbuf_getline(buf, stdin);
+ }
+
+ strbuf_reset(buf);
+ ch = getchar();
+ if (ch == EOF) {
+ restore_term();
+ return EOF;
+ }
+ strbuf_addch(buf, ch);
+
+ if (ch == '\033' /* ESC */) {
+ /*
+ * We are most likely looking at an Escape sequence. Let's try
+ * to read more bytes, waiting at most half a second, assuming
+ * that the sequence is complete if we did not receive any byte
+ * within that time.
+ *
+ * Start by replacing the Escape byte with ^[ */
+ strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
+
+ /*
+ * Query the terminal capabilities once about all the Escape
+ * sequences it knows about, so that we can avoid waiting for
+ * half a second when we know that the sequence is complete.
+ */
+ while (!is_known_escape_sequence(buf->buf)) {
+ struct pollfd pfd = { .fd = 0, .events = POLLIN };
+
+ if (poll(&pfd, 1, 500) < 1)
+ break;
+
+ ch = getchar();
+ if (ch == EOF)
+ return 0;
+ strbuf_addch(buf, ch);
+ }
+ }
+
+ restore_term();
+ return 0;
+}
+
#else
char *git_terminal_prompt(const char *prompt, int echo)
@@ -144,4 +366,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
return getpass(prompt);
}
+int read_key_without_echo(struct strbuf *buf)
+{
+ static int warning_displayed;
+ const char *res;
+
+ if (!warning_displayed) {
+ warning("reading single keystrokes not supported on this "
+ "platform; reading line instead");
+ warning_displayed = 1;
+ }
+
+ res = getpass("");
+ strbuf_reset(buf);
+ if (!res)
+ return EOF;
+ strbuf_addstr(buf, res);
+ return 0;
+}
+
#endif
diff --git a/compat/terminal.h b/compat/terminal.h
index 97db7cd69d..a9d52b8464 100644
--- a/compat/terminal.h
+++ b/compat/terminal.h
@@ -3,4 +3,7 @@
char *git_terminal_prompt(const char *prompt, int echo);
+/* Read a single keystroke, without echoing it to the terminal */
+int read_key_without_echo(struct strbuf *buf);
+
#endif /* COMPAT_TERMINAL_H */
diff --git a/compat/vcbuild/.gitignore b/compat/vcbuild/.gitignore
new file mode 100644
index 0000000000..8f8b794ef3
--- /dev/null
+++ b/compat/vcbuild/.gitignore
@@ -0,0 +1,3 @@
+/vcpkg/
+/MSVC-DEFS-GEN
+/VCPKG-DEFS
diff --git a/compat/vcbuild/README b/compat/vcbuild/README
index 60fd873fe8..1b6dabf5a2 100644
--- a/compat/vcbuild/README
+++ b/compat/vcbuild/README
@@ -1,3 +1,65 @@
+The Steps to Build Git with VS2015 or VS2017 from the command line.
+
+1. Install the "vcpkg" open source package manager and build essential
+ third-party libraries. The steps for this have been captured in a
+ set of convenience scripts. These can be run from a stock Command
+ Prompt or from an SDK bash window:
+
+ $ cd <repo_root>
+ $ ./compat/vcbuild/vcpkg_install.bat
+
+ The vcpkg tools and all of the third-party sources will be installed
+ in this folder:
+ <repo_root>/compat/vcbuild/vcpkg/
+
+ A file will be created with a set of Makefile macros pointing to a
+ unified "include", "lib", and "bin" directory (release and debug) for
+ all of the required packages. This file will be included by the main
+ Makefile:
+ <repo_root>/compat/vcbuild/MSVC-DEFS-GEN
+
+2. OPTIONALLY copy the third-party *.dll and *.pdb files into the repo
+ root to make it easier to run and debug git.exe without having to
+ manipulate your PATH. This is especially true for debug sessions in
+ Visual Studio.
+
+ Use ONE of the following forms which should match how you want to
+ compile git.exe.
+
+ $ ./compat/vcbuild/vcpkg_copy_packages.bat debug
+ $ ./compat/vcbuild/vcpkg_copy_packages.bat release
+
+3. Build git using MSVC from an SDK bash window using one of the
+ following commands:
+
+ $ make MSVC=1
+ $ make MSVC=1 DEBUG=1
+
+================================================================
+
+Alternatively, run `make vcxproj` and then load the generated `git.sln` in
+Visual Studio. The initial build will install the vcpkg system and build the
+dependencies automatically. This will take a while.
+
+Instead of generating the `git.sln` file yourself (which requires a full Git
+for Windows SDK), you may want to consider fetching the `vs/master` branch of
+https://github.com/git-for-windows/git instead (which is updated automatically
+via CI running `make vcxproj`). The `vs/master` branch does not require a Git
+for Windows to build, but you can run the test scripts in a regular Git Bash.
+
+Note that `make vcxproj` will automatically add and commit the generated `.sln`
+and `.vcxproj` files to the repo. This is necessary to allow building a
+fully-testable Git in Visual Studio, where a regular Git Bash can be used to
+run the test scripts (as opposed to a full Git for Windows SDK): a number of
+build targets, such as Git commands implemented as Unix shell scripts (where
+`@@SHELL_PATH@@` and other placeholders are interpolated) require a full-blown
+Git for Windows SDK (which is about 10x the size of a regular Git for Windows
+installation).
+
+If your plan is to open a Pull Request with Git for Windows, it is a good idea
+to drop this commit before submitting.
+
+================================================================
The Steps of Build Git with VS2008
1. You need the build environment, which contains the Git dependencies
diff --git a/compat/vcbuild/find_vs_env.bat b/compat/vcbuild/find_vs_env.bat
new file mode 100644
index 0000000000..b35d264c0e
--- /dev/null
+++ b/compat/vcbuild/find_vs_env.bat
@@ -0,0 +1,168 @@
+@ECHO OFF
+REM ================================================================
+REM You can use either GCC (the default) or MSVC to build git
+REM using the GIT-SDK command line tools.
+REM $ make
+REM $ make MSVC=1
+REM
+REM GIT-SDK BASH windows inherit environment variables with all of
+REM the bin/lib/include paths for GCC. It DOES NOT inherit values
+REM for the corresponding MSVC tools.
+REM
+REM During normal (non-git) Windows development, you launch one
+REM of the provided "developer command prompts" to set environment
+REM variables for the MSVC tools.
+REM
+REM Therefore, to allow MSVC command line builds of git from BASH
+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" 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
+REM The output of this script should be written to a make "include
+REM file" and referenced by the top-level Makefile.
+REM
+REM See "config.mak.uname" (look for compat/vcbuild/MSVC-DEFS-GEN).
+REM ================================================================
+REM The provided command prompts are custom to each VS release and
+REM filled with lots of internal knowledge (such as Registry settings);
+REM even their names vary by release, so it is not appropriate for us
+REM to look inside them. Rather, just run them in a subordinate
+REM process and extract the settings we need.
+REM ================================================================
+REM
+REM Current (VS2017 and beyond)
+REM -------------------
+REM Visual Studio 2017 introduced a new installation layout and
+REM support for side-by-side installation of multiple versions of
+REM VS2017. Furthermore, these can all coexist with installations
+REM of previous versions of VS (which have a completely different
+REM layout on disk).
+REM
+REM VS2017 Update 2 introduced a "vswhere.exe" command:
+REM https://github.com/Microsoft/vswhere
+REM https://blogs.msdn.microsoft.com/heaths/2017/02/25/vswhere-available/
+REM https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
+REM
+REM VS2015
+REM ------
+REM Visual Studio 2015 uses the traditional VcVarsAll.
+REM
+REM Earlier Versions
+REM ----------------
+REM Currently unsupported.
+REM
+REM ================================================================
+REM Note: Throughout this script we use "dir <path> && <cmd>" rather
+REM than "if exist <path>" because of script problems with pathnames
+REM containing spaces.
+REM ================================================================
+
+REM Sanitize PATH to prevent git-sdk paths from confusing "wmic.exe"
+REM (called internally in some of the system BAT files).
+SET PATH=%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System32\Wbem;
+
+REM ================================================================
+
+:current
+ SET vs_where=C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe
+ dir "%vs_where%" >nul 2>nul && GOTO have_vs_where
+ GOTO not_2017
+
+:have_vs_where
+ REM Try to use VsWhere to get the location of VsDevCmd.
+
+ REM Keep VsDevCmd from cd'ing away.
+ SET VSCMD_START_DIR=.
+
+ REM Get the root of the VS product installation.
+ FOR /F "usebackq tokens=*" %%i IN (`"%vs_where%" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath`) DO @SET vs_ip=%%i
+
+ SET vs_devcmd=%vs_ip%\Common7\Tools\VsDevCmd.bat
+ dir "%vs_devcmd%" >nul 2>nul && GOTO have_vs_devcmd
+ GOTO not_2017
+
+:have_vs_devcmd
+ REM Use VsDevCmd to setup the environment of this process.
+ REM Setup CL for building 64-bit apps using 64-bit tools.
+ @call "%vs_devcmd%" -no_logo -arch=x64 -host_arch=x64
+
+ SET tgt=%VSCMD_ARG_TGT_ARCH%
+
+ SET mn=%VCToolsInstallDir%
+ SET msvc_includes=-I"%mn%INCLUDE"
+ SET msvc_libs=-L"%mn%lib\%tgt%"
+ SET msvc_bin_dir=%mn%bin\Host%VSCMD_ARG_HOST_ARCH%\%tgt%
+
+ SET sdk_dir=%WindowsSdkDir%
+ SET sdk_ver=%WindowsSDKVersion%
+ SET si=%sdk_dir%Include\%sdk_ver%
+ SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared"
+ SET sl=%sdk_dir%lib\%sdk_ver%
+ SET sdk_libs=-L"%sl%ucrt\%tgt%" -L"%sl%um\%tgt%"
+
+ SET vs_ver=%VisualStudioVersion%
+
+ GOTO print_vars
+
+REM ================================================================
+
+:not_2017
+ REM See if VS2015 is installed.
+
+ SET vs_2015_bat=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
+ dir "%vs_2015_bat%" >nul 2>nul && GOTO have_vs_2015
+ GOTO not_2015
+
+:have_vs_2015
+ REM Use VcVarsAll like the "x64 Native" command prompt.
+ REM Setup CL for building 64-bit apps using 64-bit tools.
+ @call "%vs_2015_bat%" amd64
+
+ REM Note that in VS2015 they use "x64" in some contexts and "amd64" in others.
+ SET mn=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\
+ SET msvc_includes=-I"%mn%INCLUDE"
+ SET msvc_libs=-L"%mn%lib\amd64"
+ SET msvc_bin_dir=%mn%bin\amd64
+
+ SET sdk_dir=%WindowsSdkDir%
+ SET sdk_ver=%WindowsSDKVersion%
+ SET si=%sdk_dir%Include\%sdk_ver%
+ SET sdk_includes=-I"%si%ucrt" -I"%si%um" -I"%si%shared" -I"%si%winrt"
+ SET sl=%sdk_dir%lib\%sdk_ver%
+ SET sdk_libs=-L"%sl%ucrt\x64" -L"%sl%um\x64"
+
+ SET vs_ver=%VisualStudioVersion%
+
+ GOTO print_vars
+
+REM ================================================================
+
+:not_2015
+ echo "ERROR: unsupported VS version (older than VS2015)" >&2
+ EXIT /B 1
+
+REM ================================================================
+
+:print_vars
+ REM Dump the essential vars to stdout to allow the main
+ REM Makefile to include it. See config.mak.uname.
+ REM Include DOS-style and BASH-style path for bin dir.
+
+ echo msvc_bin_dir=%msvc_bin_dir%
+ SET X1=%msvc_bin_dir:C:=/C%
+ SET X2=%X1:\=/%
+ echo msvc_bin_dir_msys=%X2%
+
+ echo msvc_includes=%msvc_includes%
+ echo msvc_libs=%msvc_libs%
+
+ echo sdk_includes=%sdk_includes%
+ echo sdk_libs=%sdk_libs%
+
+ echo vs_ver=%vs_ver%
+
+ EXIT /B 0
diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl
index a87d0da512..d9f71b7cbb 100755
--- a/compat/vcbuild/scripts/clink.pl
+++ b/compat/vcbuild/scripts/clink.pl
@@ -12,43 +12,120 @@
use strict;
my @args = ();
my @cflags = ();
+my @lflags = ();
my $is_linking = 0;
+my $is_debug = 0;
while (@ARGV) {
my $arg = shift @ARGV;
- if ("$arg" =~ /^-[DIMGO]/) {
+ if ("$arg" eq "-DDEBUG") {
+ # Some vcpkg-based libraries have different names for release
+ # and debug versions. This hack assumes that -DDEBUG comes
+ # before any "-l*" flags.
+ $is_debug = 1;
+ }
+ if ("$arg" =~ /^-[DIMGOZ]/) {
push(@cflags, $arg);
} elsif ("$arg" eq "-o") {
my $file_out = shift @ARGV;
if ("$file_out" =~ /exe$/) {
$is_linking = 1;
+ # Create foo.exe and foo.pdb
push(@args, "-OUT:$file_out");
} else {
+ # Create foo.o and foo.o.pdb
push(@args, "-Fo$file_out");
+ push(@args, "-Fd$file_out.pdb");
}
} elsif ("$arg" eq "-lz") {
+ if ($is_debug) {
+ push(@args, "zlibd.lib");
+ } else{
push(@args, "zlib.lib");
+ }
} elsif ("$arg" eq "-liconv") {
- push(@args, "iconv.lib");
+ push(@args, "libiconv.lib");
} elsif ("$arg" eq "-lcrypto") {
- push(@args, "libeay32.lib");
+ push(@args, "libcrypto.lib");
} elsif ("$arg" eq "-lssl") {
- push(@args, "ssleay32.lib");
+ push(@args, "libssl.lib");
} elsif ("$arg" eq "-lcurl") {
- push(@args, "libcurl.lib");
+ my $lib = "";
+ # Newer vcpkg definitions call this libcurl_imp.lib; Do we
+ # need to use that instead?
+ foreach my $flag (@lflags) {
+ if ($flag =~ /^-LIBPATH:(.*)/) {
+ foreach my $l ("libcurl_imp.lib", "libcurl.lib") {
+ if (-f "$1/$l") {
+ $lib = $l;
+ last;
+ }
+ }
+ }
+ }
+ push(@args, $lib);
+ } elsif ("$arg" eq "-lexpat") {
+ push(@args, "expat.lib");
} elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") {
$arg =~ s/^-L/-LIBPATH:/;
- push(@args, $arg);
- } elsif ("$arg" =~ /^-R/) {
+ push(@lflags, $arg);
+ } elsif ("$arg" =~ /^-[Rl]/) {
# eat
+ } elsif ("$arg" eq "-Werror") {
+ push(@cflags, "-WX");
+ } elsif ("$arg" eq "-Wall") {
+ # cl.exe understands -Wall, but it is really overzealous
+ push(@cflags, "-W4");
+ # disable the "signed/unsigned mismatch" warnings; our source code violates that
+ push(@cflags, "-wd4018");
+ push(@cflags, "-wd4245");
+ push(@cflags, "-wd4389");
+ # disable the "unreferenced formal parameter" warning; our source code violates that
+ push(@cflags, "-wd4100");
+ # disable the "conditional expression is constant" warning; our source code violates that
+ push(@cflags, "-wd4127");
+ # disable the "const object should be initialized" warning; these warnings affect only objects that are `static`
+ push(@cflags, "-wd4132");
+ # disable the "function/data pointer conversion in expression" warning; our source code violates that
+ push(@cflags, "-wd4152");
+ # disable the "non-constant aggregate initializer" warning; our source code violates that
+ push(@cflags, "-wd4204");
+ # disable the "cannot be initialized using address of automatic variable" warning; our source code violates that
+ push(@cflags, "-wd4221");
+ # disable the "possible loss of data" warnings; our source code violates that
+ push(@cflags, "-wd4244");
+ push(@cflags, "-wd4267");
+ # disable the "array is too small to include a terminating null character" warning; we ab-use strings to initialize OIDs
+ push(@cflags, "-wd4295");
+ # disable the "'<<': result of 32-bit shift implicitly converted to 64 bits" warning; our source code violates that
+ push(@cflags, "-wd4334");
+ # disable the "declaration hides previous local declaration" warning; our source code violates that
+ push(@cflags, "-wd4456");
+ # disable the "declaration hides function parameter" warning; our source code violates that
+ push(@cflags, "-wd4457");
+ # disable the "declaration hides global declaration" warning; our source code violates that
+ push(@cflags, "-wd4459");
+ # disable the "potentially uninitialized local variable '<name>' used" warning; our source code violates that
+ push(@cflags, "-wd4701");
+ # disable the "unreachable code" warning; our source code violates that
+ push(@cflags, "-wd4702");
+ # disable the "potentially uninitialized local pointer variable used" warning; our source code violates that
+ push(@cflags, "-wd4703");
+ # disable the "assignment within conditional expression" warning; our source code violates that
+ push(@cflags, "-wd4706");
+ # disable the "'inet_ntoa': Use inet_ntop() or InetNtop() instead" warning; our source code violates that
+ push(@cflags, "-wd4996");
+ } elsif ("$arg" =~ /^-W[a-z]/) {
+ # let's ignore those
} else {
push(@args, $arg);
}
}
if ($is_linking) {
+ push(@args, @lflags);
unshift(@args, "link.exe");
} else {
unshift(@args, "cl.exe");
push(@args, @cflags);
}
-#printf("**** @args\n");
+printf(STDERR "**** @args\n\n\n") if (!defined($ENV{'QUIET_GEN'}));
exit (system(@args) != 0);
diff --git a/compat/vcbuild/vcpkg_copy_dlls.bat b/compat/vcbuild/vcpkg_copy_dlls.bat
new file mode 100644
index 0000000000..13661c14f8
--- /dev/null
+++ b/compat/vcbuild/vcpkg_copy_dlls.bat
@@ -0,0 +1,39 @@
+@ECHO OFF
+REM ================================================================
+REM This script is an optional step. It copies the *.dll and *.pdb
+REM files (created by vcpkg_install.bat) into the top-level directory
+REM of the repo so that you can type "./git.exe" and find them without
+REM having to fixup your PATH.
+REM
+REM NOTE: Because the names of some DLL files change between DEBUG and
+REM NOTE: RELEASE builds when built using "vcpkg.exe", you will need
+REM NOTE: to copy up the corresponding version.
+REM ================================================================
+
+ SETLOCAL EnableDelayedExpansion
+
+ @FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
+ cd %cwd%
+
+ SET arch=x64-windows
+ SET inst=%cwd%vcpkg\installed\%arch%
+
+ IF [%1]==[release] (
+ echo Copying RELEASE mode DLLs to repo root...
+ ) ELSE IF [%1]==[debug] (
+ SET inst=%inst%\debug
+ echo Copying DEBUG mode DLLs to repo root...
+ ) ELSE (
+ echo ERROR: Invalid argument.
+ echo Usage: %~0 release
+ echo Usage: %~0 debug
+ EXIT /B 1
+ )
+
+ xcopy /e/s/v/y %inst%\bin\*.dll ..\..\
+ xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\
+
+ xcopy /e/s/v/y %inst%\bin\*.dll ..\..\t\helper\
+ xcopy /e/s/v/y %inst%\bin\*.pdb ..\..\t\helper\
+
+ EXIT /B 0
diff --git a/compat/vcbuild/vcpkg_install.bat b/compat/vcbuild/vcpkg_install.bat
new file mode 100644
index 0000000000..ebd0bad242
--- /dev/null
+++ b/compat/vcbuild/vcpkg_install.bat
@@ -0,0 +1,80 @@
+@ECHO OFF
+REM ================================================================
+REM This script installs the "vcpkg" source package manager and uses
+REM it to build the third-party libraries that git requires when it
+REM is built using MSVC.
+REM
+REM [1] Install VCPKG.
+REM [a] Create <root>/compat/vcbuild/vcpkg/
+REM [b] Download "vcpkg".
+REM [c] Compile using the currently installed version of VS.
+REM [d] Create <root>/compat/vcbuild/vcpkg/vcpkg.exe
+REM
+REM [2] Install third-party libraries.
+REM [a] Download each (which may also install CMAKE).
+REM [b] Compile in RELEASE mode and install in:
+REM vcpkg/installed/<arch>/{bin,lib}
+REM [c] Compile in DEBUG mode and install in:
+REM vcpkg/installed/<arch>/debug/{bin,lib}
+REM [d] Install headers in:
+REM vcpkg/installed/<arch>/include
+REM
+REM [3] Create a set of MAKE definitions for the top-level
+REM Makefile to allow "make MSVC=1" to find the above
+REM third-party libraries.
+REM [a] Write vcpkg/VCPGK-DEFS
+REM
+REM https://blogs.msdn.microsoft.com/vcblog/2016/09/19/vcpkg-a-tool-to-acquire-and-build-c-open-source-libraries-on-windows/
+REM https://github.com/Microsoft/vcpkg
+REM https://vcpkg.readthedocs.io/en/latest/
+REM ================================================================
+
+ SETLOCAL EnableDelayedExpansion
+
+ @FOR /F "delims=" %%D IN ("%~dp0") DO @SET cwd=%%~fD
+ cd %cwd%
+
+ dir vcpkg\vcpkg.exe >nul 2>nul && GOTO :install_libraries
+
+ echo Fetching vcpkg in %cwd%vcpkg
+ git.exe clone https://github.com/Microsoft/vcpkg vcpkg
+ IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+ cd vcpkg
+ echo Building vcpkg
+ powershell -exec bypass scripts\bootstrap.ps1
+ IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+ echo Successfully installed %cwd%vcpkg\vcpkg.exe
+
+:install_libraries
+ SET arch=x64-windows
+
+ echo Installing third-party libraries...
+ FOR %%i IN (zlib expat libiconv openssl libssh2 curl) DO (
+ cd %cwd%vcpkg
+ IF NOT EXIST "packages\%%i_%arch%" CALL :sub__install_one %%i
+ IF ERRORLEVEL 1 ( EXIT /B 1 )
+ )
+
+:install_defines
+ cd %cwd%
+ SET inst=%cwd%vcpkg\installed\%arch%
+
+ echo vcpkg_inc=-I"%inst%\include">VCPKG-DEFS
+ echo vcpkg_rel_lib=-L"%inst%\lib">>VCPKG-DEFS
+ echo vcpkg_rel_bin="%inst%\bin">>VCPKG-DEFS
+ echo vcpkg_dbg_lib=-L"%inst%\debug\lib">>VCPKG-DEFS
+ echo vcpkg_dbg_bin="%inst%\debug\bin">>VCPKG-DEFS
+
+ EXIT /B 0
+
+
+:sub__install_one
+ echo Installing package %1...
+
+ .\vcpkg.exe install %1:%arch%
+ IF ERRORLEVEL 1 ( EXIT /B 1 )
+
+ echo Finished %1
+ goto :EOF
diff --git a/compat/win32/git.manifest b/compat/win32/git.manifest
new file mode 100644
index 0000000000..771e3cce43
--- /dev/null
+++ b/compat/win32/git.manifest
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+ <assemblyIdentity type="win32" name="Git" version="0.0.0.1" />
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel level="asInvoker" uiAccess="false" />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows Vista -->
+ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
+ <!-- Windows 7 -->
+ <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows 8 -->
+ <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 8.1 -->
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 10 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ </application>
+ </compatibility>
+</assembly>
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 0f70d43920..f2e70872cd 100644
--- a/compat/win32/path-utils.h
+++ b/compat/win32/path-utils.h
@@ -1,5 +1,9 @@
-#define has_dos_drive_prefix(path) \
- (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
+#ifndef WIN32_PATH_UTILS_H
+#define WIN32_PATH_UTILS_H
+
+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)
@@ -18,3 +22,5 @@ static inline char *win32_find_last_dir_sep(const char *path)
#define find_last_dir_sep win32_find_last_dir_sep
int win32_offset_1st_component(const char *path);
#define offset_1st_component win32_offset_1st_component
+
+#endif
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
index c6cb8dd219..737983d00b 100644
--- a/compat/win32/pthread.h
+++ b/compat/win32/pthread.h
@@ -50,8 +50,8 @@ typedef struct {
DWORD tid;
} pthread_t;
-extern int pthread_create(pthread_t *thread, const void *unused,
- void *(*start_routine)(void*), void *arg);
+int pthread_create(pthread_t *thread, const void *unused,
+ void *(*start_routine)(void*), void *arg);
/*
* To avoid the need of copying a struct, we use small macro wrapper to pass
@@ -59,10 +59,10 @@ extern int pthread_create(pthread_t *thread, const void *unused,
*/
#define pthread_join(a, b) win32_pthread_join(&(a), (b))
-extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+int win32_pthread_join(pthread_t *thread, void **value_ptr);
#define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
-extern pthread_t pthread_self(void);
+pthread_t pthread_self(void);
static inline void NORETURN pthread_exit(void *ret)
{
diff --git a/compat/win32/trace2_win32_process_info.c b/compat/win32/trace2_win32_process_info.c
new file mode 100644
index 0000000000..8ccbd1c2c6
--- /dev/null
+++ b/compat/win32/trace2_win32_process_info.c
@@ -0,0 +1,191 @@
+#include "../../cache.h"
+#include "../../json-writer.h"
+#include "lazyload.h"
+#include <Psapi.h>
+#include <tlHelp32.h>
+
+/*
+ * An arbitrarily chosen value to limit the size of the ancestor
+ * array built in git_processes().
+ */
+#define NR_PIDS_LIMIT 10
+
+/*
+ * Find the process data for the given PID in the given snapshot
+ * and update the PROCESSENTRY32 data.
+ */
+static int find_pid(DWORD pid, HANDLE hSnapshot, PROCESSENTRY32 *pe32)
+{
+ pe32->dwSize = sizeof(PROCESSENTRY32);
+
+ if (Process32First(hSnapshot, pe32)) {
+ do {
+ if (pe32->th32ProcessID == pid)
+ return 1;
+ } while (Process32Next(hSnapshot, pe32));
+ }
+ return 0;
+}
+
+/*
+ * Accumulate JSON array of our parent processes:
+ * [
+ * exe-name-parent,
+ * exe-name-grand-parent,
+ * ...
+ * ]
+ *
+ * Note: we only report the filename of the process executable; the
+ * only way to get its full pathname is to use OpenProcess()
+ * and GetModuleFileNameEx() or QueryfullProcessImageName()
+ * and that seems rather expensive (on top of the cost of
+ * getting the snapshot).
+ *
+ * Note: we compute the set of parent processes by walking the PPID
+ * link in each visited PROCESSENTRY32 record. This search
+ * stops when an ancestor process is not found in the snapshot
+ * (because it exited before the current or intermediate parent
+ * process exited).
+ *
+ * This search may compute an incorrect result if the PPID link
+ * refers to the PID of an exited parent and that PID has been
+ * recycled and given to a new unrelated process.
+ *
+ * Worse, it is possible for a child or descendant of the
+ * current process to be given the recycled PID and cause a
+ * PPID-cycle. This would cause an infinite loop building our
+ * parent process array.
+ *
+ * Note: for completeness, the "System Idle" process has PID=0 and
+ * PPID=0 and could cause another PPID-cycle. We don't expect
+ * Git to be a descendant of the idle process, but because of
+ * PID recycling, it might be possible to get a PPID link value
+ * of 0. This too would cause an infinite loop.
+ *
+ * Therefore, we keep an array of the visited PPIDs to guard against
+ * cycles.
+ *
+ * We use a fixed-size array rather than ALLOC_GROW to keep things
+ * simple and avoid the alloc/realloc overhead. It is OK if we
+ * truncate the search and return a partial answer.
+ */
+static void get_processes(struct json_writer *jw, HANDLE hSnapshot)
+{
+ PROCESSENTRY32 pe32;
+ DWORD pid;
+ DWORD pid_list[NR_PIDS_LIMIT];
+ int k, nr_pids = 0;
+
+ pid = GetCurrentProcessId();
+ while (find_pid(pid, hSnapshot, &pe32)) {
+ /* Only report parents. Omit self from the JSON output. */
+ if (nr_pids)
+ jw_array_string(jw, pe32.szExeFile);
+
+ /* Check for cycle in snapshot. (Yes, it happened.) */
+ for (k = 0; k < nr_pids; k++)
+ if (pid == pid_list[k]) {
+ jw_array_string(jw, "(cycle)");
+ return;
+ }
+
+ if (nr_pids == NR_PIDS_LIMIT) {
+ jw_array_string(jw, "(truncated)");
+ return;
+ }
+
+ pid_list[nr_pids++] = pid;
+
+ pid = pe32.th32ParentProcessID;
+ }
+}
+
+/*
+ * Emit JSON data for the current and parent processes. Individual
+ * trace2 targets can decide how to actually print it.
+ */
+static void get_ancestry(void)
+{
+ HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+
+ if (hSnapshot != INVALID_HANDLE_VALUE) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_array_begin(&jw, 0);
+ get_processes(&jw, hSnapshot);
+ jw_end(&jw);
+
+ trace2_data_json("process", the_repository, "windows/ancestry",
+ &jw);
+
+ jw_release(&jw);
+ CloseHandle(hSnapshot);
+ }
+}
+
+/*
+ * Is a debugger attached to the current process?
+ *
+ * This will catch debug runs (where the debugger started the process).
+ * This is the normal case. Since this code is called during our startup,
+ * it will not report instances where a debugger is attached dynamically
+ * to a running git process, but that is relatively rare.
+ */
+static void get_is_being_debugged(void)
+{
+ if (IsDebuggerPresent())
+ trace2_data_intmax("process", the_repository,
+ "windows/debugger_present", 1);
+}
+
+/*
+ * Emit JSON data with the peak memory usage of the current process.
+ */
+static void get_peak_memory_info(void)
+{
+ DECLARE_PROC_ADDR(psapi.dll, BOOL, GetProcessMemoryInfo, HANDLE,
+ PPROCESS_MEMORY_COUNTERS, DWORD);
+
+ if (INIT_PROC_ADDR(GetProcessMemoryInfo)) {
+ PROCESS_MEMORY_COUNTERS pmc;
+
+ if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc,
+ sizeof(pmc))) {
+ struct json_writer jw = JSON_WRITER_INIT;
+
+ jw_object_begin(&jw, 0);
+
+#define KV(kv) #kv, (intmax_t)pmc.kv
+
+ jw_object_intmax(&jw, KV(PageFaultCount));
+ jw_object_intmax(&jw, KV(PeakWorkingSetSize));
+ jw_object_intmax(&jw, KV(PeakPagefileUsage));
+
+ jw_end(&jw);
+
+ trace2_data_json("process", the_repository,
+ "windows/memory", &jw);
+ jw_release(&jw);
+ }
+ }
+}
+
+void trace2_collect_process_info(enum trace2_process_info_reason reason)
+{
+ if (!trace2_is_enabled())
+ return;
+
+ switch (reason) {
+ case TRACE2_PROCESS_INFO_STARTUP:
+ get_is_being_debugged();
+ get_ancestry();
+ return;
+
+ case TRACE2_PROCESS_INFO_EXIT:
+ get_peak_memory_info();
+ return;
+
+ default:
+ BUG("trace2_collect_process_info: unknown reason '%d'", reason);
+ }
+}
diff --git a/compat/winansi.c b/compat/winansi.c
index f4f08237f9..c27b20a79d 100644
--- a/compat/winansi.c
+++ b/compat/winansi.c
@@ -7,6 +7,7 @@
#include <wingdi.h>
#include <winreg.h>
#include "win32.h"
+#include "win32/lazyload.h"
static int fd_is_interactive[3] = { 0, 0, 0 };
#define FD_CONSOLE 0x1
@@ -41,26 +42,21 @@ typedef struct _CONSOLE_FONT_INFOEX {
#endif
#endif
-typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL,
- PCONSOLE_FONT_INFOEX);
-
static void warn_if_raster_font(void)
{
DWORD fontFamily = 0;
- PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx;
+ DECLARE_PROC_ADDR(kernel32.dll, BOOL, GetCurrentConsoleFontEx,
+ HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
/* don't bother if output was ascii only */
if (!non_ascii_used)
return;
/* GetCurrentConsoleFontEx is available since Vista */
- pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress(
- GetModuleHandle("kernel32.dll"),
- "GetCurrentConsoleFontEx");
- if (pGetCurrentConsoleFontEx) {
+ if (INIT_PROC_ADDR(GetCurrentConsoleFontEx)) {
CONSOLE_FONT_INFOEX cfi;
cfi.cbSize = sizeof(cfi);
- if (pGetCurrentConsoleFontEx(console, 0, &cfi))
+ if (GetCurrentConsoleFontEx(console, 0, &cfi))
fontFamily = cfi.FontFamily;
} else {
/* pre-Vista: check default console font in registry */
@@ -544,7 +540,20 @@ static HANDLE swap_osfhnd(int fd, HANDLE new_handle)
#ifdef DETECT_MSYS_TTY
#include <winternl.h>
+
+#if defined(_MSC_VER)
+
+typedef struct _OBJECT_NAME_INFORMATION
+{
+ UNICODE_STRING Name;
+ WCHAR NameBuffer[FLEX_ARRAY];
+} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
+
+#define ObjectNameInformation 1
+
+#else
#include <ntstatus.h>
+#endif
static void detect_msys_tty(int fd)
{
@@ -599,7 +608,7 @@ int winansi_isatty(int fd)
void winansi_init(void)
{
int con1, con2;
- char name[32];
+ wchar_t name[32];
/* check if either stdout or stderr is a console output screen buffer */
con1 = is_console(1);
@@ -619,13 +628,15 @@ void winansi_init(void)
}
/* create a named pipe to communicate with the console thread */
- xsnprintf(name, sizeof(name), "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId());
- hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND,
+ if (swprintf(name, ARRAY_SIZE(name) - 1, L"\\\\.\\pipe\\winansi%lu",
+ GetCurrentProcessId()) < 0)
+ die("Could not initialize winansi pipe name");
+ hwrite = CreateNamedPipeW(name, PIPE_ACCESS_OUTBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL);
if (hwrite == INVALID_HANDLE_VALUE)
die_lasterr("CreateNamedPipe failed");
- hread = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+ hread = CreateFileW(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hread == INVALID_HANDLE_VALUE)
die_lasterr("CreateFile for named pipe failed");
@@ -651,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;
}