compat / mingw.con commit sequencer: refactor check_todo_list() to work on a todo_list (6ca89c6)
   1#include "../git-compat-util.h"
   2#include "win32.h"
   3#include <conio.h>
   4#include <wchar.h>
   5#include "../strbuf.h"
   6#include "../run-command.h"
   7#include "../cache.h"
   8#include "win32/lazyload.h"
   9#include "../config.h"
  10
  11#define HCAST(type, handle) ((type)(intptr_t)handle)
  12
  13static const int delay[] = { 0, 1, 10, 20, 40 };
  14
  15int err_win_to_posix(DWORD winerr)
  16{
  17        int error = ENOSYS;
  18        switch(winerr) {
  19        case ERROR_ACCESS_DENIED: error = EACCES; break;
  20        case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
  21        case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
  22        case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
  23        case ERROR_ALREADY_EXISTS: error = EEXIST; break;
  24        case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
  25        case ERROR_BAD_COMMAND: error = EIO; break;
  26        case ERROR_BAD_DEVICE: error = ENODEV; break;
  27        case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
  28        case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
  29        case ERROR_BAD_FORMAT: error = ENOEXEC; break;
  30        case ERROR_BAD_LENGTH: error = EINVAL; break;
  31        case ERROR_BAD_PATHNAME: error = ENOENT; break;
  32        case ERROR_BAD_PIPE: error = EPIPE; break;
  33        case ERROR_BAD_UNIT: error = ENODEV; break;
  34        case ERROR_BAD_USERNAME: error = EINVAL; break;
  35        case ERROR_BROKEN_PIPE: error = EPIPE; break;
  36        case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
  37        case ERROR_BUSY: error = EBUSY; break;
  38        case ERROR_BUSY_DRIVE: error = EBUSY; break;
  39        case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
  40        case ERROR_CANNOT_MAKE: error = EACCES; break;
  41        case ERROR_CANTOPEN: error = EIO; break;
  42        case ERROR_CANTREAD: error = EIO; break;
  43        case ERROR_CANTWRITE: error = EIO; break;
  44        case ERROR_CRC: error = EIO; break;
  45        case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
  46        case ERROR_DEVICE_IN_USE: error = EBUSY; break;
  47        case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
  48        case ERROR_DIRECTORY: error = EINVAL; break;
  49        case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
  50        case ERROR_DISK_CHANGE: error = EIO; break;
  51        case ERROR_DISK_FULL: error = ENOSPC; break;
  52        case ERROR_DRIVE_LOCKED: error = EBUSY; break;
  53        case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
  54        case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
  55        case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
  56        case ERROR_FILE_EXISTS: error = EEXIST; break;
  57        case ERROR_FILE_INVALID: error = ENODEV; break;
  58        case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
  59        case ERROR_GEN_FAILURE: error = EIO; break;
  60        case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
  61        case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
  62        case ERROR_INVALID_ACCESS: error = EACCES; break;
  63        case ERROR_INVALID_ADDRESS: error = EFAULT; break;
  64        case ERROR_INVALID_BLOCK: error = EFAULT; break;
  65        case ERROR_INVALID_DATA: error = EINVAL; break;
  66        case ERROR_INVALID_DRIVE: error = ENODEV; break;
  67        case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
  68        case ERROR_INVALID_FLAGS: error = EINVAL; break;
  69        case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
  70        case ERROR_INVALID_HANDLE: error = EBADF; break;
  71        case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
  72        case ERROR_INVALID_NAME: error = EINVAL; break;
  73        case ERROR_INVALID_OWNER: error = EINVAL; break;
  74        case ERROR_INVALID_PARAMETER: error = EINVAL; break;
  75        case ERROR_INVALID_PASSWORD: error = EPERM; break;
  76        case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
  77        case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
  78        case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
  79        case ERROR_INVALID_WORKSTATION: error = EACCES; break;
  80        case ERROR_IO_DEVICE: error = EIO; break;
  81        case ERROR_IO_INCOMPLETE: error = EINTR; break;
  82        case ERROR_LOCKED: error = EBUSY; break;
  83        case ERROR_LOCK_VIOLATION: error = EACCES; break;
  84        case ERROR_LOGON_FAILURE: error = EACCES; break;
  85        case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
  86        case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
  87        case ERROR_MORE_DATA: error = EPIPE; break;
  88        case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
  89        case ERROR_NOACCESS: error = EFAULT; break;
  90        case ERROR_NONE_MAPPED: error = EINVAL; break;
  91        case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
  92        case ERROR_NOT_READY: error = EAGAIN; break;
  93        case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
  94        case ERROR_NO_DATA: error = EPIPE; break;
  95        case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
  96        case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
  97        case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
  98        case ERROR_OPEN_FAILED: error = EIO; break;
  99        case ERROR_OPEN_FILES: error = EBUSY; break;
 100        case ERROR_OPERATION_ABORTED: error = EINTR; break;
 101        case ERROR_OUTOFMEMORY: error = ENOMEM; break;
 102        case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
 103        case ERROR_PATH_BUSY: error = EBUSY; break;
 104        case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
 105        case ERROR_PIPE_BUSY: error = EBUSY; break;
 106        case ERROR_PIPE_CONNECTED: error = EPIPE; break;
 107        case ERROR_PIPE_LISTENING: error = EPIPE; break;
 108        case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
 109        case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
 110        case ERROR_READ_FAULT: error = EIO; break;
 111        case ERROR_SEEK: error = EIO; break;
 112        case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
 113        case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
 114        case ERROR_SHARING_VIOLATION: error = EACCES; break;
 115        case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
 116        case ERROR_SWAPERROR: error = ENOENT; break;
 117        case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
 118        case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
 119        case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
 120        case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
 121        case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
 122        case ERROR_WRITE_FAULT: error = EIO; break;
 123        case ERROR_WRITE_PROTECT: error = EROFS; break;
 124        }
 125        return error;
 126}
 127
 128static inline int is_file_in_use_error(DWORD errcode)
 129{
 130        switch (errcode) {
 131        case ERROR_SHARING_VIOLATION:
 132        case ERROR_ACCESS_DENIED:
 133                return 1;
 134        }
 135
 136        return 0;
 137}
 138
 139static int read_yes_no_answer(void)
 140{
 141        char answer[1024];
 142
 143        if (fgets(answer, sizeof(answer), stdin)) {
 144                size_t answer_len = strlen(answer);
 145                int got_full_line = 0, c;
 146
 147                /* remove the newline */
 148                if (answer_len >= 2 && answer[answer_len-2] == '\r') {
 149                        answer[answer_len-2] = '\0';
 150                        got_full_line = 1;
 151                } else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
 152                        answer[answer_len-1] = '\0';
 153                        got_full_line = 1;
 154                }
 155                /* flush the buffer in case we did not get the full line */
 156                if (!got_full_line)
 157                        while ((c = getchar()) != EOF && c != '\n')
 158                                ;
 159        } else
 160                /* we could not read, return the
 161                 * default answer which is no */
 162                return 0;
 163
 164        if (tolower(answer[0]) == 'y' && !answer[1])
 165                return 1;
 166        if (!strncasecmp(answer, "yes", sizeof(answer)))
 167                return 1;
 168        if (tolower(answer[0]) == 'n' && !answer[1])
 169                return 0;
 170        if (!strncasecmp(answer, "no", sizeof(answer)))
 171                return 0;
 172
 173        /* did not find an answer we understand */
 174        return -1;
 175}
 176
 177static int ask_yes_no_if_possible(const char *format, ...)
 178{
 179        char question[4096];
 180        const char *retry_hook[] = { NULL, NULL, NULL };
 181        va_list args;
 182
 183        va_start(args, format);
 184        vsnprintf(question, sizeof(question), format, args);
 185        va_end(args);
 186
 187        if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
 188                retry_hook[1] = question;
 189                return !run_command_v_opt(retry_hook, 0);
 190        }
 191
 192        if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
 193                return 0;
 194
 195        while (1) {
 196                int answer;
 197                fprintf(stderr, "%s (y/n) ", question);
 198
 199                if ((answer = read_yes_no_answer()) >= 0)
 200                        return answer;
 201
 202                fprintf(stderr, "Sorry, I did not understand your answer. "
 203                                "Please type 'y' or 'n'\n");
 204        }
 205}
 206
 207/* Windows only */
 208enum hide_dotfiles_type {
 209        HIDE_DOTFILES_FALSE = 0,
 210        HIDE_DOTFILES_TRUE,
 211        HIDE_DOTFILES_DOTGITONLY
 212};
 213
 214static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 215static char *unset_environment_variables;
 216
 217int mingw_core_config(const char *var, const char *value, void *cb)
 218{
 219        if (!strcmp(var, "core.hidedotfiles")) {
 220                if (value && !strcasecmp(value, "dotgitonly"))
 221                        hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 222                else
 223                        hide_dotfiles = git_config_bool(var, value);
 224                return 0;
 225        }
 226
 227        if (!strcmp(var, "core.unsetenvvars")) {
 228                free(unset_environment_variables);
 229                unset_environment_variables = xstrdup(value);
 230                return 0;
 231        }
 232
 233        return 0;
 234}
 235
 236/* Normalizes NT paths as returned by some low-level APIs. */
 237static wchar_t *normalize_ntpath(wchar_t *wbuf)
 238{
 239        int i;
 240        /* fix absolute path prefixes */
 241        if (wbuf[0] == '\\') {
 242                /* strip NT namespace prefixes */
 243                if (!wcsncmp(wbuf, L"\\??\\", 4) ||
 244                    !wcsncmp(wbuf, L"\\\\?\\", 4))
 245                        wbuf += 4;
 246                else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
 247                        wbuf += 12;
 248                /* replace remaining '...UNC\' with '\\' */
 249                if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
 250                        wbuf += 2;
 251                        *wbuf = '\\';
 252                }
 253        }
 254        /* convert backslashes to slashes */
 255        for (i = 0; wbuf[i]; i++)
 256                if (wbuf[i] == '\\')
 257                        wbuf[i] = '/';
 258        return wbuf;
 259}
 260
 261int mingw_unlink(const char *pathname)
 262{
 263        int ret, tries = 0;
 264        wchar_t wpathname[MAX_PATH];
 265        if (xutftowcs_path(wpathname, pathname) < 0)
 266                return -1;
 267
 268        /* read-only files cannot be removed */
 269        _wchmod(wpathname, 0666);
 270        while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 271                if (!is_file_in_use_error(GetLastError()))
 272                        break;
 273                /*
 274                 * We assume that some other process had the source or
 275                 * destination file open at the wrong moment and retry.
 276                 * In order to give the other process a higher chance to
 277                 * complete its operation, we give up our time slice now.
 278                 * If we have to retry again, we do sleep a bit.
 279                 */
 280                Sleep(delay[tries]);
 281                tries++;
 282        }
 283        while (ret == -1 && is_file_in_use_error(GetLastError()) &&
 284               ask_yes_no_if_possible("Unlink of file '%s' failed. "
 285                        "Should I try again?", pathname))
 286               ret = _wunlink(wpathname);
 287        return ret;
 288}
 289
 290static int is_dir_empty(const wchar_t *wpath)
 291{
 292        WIN32_FIND_DATAW findbuf;
 293        HANDLE handle;
 294        wchar_t wbuf[MAX_PATH + 2];
 295        wcscpy(wbuf, wpath);
 296        wcscat(wbuf, L"\\*");
 297        handle = FindFirstFileW(wbuf, &findbuf);
 298        if (handle == INVALID_HANDLE_VALUE)
 299                return GetLastError() == ERROR_NO_MORE_FILES;
 300
 301        while (!wcscmp(findbuf.cFileName, L".") ||
 302                        !wcscmp(findbuf.cFileName, L".."))
 303                if (!FindNextFileW(handle, &findbuf)) {
 304                        DWORD err = GetLastError();
 305                        FindClose(handle);
 306                        return err == ERROR_NO_MORE_FILES;
 307                }
 308        FindClose(handle);
 309        return 0;
 310}
 311
 312int mingw_rmdir(const char *pathname)
 313{
 314        int ret, tries = 0;
 315        wchar_t wpathname[MAX_PATH];
 316        if (xutftowcs_path(wpathname, pathname) < 0)
 317                return -1;
 318
 319        while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
 320                if (!is_file_in_use_error(GetLastError()))
 321                        errno = err_win_to_posix(GetLastError());
 322                if (errno != EACCES)
 323                        break;
 324                if (!is_dir_empty(wpathname)) {
 325                        errno = ENOTEMPTY;
 326                        break;
 327                }
 328                /*
 329                 * We assume that some other process had the source or
 330                 * destination file open at the wrong moment and retry.
 331                 * In order to give the other process a higher chance to
 332                 * complete its operation, we give up our time slice now.
 333                 * If we have to retry again, we do sleep a bit.
 334                 */
 335                Sleep(delay[tries]);
 336                tries++;
 337        }
 338        while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
 339               ask_yes_no_if_possible("Deletion of directory '%s' failed. "
 340                        "Should I try again?", pathname))
 341               ret = _wrmdir(wpathname);
 342        return ret;
 343}
 344
 345static inline int needs_hiding(const char *path)
 346{
 347        const char *basename;
 348
 349        if (hide_dotfiles == HIDE_DOTFILES_FALSE)
 350                return 0;
 351
 352        /* We cannot use basename(), as it would remove trailing slashes */
 353        mingw_skip_dos_drive_prefix((char **)&path);
 354        if (!*path)
 355                return 0;
 356
 357        for (basename = path; *path; path++)
 358                if (is_dir_sep(*path)) {
 359                        do {
 360                                path++;
 361                        } while (is_dir_sep(*path));
 362                        /* ignore trailing slashes */
 363                        if (*path)
 364                                basename = path;
 365                }
 366
 367        if (hide_dotfiles == HIDE_DOTFILES_TRUE)
 368                return *basename == '.';
 369
 370        assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
 371        return !strncasecmp(".git", basename, 4) &&
 372                (!basename[4] || is_dir_sep(basename[4]));
 373}
 374
 375static int set_hidden_flag(const wchar_t *path, int set)
 376{
 377        DWORD original = GetFileAttributesW(path), modified;
 378        if (set)
 379                modified = original | FILE_ATTRIBUTE_HIDDEN;
 380        else
 381                modified = original & ~FILE_ATTRIBUTE_HIDDEN;
 382        if (original == modified || SetFileAttributesW(path, modified))
 383                return 0;
 384        errno = err_win_to_posix(GetLastError());
 385        return -1;
 386}
 387
 388int mingw_mkdir(const char *path, int mode)
 389{
 390        int ret;
 391        wchar_t wpath[MAX_PATH];
 392        if (xutftowcs_path(wpath, path) < 0)
 393                return -1;
 394        ret = _wmkdir(wpath);
 395        if (!ret && needs_hiding(path))
 396                return set_hidden_flag(wpath, 1);
 397        return ret;
 398}
 399
 400/*
 401 * Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA
 402 * is documented in [1] as opening a writable file handle in append mode.
 403 * (It is believed that) this is atomic since it is maintained by the
 404 * kernel unlike the O_APPEND flag which is racily maintained by the CRT.
 405 *
 406 * [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants
 407 *
 408 * This trick does not appear to work for named pipes.  Instead it creates
 409 * a named pipe client handle that cannot be written to.  Callers should
 410 * just use the regular _wopen() for them.  (And since client handle gets
 411 * bound to a unique server handle, it isn't really an issue.)
 412 */
 413static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
 414{
 415        HANDLE handle;
 416        int fd;
 417        DWORD create = (oflags & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING;
 418
 419        /* only these flags are supported */
 420        if ((oflags & ~O_CREAT) != (O_WRONLY | O_APPEND))
 421                return errno = ENOSYS, -1;
 422
 423        /*
 424         * FILE_SHARE_WRITE is required to permit child processes
 425         * to append to the file.
 426         */
 427        handle = CreateFileW(wfilename, FILE_APPEND_DATA,
 428                        FILE_SHARE_WRITE | FILE_SHARE_READ,
 429                        NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
 430        if (handle == INVALID_HANDLE_VALUE)
 431                return errno = err_win_to_posix(GetLastError()), -1;
 432
 433        /*
 434         * No O_APPEND here, because the CRT uses it only to reset the
 435         * file pointer to EOF before each write(); but that is not
 436         * necessary (and may lead to races) for a file created with
 437         * FILE_APPEND_DATA.
 438         */
 439        fd = _open_osfhandle((intptr_t)handle, O_BINARY);
 440        if (fd < 0)
 441                CloseHandle(handle);
 442        return fd;
 443}
 444
 445/*
 446 * Does the pathname map to the local named pipe filesystem?
 447 * That is, does it have a "//./pipe/" prefix?
 448 */
 449static int is_local_named_pipe_path(const char *filename)
 450{
 451        return (is_dir_sep(filename[0]) &&
 452                is_dir_sep(filename[1]) &&
 453                filename[2] == '.'  &&
 454                is_dir_sep(filename[3]) &&
 455                !strncasecmp(filename+4, "pipe", 4) &&
 456                is_dir_sep(filename[8]) &&
 457                filename[9]);
 458}
 459
 460int mingw_open (const char *filename, int oflags, ...)
 461{
 462        typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
 463        va_list args;
 464        unsigned mode;
 465        int fd;
 466        wchar_t wfilename[MAX_PATH];
 467        open_fn_t open_fn;
 468
 469        va_start(args, oflags);
 470        mode = va_arg(args, int);
 471        va_end(args);
 472
 473        if (filename && !strcmp(filename, "/dev/null"))
 474                filename = "nul";
 475
 476        if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
 477                open_fn = mingw_open_append;
 478        else
 479                open_fn = _wopen;
 480
 481        if (xutftowcs_path(wfilename, filename) < 0)
 482                return -1;
 483        fd = open_fn(wfilename, oflags, mode);
 484
 485        if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
 486                DWORD attrs = GetFileAttributesW(wfilename);
 487                if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
 488                        errno = EISDIR;
 489        }
 490        if ((oflags & O_CREAT) && needs_hiding(filename)) {
 491                /*
 492                 * Internally, _wopen() uses the CreateFile() API which errors
 493                 * out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
 494                 * specified and an already existing file's attributes do not
 495                 * match *exactly*. As there is no mode or flag we can set that
 496                 * would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
 497                 * again *without* the O_CREAT flag (that corresponds to the
 498                 * CREATE_ALWAYS flag of CreateFile()).
 499                 */
 500                if (fd < 0 && errno == EACCES)
 501                        fd = open_fn(wfilename, oflags & ~O_CREAT, mode);
 502                if (fd >= 0 && set_hidden_flag(wfilename, 1))
 503                        warning("could not mark '%s' as hidden.", filename);
 504        }
 505        return fd;
 506}
 507
 508static BOOL WINAPI ctrl_ignore(DWORD type)
 509{
 510        return TRUE;
 511}
 512
 513#undef fgetc
 514int mingw_fgetc(FILE *stream)
 515{
 516        int ch;
 517        if (!isatty(_fileno(stream)))
 518                return fgetc(stream);
 519
 520        SetConsoleCtrlHandler(ctrl_ignore, TRUE);
 521        while (1) {
 522                ch = fgetc(stream);
 523                if (ch != EOF || GetLastError() != ERROR_OPERATION_ABORTED)
 524                        break;
 525
 526                /* Ctrl+C was pressed, simulate SIGINT and retry */
 527                mingw_raise(SIGINT);
 528        }
 529        SetConsoleCtrlHandler(ctrl_ignore, FALSE);
 530        return ch;
 531}
 532
 533#undef fopen
 534FILE *mingw_fopen (const char *filename, const char *otype)
 535{
 536        int hide = needs_hiding(filename);
 537        FILE *file;
 538        wchar_t wfilename[MAX_PATH], wotype[4];
 539        if (filename && !strcmp(filename, "/dev/null"))
 540                filename = "nul";
 541        if (xutftowcs_path(wfilename, filename) < 0 ||
 542                xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
 543                return NULL;
 544        if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
 545                error("could not unhide %s", filename);
 546                return NULL;
 547        }
 548        file = _wfopen(wfilename, wotype);
 549        if (!file && GetLastError() == ERROR_INVALID_NAME)
 550                errno = ENOENT;
 551        if (file && hide && set_hidden_flag(wfilename, 1))
 552                warning("could not mark '%s' as hidden.", filename);
 553        return file;
 554}
 555
 556FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
 557{
 558        int hide = needs_hiding(filename);
 559        FILE *file;
 560        wchar_t wfilename[MAX_PATH], wotype[4];
 561        if (filename && !strcmp(filename, "/dev/null"))
 562                filename = "nul";
 563        if (xutftowcs_path(wfilename, filename) < 0 ||
 564                xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
 565                return NULL;
 566        if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
 567                error("could not unhide %s", filename);
 568                return NULL;
 569        }
 570        file = _wfreopen(wfilename, wotype, stream);
 571        if (file && hide && set_hidden_flag(wfilename, 1))
 572                warning("could not mark '%s' as hidden.", filename);
 573        return file;
 574}
 575
 576#undef fflush
 577int mingw_fflush(FILE *stream)
 578{
 579        int ret = fflush(stream);
 580
 581        /*
 582         * write() is used behind the scenes of stdio output functions.
 583         * Since git code does not check for errors after each stdio write
 584         * operation, it can happen that write() is called by a later
 585         * stdio function even if an earlier write() call failed. In the
 586         * case of a pipe whose readable end was closed, only the first
 587         * call to write() reports EPIPE on Windows. Subsequent write()
 588         * calls report EINVAL. It is impossible to notice whether this
 589         * fflush invocation triggered such a case, therefore, we have to
 590         * catch all EINVAL errors whole-sale.
 591         */
 592        if (ret && errno == EINVAL)
 593                errno = EPIPE;
 594
 595        return ret;
 596}
 597
 598#undef write
 599ssize_t mingw_write(int fd, const void *buf, size_t len)
 600{
 601        ssize_t result = write(fd, buf, len);
 602
 603        if (result < 0 && errno == EINVAL && buf) {
 604                /* check if fd is a pipe */
 605                HANDLE h = (HANDLE) _get_osfhandle(fd);
 606                if (GetFileType(h) == FILE_TYPE_PIPE)
 607                        errno = EPIPE;
 608                else
 609                        errno = EINVAL;
 610        }
 611
 612        return result;
 613}
 614
 615int mingw_access(const char *filename, int mode)
 616{
 617        wchar_t wfilename[MAX_PATH];
 618        if (xutftowcs_path(wfilename, filename) < 0)
 619                return -1;
 620        /* X_OK is not supported by the MSVCRT version */
 621        return _waccess(wfilename, mode & ~X_OK);
 622}
 623
 624int mingw_chdir(const char *dirname)
 625{
 626        wchar_t wdirname[MAX_PATH];
 627        if (xutftowcs_path(wdirname, dirname) < 0)
 628                return -1;
 629        return _wchdir(wdirname);
 630}
 631
 632int mingw_chmod(const char *filename, int mode)
 633{
 634        wchar_t wfilename[MAX_PATH];
 635        if (xutftowcs_path(wfilename, filename) < 0)
 636                return -1;
 637        return _wchmod(wfilename, mode);
 638}
 639
 640/*
 641 * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
 642 * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
 643 */
 644static inline long long filetime_to_hnsec(const FILETIME *ft)
 645{
 646        long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
 647        /* Windows to Unix Epoch conversion */
 648        return winTime - 116444736000000000LL;
 649}
 650
 651static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
 652{
 653        long long hnsec = filetime_to_hnsec(ft);
 654        ts->tv_sec = (time_t)(hnsec / 10000000);
 655        ts->tv_nsec = (hnsec % 10000000) * 100;
 656}
 657
 658/**
 659 * Verifies that safe_create_leading_directories() would succeed.
 660 */
 661static int has_valid_directory_prefix(wchar_t *wfilename)
 662{
 663        int n = wcslen(wfilename);
 664
 665        while (n > 0) {
 666                wchar_t c = wfilename[--n];
 667                DWORD attributes;
 668
 669                if (!is_dir_sep(c))
 670                        continue;
 671
 672                wfilename[n] = L'\0';
 673                attributes = GetFileAttributesW(wfilename);
 674                wfilename[n] = c;
 675                if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
 676                                attributes == FILE_ATTRIBUTE_DEVICE)
 677                        return 1;
 678                if (attributes == INVALID_FILE_ATTRIBUTES)
 679                        switch (GetLastError()) {
 680                        case ERROR_PATH_NOT_FOUND:
 681                                continue;
 682                        case ERROR_FILE_NOT_FOUND:
 683                                /* This implies parent directory exists. */
 684                                return 1;
 685                        }
 686                return 0;
 687        }
 688        return 1;
 689}
 690
 691/* We keep the do_lstat code in a separate function to avoid recursion.
 692 * When a path ends with a slash, the stat will fail with ENOENT. In
 693 * this case, we strip the trailing slashes and stat again.
 694 *
 695 * If follow is true then act like stat() and report on the link
 696 * target. Otherwise report on the link itself.
 697 */
 698static int do_lstat(int follow, const char *file_name, struct stat *buf)
 699{
 700        WIN32_FILE_ATTRIBUTE_DATA fdata;
 701        wchar_t wfilename[MAX_PATH];
 702        if (xutftowcs_path(wfilename, file_name) < 0)
 703                return -1;
 704
 705        if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
 706                buf->st_ino = 0;
 707                buf->st_gid = 0;
 708                buf->st_uid = 0;
 709                buf->st_nlink = 1;
 710                buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 711                buf->st_size = fdata.nFileSizeLow |
 712                        (((off_t)fdata.nFileSizeHigh)<<32);
 713                buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 714                filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 715                filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 716                filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
 717                if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
 718                        WIN32_FIND_DATAW findbuf;
 719                        HANDLE handle = FindFirstFileW(wfilename, &findbuf);
 720                        if (handle != INVALID_HANDLE_VALUE) {
 721                                if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
 722                                                (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
 723                                        if (follow) {
 724                                                char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
 725                                                buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
 726                                        } else {
 727                                                buf->st_mode = S_IFLNK;
 728                                        }
 729                                        buf->st_mode |= S_IREAD;
 730                                        if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
 731                                                buf->st_mode |= S_IWRITE;
 732                                }
 733                                FindClose(handle);
 734                        }
 735                }
 736                return 0;
 737        }
 738        switch (GetLastError()) {
 739        case ERROR_ACCESS_DENIED:
 740        case ERROR_SHARING_VIOLATION:
 741        case ERROR_LOCK_VIOLATION:
 742        case ERROR_SHARING_BUFFER_EXCEEDED:
 743                errno = EACCES;
 744                break;
 745        case ERROR_BUFFER_OVERFLOW:
 746                errno = ENAMETOOLONG;
 747                break;
 748        case ERROR_NOT_ENOUGH_MEMORY:
 749                errno = ENOMEM;
 750                break;
 751        case ERROR_PATH_NOT_FOUND:
 752                if (!has_valid_directory_prefix(wfilename)) {
 753                        errno = ENOTDIR;
 754                        break;
 755                }
 756                /* fallthru */
 757        default:
 758                errno = ENOENT;
 759                break;
 760        }
 761        return -1;
 762}
 763
 764/* We provide our own lstat/fstat functions, since the provided
 765 * lstat/fstat functions are so slow. These stat functions are
 766 * tailored for Git's usage (read: fast), and are not meant to be
 767 * complete. Note that Git stat()s are redirected to mingw_lstat()
 768 * too, since Windows doesn't really handle symlinks that well.
 769 */
 770static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
 771{
 772        int namelen;
 773        char alt_name[PATH_MAX];
 774
 775        if (!do_lstat(follow, file_name, buf))
 776                return 0;
 777
 778        /* if file_name ended in a '/', Windows returned ENOENT;
 779         * try again without trailing slashes
 780         */
 781        if (errno != ENOENT)
 782                return -1;
 783
 784        namelen = strlen(file_name);
 785        if (namelen && file_name[namelen-1] != '/')
 786                return -1;
 787        while (namelen && file_name[namelen-1] == '/')
 788                --namelen;
 789        if (!namelen || namelen >= PATH_MAX)
 790                return -1;
 791
 792        memcpy(alt_name, file_name, namelen);
 793        alt_name[namelen] = 0;
 794        return do_lstat(follow, alt_name, buf);
 795}
 796
 797static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
 798{
 799        BY_HANDLE_FILE_INFORMATION fdata;
 800
 801        if (!GetFileInformationByHandle(hnd, &fdata)) {
 802                errno = err_win_to_posix(GetLastError());
 803                return -1;
 804        }
 805
 806        buf->st_ino = 0;
 807        buf->st_gid = 0;
 808        buf->st_uid = 0;
 809        buf->st_nlink = 1;
 810        buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
 811        buf->st_size = fdata.nFileSizeLow |
 812                (((off_t)fdata.nFileSizeHigh)<<32);
 813        buf->st_dev = buf->st_rdev = 0; /* not used by Git */
 814        filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
 815        filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
 816        filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
 817        return 0;
 818}
 819
 820int mingw_lstat(const char *file_name, struct stat *buf)
 821{
 822        return do_stat_internal(0, file_name, buf);
 823}
 824int mingw_stat(const char *file_name, struct stat *buf)
 825{
 826        return do_stat_internal(1, file_name, buf);
 827}
 828
 829int mingw_fstat(int fd, struct stat *buf)
 830{
 831        HANDLE fh = (HANDLE)_get_osfhandle(fd);
 832        DWORD avail, type = GetFileType(fh) & ~FILE_TYPE_REMOTE;
 833
 834        switch (type) {
 835        case FILE_TYPE_DISK:
 836                return get_file_info_by_handle(fh, buf);
 837
 838        case FILE_TYPE_CHAR:
 839        case FILE_TYPE_PIPE:
 840                /* initialize stat fields */
 841                memset(buf, 0, sizeof(*buf));
 842                buf->st_nlink = 1;
 843
 844                if (type == FILE_TYPE_CHAR) {
 845                        buf->st_mode = _S_IFCHR;
 846                } else {
 847                        buf->st_mode = _S_IFIFO;
 848                        if (PeekNamedPipe(fh, NULL, 0, NULL, &avail, NULL))
 849                                buf->st_size = avail;
 850                }
 851                return 0;
 852
 853        default:
 854                errno = EBADF;
 855                return -1;
 856        }
 857}
 858
 859static inline void time_t_to_filetime(time_t t, FILETIME *ft)
 860{
 861        long long winTime = t * 10000000LL + 116444736000000000LL;
 862        ft->dwLowDateTime = winTime;
 863        ft->dwHighDateTime = winTime >> 32;
 864}
 865
 866int mingw_utime (const char *file_name, const struct utimbuf *times)
 867{
 868        FILETIME mft, aft;
 869        int fh, rc;
 870        DWORD attrs;
 871        wchar_t wfilename[MAX_PATH];
 872        if (xutftowcs_path(wfilename, file_name) < 0)
 873                return -1;
 874
 875        /* must have write permission */
 876        attrs = GetFileAttributesW(wfilename);
 877        if (attrs != INVALID_FILE_ATTRIBUTES &&
 878            (attrs & FILE_ATTRIBUTE_READONLY)) {
 879                /* ignore errors here; open() will report them */
 880                SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
 881        }
 882
 883        if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) {
 884                rc = -1;
 885                goto revert_attrs;
 886        }
 887
 888        if (times) {
 889                time_t_to_filetime(times->modtime, &mft);
 890                time_t_to_filetime(times->actime, &aft);
 891        } else {
 892                GetSystemTimeAsFileTime(&mft);
 893                aft = mft;
 894        }
 895        if (!SetFileTime((HANDLE)_get_osfhandle(fh), NULL, &aft, &mft)) {
 896                errno = EINVAL;
 897                rc = -1;
 898        } else
 899                rc = 0;
 900        close(fh);
 901
 902revert_attrs:
 903        if (attrs != INVALID_FILE_ATTRIBUTES &&
 904            (attrs & FILE_ATTRIBUTE_READONLY)) {
 905                /* ignore errors again */
 906                SetFileAttributesW(wfilename, attrs);
 907        }
 908        return rc;
 909}
 910
 911#undef strftime
 912size_t mingw_strftime(char *s, size_t max,
 913                      const char *format, const struct tm *tm)
 914{
 915        size_t ret = strftime(s, max, format, tm);
 916
 917        if (!ret && errno == EINVAL)
 918                die("invalid strftime format: '%s'", format);
 919        return ret;
 920}
 921
 922unsigned int sleep (unsigned int seconds)
 923{
 924        Sleep(seconds*1000);
 925        return 0;
 926}
 927
 928char *mingw_mktemp(char *template)
 929{
 930        wchar_t wtemplate[MAX_PATH];
 931        if (xutftowcs_path(wtemplate, template) < 0)
 932                return NULL;
 933        if (!_wmktemp(wtemplate))
 934                return NULL;
 935        if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
 936                return NULL;
 937        return template;
 938}
 939
 940int mkstemp(char *template)
 941{
 942        char *filename = mktemp(template);
 943        if (filename == NULL)
 944                return -1;
 945        return open(filename, O_RDWR | O_CREAT, 0600);
 946}
 947
 948int gettimeofday(struct timeval *tv, void *tz)
 949{
 950        FILETIME ft;
 951        long long hnsec;
 952
 953        GetSystemTimeAsFileTime(&ft);
 954        hnsec = filetime_to_hnsec(&ft);
 955        tv->tv_sec = hnsec / 10000000;
 956        tv->tv_usec = (hnsec % 10000000) / 10;
 957        return 0;
 958}
 959
 960int pipe(int filedes[2])
 961{
 962        HANDLE h[2];
 963
 964        /* this creates non-inheritable handles */
 965        if (!CreatePipe(&h[0], &h[1], NULL, 8192)) {
 966                errno = err_win_to_posix(GetLastError());
 967                return -1;
 968        }
 969        filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
 970        if (filedes[0] < 0) {
 971                CloseHandle(h[0]);
 972                CloseHandle(h[1]);
 973                return -1;
 974        }
 975        filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
 976        if (filedes[1] < 0) {
 977                close(filedes[0]);
 978                CloseHandle(h[1]);
 979                return -1;
 980        }
 981        return 0;
 982}
 983
 984struct tm *gmtime_r(const time_t *timep, struct tm *result)
 985{
 986        /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
 987        memcpy(result, gmtime(timep), sizeof(struct tm));
 988        return result;
 989}
 990
 991struct tm *localtime_r(const time_t *timep, struct tm *result)
 992{
 993        /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
 994        memcpy(result, localtime(timep), sizeof(struct tm));
 995        return result;
 996}
 997
 998char *mingw_getcwd(char *pointer, int len)
 999{
1000        wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
1001        DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
1002
1003        if (!ret || ret >= ARRAY_SIZE(cwd)) {
1004                errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
1005                return NULL;
1006        }
1007        ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
1008        if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
1009                HANDLE hnd = CreateFileW(cwd, 0,
1010                        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
1011                        OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1012                if (hnd == INVALID_HANDLE_VALUE)
1013                        return NULL;
1014                ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
1015                CloseHandle(hnd);
1016                if (!ret || ret >= ARRAY_SIZE(wpointer))
1017                        return NULL;
1018                if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
1019                        return NULL;
1020                return pointer;
1021        }
1022        if (!ret || ret >= ARRAY_SIZE(wpointer))
1023                return NULL;
1024        if (xwcstoutf(pointer, wpointer, len) < 0)
1025                return NULL;
1026        convert_slashes(pointer);
1027        return pointer;
1028}
1029
1030/*
1031 * See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
1032 * https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
1033 */
1034static const char *quote_arg(const char *arg)
1035{
1036        /* count chars to quote */
1037        int len = 0, n = 0;
1038        int force_quotes = 0;
1039        char *q, *d;
1040        const char *p = arg;
1041        if (!*p) force_quotes = 1;
1042        while (*p) {
1043                if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
1044                        force_quotes = 1;
1045                else if (*p == '"')
1046                        n++;
1047                else if (*p == '\\') {
1048                        int count = 0;
1049                        while (*p == '\\') {
1050                                count++;
1051                                p++;
1052                                len++;
1053                        }
1054                        if (*p == '"')
1055                                n += count*2 + 1;
1056                        continue;
1057                }
1058                len++;
1059                p++;
1060        }
1061        if (!force_quotes && n == 0)
1062                return arg;
1063
1064        /* insert \ where necessary */
1065        d = q = xmalloc(st_add3(len, n, 3));
1066        *d++ = '"';
1067        while (*arg) {
1068                if (*arg == '"')
1069                        *d++ = '\\';
1070                else if (*arg == '\\') {
1071                        int count = 0;
1072                        while (*arg == '\\') {
1073                                count++;
1074                                *d++ = *arg++;
1075                        }
1076                        if (*arg == '"') {
1077                                while (count-- > 0)
1078                                        *d++ = '\\';
1079                                *d++ = '\\';
1080                        }
1081                }
1082                *d++ = *arg++;
1083        }
1084        *d++ = '"';
1085        *d++ = 0;
1086        return q;
1087}
1088
1089static const char *parse_interpreter(const char *cmd)
1090{
1091        static char buf[100];
1092        char *p, *opt;
1093        int n, fd;
1094
1095        /* don't even try a .exe */
1096        n = strlen(cmd);
1097        if (n >= 4 && !strcasecmp(cmd+n-4, ".exe"))
1098                return NULL;
1099
1100        fd = open(cmd, O_RDONLY);
1101        if (fd < 0)
1102                return NULL;
1103        n = read(fd, buf, sizeof(buf)-1);
1104        close(fd);
1105        if (n < 4)      /* at least '#!/x' and not error */
1106                return NULL;
1107
1108        if (buf[0] != '#' || buf[1] != '!')
1109                return NULL;
1110        buf[n] = '\0';
1111        p = buf + strcspn(buf, "\r\n");
1112        if (!*p)
1113                return NULL;
1114
1115        *p = '\0';
1116        if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\')))
1117                return NULL;
1118        /* strip options */
1119        if ((opt = strchr(p+1, ' ')))
1120                *opt = '\0';
1121        return p+1;
1122}
1123
1124/*
1125 * exe_only means that we only want to detect .exe files, but not scripts
1126 * (which do not have an extension)
1127 */
1128static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
1129                         int isexe, int exe_only)
1130{
1131        char path[MAX_PATH];
1132        snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
1133
1134        if (!isexe && access(path, F_OK) == 0)
1135                return xstrdup(path);
1136        path[strlen(path)-4] = '\0';
1137        if ((!exe_only || isexe) && access(path, F_OK) == 0)
1138                if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
1139                        return xstrdup(path);
1140        return NULL;
1141}
1142
1143/*
1144 * Determines the absolute path of cmd using the split path in path.
1145 * If cmd contains a slash or backslash, no lookup is performed.
1146 */
1147static char *path_lookup(const char *cmd, int exe_only)
1148{
1149        const char *path;
1150        char *prog = NULL;
1151        int len = strlen(cmd);
1152        int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
1153
1154        if (strchr(cmd, '/') || strchr(cmd, '\\'))
1155                return xstrdup(cmd);
1156
1157        path = mingw_getenv("PATH");
1158        if (!path)
1159                return NULL;
1160
1161        while (!prog) {
1162                const char *sep = strchrnul(path, ';');
1163                int dirlen = sep - path;
1164                if (dirlen)
1165                        prog = lookup_prog(path, dirlen, cmd, isexe, exe_only);
1166                if (!*sep)
1167                        break;
1168                path = sep + 1;
1169        }
1170
1171        return prog;
1172}
1173
1174static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
1175{
1176        while (*s && *s != c)
1177                s++;
1178        return s;
1179}
1180
1181/* Compare only keys */
1182static int wenvcmp(const void *a, const void *b)
1183{
1184        wchar_t *p = *(wchar_t **)a, *q = *(wchar_t **)b;
1185        size_t p_len, q_len;
1186
1187        /* Find the keys */
1188        p_len = wcschrnul(p, L'=') - p;
1189        q_len = wcschrnul(q, L'=') - q;
1190
1191        /* If the length differs, include the shorter key's NUL */
1192        if (p_len < q_len)
1193                p_len++;
1194        else if (p_len > q_len)
1195                p_len = q_len + 1;
1196
1197        return _wcsnicmp(p, q, p_len);
1198}
1199
1200/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
1201#ifndef INTERNAL_QSORT
1202#include "qsort.c"
1203#endif
1204
1205/*
1206 * Build an environment block combining the inherited environment
1207 * merged with the given list of settings.
1208 *
1209 * Values of the form "KEY=VALUE" in deltaenv override inherited values.
1210 * Values of the form "KEY" in deltaenv delete inherited values.
1211 *
1212 * Multiple entries in deltaenv for the same key are explicitly allowed.
1213 *
1214 * We return a contiguous block of UNICODE strings with a final trailing
1215 * zero word.
1216 */
1217static wchar_t *make_environment_block(char **deltaenv)
1218{
1219        wchar_t *wenv = GetEnvironmentStringsW(), *wdeltaenv, *result, *p;
1220        size_t wlen, s, delta_size, size;
1221
1222        wchar_t **array = NULL;
1223        size_t alloc = 0, nr = 0, i;
1224
1225        size = 1; /* for extra NUL at the end */
1226
1227        /* If there is no deltaenv to apply, simply return a copy. */
1228        if (!deltaenv || !*deltaenv) {
1229                for (p = wenv; p && *p; ) {
1230                        size_t s = wcslen(p) + 1;
1231                        size += s;
1232                        p += s;
1233                }
1234
1235                ALLOC_ARRAY(result, size);
1236                memcpy(result, wenv, size * sizeof(*wenv));
1237                FreeEnvironmentStringsW(wenv);
1238                return result;
1239        }
1240
1241        /*
1242         * If there is a deltaenv, let's accumulate all keys into `array`,
1243         * sort them using the stable git_qsort() and then copy, skipping
1244         * duplicate keys
1245         */
1246        for (p = wenv; p && *p; ) {
1247                ALLOC_GROW(array, nr + 1, alloc);
1248                s = wcslen(p) + 1;
1249                array[nr++] = p;
1250                p += s;
1251                size += s;
1252        }
1253
1254        /* (over-)assess size needed for wchar version of deltaenv */
1255        for (delta_size = 0, i = 0; deltaenv[i]; i++)
1256                delta_size += strlen(deltaenv[i]) * 2 + 1;
1257        ALLOC_ARRAY(wdeltaenv, delta_size);
1258
1259        /* convert the deltaenv, appending to array */
1260        for (i = 0, p = wdeltaenv; deltaenv[i]; i++) {
1261                ALLOC_GROW(array, nr + 1, alloc);
1262                wlen = xutftowcs(p, deltaenv[i], wdeltaenv + delta_size - p);
1263                array[nr++] = p;
1264                p += wlen + 1;
1265        }
1266
1267        git_qsort(array, nr, sizeof(*array), wenvcmp);
1268        ALLOC_ARRAY(result, size + delta_size);
1269
1270        for (p = result, i = 0; i < nr; i++) {
1271                /* Skip any duplicate keys; last one wins */
1272                while (i + 1 < nr && !wenvcmp(array + i, array + i + 1))
1273                       i++;
1274
1275                /* Skip "to delete" entry */
1276                if (!wcschr(array[i], L'='))
1277                        continue;
1278
1279                size = wcslen(array[i]) + 1;
1280                memcpy(p, array[i], size * sizeof(*p));
1281                p += size;
1282        }
1283        *p = L'\0';
1284
1285        free(array);
1286        free(wdeltaenv);
1287        FreeEnvironmentStringsW(wenv);
1288        return result;
1289}
1290
1291static void do_unset_environment_variables(void)
1292{
1293        static int done;
1294        char *p = unset_environment_variables;
1295
1296        if (done || !p)
1297                return;
1298        done = 1;
1299
1300        for (;;) {
1301                char *comma = strchr(p, ',');
1302
1303                if (comma)
1304                        *comma = '\0';
1305                unsetenv(p);
1306                if (!comma)
1307                        break;
1308                p = comma + 1;
1309        }
1310}
1311
1312struct pinfo_t {
1313        struct pinfo_t *next;
1314        pid_t pid;
1315        HANDLE proc;
1316};
1317static struct pinfo_t *pinfo = NULL;
1318CRITICAL_SECTION pinfo_cs;
1319
1320static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
1321                              const char *dir,
1322                              int prepend_cmd, int fhin, int fhout, int fherr)
1323{
1324        STARTUPINFOW si;
1325        PROCESS_INFORMATION pi;
1326        struct strbuf args;
1327        wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
1328        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
1329        BOOL ret;
1330        HANDLE cons;
1331
1332        do_unset_environment_variables();
1333
1334        /* Determine whether or not we are associated to a console */
1335        cons = CreateFile("CONOUT$", GENERIC_WRITE,
1336                        FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
1337                        FILE_ATTRIBUTE_NORMAL, NULL);
1338        if (cons == INVALID_HANDLE_VALUE) {
1339                /* There is no console associated with this process.
1340                 * Since the child is a console process, Windows
1341                 * would normally create a console window. But
1342                 * since we'll be redirecting std streams, we do
1343                 * not need the console.
1344                 * It is necessary to use DETACHED_PROCESS
1345                 * instead of CREATE_NO_WINDOW to make ssh
1346                 * recognize that it has no console.
1347                 */
1348                flags |= DETACHED_PROCESS;
1349        } else {
1350                /* There is already a console. If we specified
1351                 * DETACHED_PROCESS here, too, Windows would
1352                 * disassociate the child from the console.
1353                 * The same is true for CREATE_NO_WINDOW.
1354                 * Go figure!
1355                 */
1356                CloseHandle(cons);
1357        }
1358        memset(&si, 0, sizeof(si));
1359        si.cb = sizeof(si);
1360        si.dwFlags = STARTF_USESTDHANDLES;
1361        si.hStdInput = winansi_get_osfhandle(fhin);
1362        si.hStdOutput = winansi_get_osfhandle(fhout);
1363        si.hStdError = winansi_get_osfhandle(fherr);
1364
1365        if (xutftowcs_path(wcmd, cmd) < 0)
1366                return -1;
1367        if (dir && xutftowcs_path(wdir, dir) < 0)
1368                return -1;
1369
1370        /* concatenate argv, quoting args as we go */
1371        strbuf_init(&args, 0);
1372        if (prepend_cmd) {
1373                char *quoted = (char *)quote_arg(cmd);
1374                strbuf_addstr(&args, quoted);
1375                if (quoted != cmd)
1376                        free(quoted);
1377        }
1378        for (; *argv; argv++) {
1379                char *quoted = (char *)quote_arg(*argv);
1380                if (*args.buf)
1381                        strbuf_addch(&args, ' ');
1382                strbuf_addstr(&args, quoted);
1383                if (quoted != *argv)
1384                        free(quoted);
1385        }
1386
1387        ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
1388        xutftowcs(wargs, args.buf, 2 * args.len + 1);
1389        strbuf_release(&args);
1390
1391        wenvblk = make_environment_block(deltaenv);
1392
1393        memset(&pi, 0, sizeof(pi));
1394        ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
1395                wenvblk, dir ? wdir : NULL, &si, &pi);
1396
1397        free(wenvblk);
1398        free(wargs);
1399
1400        if (!ret) {
1401                errno = ENOENT;
1402                return -1;
1403        }
1404        CloseHandle(pi.hThread);
1405
1406        /*
1407         * The process ID is the human-readable identifier of the process
1408         * that we want to present in log and error messages. The handle
1409         * is not useful for this purpose. But we cannot close it, either,
1410         * because it is not possible to turn a process ID into a process
1411         * handle after the process terminated.
1412         * Keep the handle in a list for waitpid.
1413         */
1414        EnterCriticalSection(&pinfo_cs);
1415        {
1416                struct pinfo_t *info = xmalloc(sizeof(struct pinfo_t));
1417                info->pid = pi.dwProcessId;
1418                info->proc = pi.hProcess;
1419                info->next = pinfo;
1420                pinfo = info;
1421        }
1422        LeaveCriticalSection(&pinfo_cs);
1423
1424        return (pid_t)pi.dwProcessId;
1425}
1426
1427static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
1428{
1429        return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
1430}
1431
1432pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
1433                     const char *dir,
1434                     int fhin, int fhout, int fherr)
1435{
1436        pid_t pid;
1437        char *prog = path_lookup(cmd, 0);
1438
1439        if (!prog) {
1440                errno = ENOENT;
1441                pid = -1;
1442        }
1443        else {
1444                const char *interpr = parse_interpreter(prog);
1445
1446                if (interpr) {
1447                        const char *argv0 = argv[0];
1448                        char *iprog = path_lookup(interpr, 1);
1449                        argv[0] = prog;
1450                        if (!iprog) {
1451                                errno = ENOENT;
1452                                pid = -1;
1453                        }
1454                        else {
1455                                pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
1456                                                       fhin, fhout, fherr);
1457                                free(iprog);
1458                        }
1459                        argv[0] = argv0;
1460                }
1461                else
1462                        pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
1463                                               fhin, fhout, fherr);
1464                free(prog);
1465        }
1466        return pid;
1467}
1468
1469static int try_shell_exec(const char *cmd, char *const *argv)
1470{
1471        const char *interpr = parse_interpreter(cmd);
1472        char *prog;
1473        int pid = 0;
1474
1475        if (!interpr)
1476                return 0;
1477        prog = path_lookup(interpr, 1);
1478        if (prog) {
1479                int argc = 0;
1480                const char **argv2;
1481                while (argv[argc]) argc++;
1482                ALLOC_ARRAY(argv2, argc + 1);
1483                argv2[0] = (char *)cmd; /* full path to the script file */
1484                memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
1485                pid = mingw_spawnv(prog, argv2, 1);
1486                if (pid >= 0) {
1487                        int status;
1488                        if (waitpid(pid, &status, 0) < 0)
1489                                status = 255;
1490                        exit(status);
1491                }
1492                pid = 1;        /* indicate that we tried but failed */
1493                free(prog);
1494                free(argv2);
1495        }
1496        return pid;
1497}
1498
1499int mingw_execv(const char *cmd, char *const *argv)
1500{
1501        /* check if git_command is a shell script */
1502        if (!try_shell_exec(cmd, argv)) {
1503                int pid, status;
1504
1505                pid = mingw_spawnv(cmd, (const char **)argv, 0);
1506                if (pid < 0)
1507                        return -1;
1508                if (waitpid(pid, &status, 0) < 0)
1509                        status = 255;
1510                exit(status);
1511        }
1512        return -1;
1513}
1514
1515int mingw_execvp(const char *cmd, char *const *argv)
1516{
1517        char *prog = path_lookup(cmd, 0);
1518
1519        if (prog) {
1520                mingw_execv(prog, argv);
1521                free(prog);
1522        } else
1523                errno = ENOENT;
1524
1525        return -1;
1526}
1527
1528int mingw_kill(pid_t pid, int sig)
1529{
1530        if (pid > 0 && sig == SIGTERM) {
1531                HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
1532
1533                if (TerminateProcess(h, -1)) {
1534                        CloseHandle(h);
1535                        return 0;
1536                }
1537
1538                errno = err_win_to_posix(GetLastError());
1539                CloseHandle(h);
1540                return -1;
1541        } else if (pid > 0 && sig == 0) {
1542                HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
1543                if (h) {
1544                        CloseHandle(h);
1545                        return 0;
1546                }
1547        }
1548
1549        errno = EINVAL;
1550        return -1;
1551}
1552
1553/*
1554 * UTF-8 versions of getenv(), putenv() and unsetenv().
1555 * Internally, they use the CRT's stock UNICODE routines
1556 * to avoid data loss.
1557 */
1558char *mingw_getenv(const char *name)
1559{
1560#define GETENV_MAX_RETAIN 30
1561        static char *values[GETENV_MAX_RETAIN];
1562        static int value_counter;
1563        int len_key, len_value;
1564        wchar_t *w_key;
1565        char *value;
1566        wchar_t w_value[32768];
1567
1568        if (!name || !*name)
1569                return NULL;
1570
1571        len_key = strlen(name) + 1;
1572        /* We cannot use xcalloc() here because that uses getenv() itself */
1573        w_key = calloc(len_key, sizeof(wchar_t));
1574        if (!w_key)
1575                die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
1576        xutftowcs(w_key, name, len_key);
1577        len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
1578        if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
1579                free(w_key);
1580                return NULL;
1581        }
1582        free(w_key);
1583
1584        len_value = len_value * 3 + 1;
1585        /* We cannot use xcalloc() here because that uses getenv() itself */
1586        value = calloc(len_value, sizeof(char));
1587        if (!value)
1588                die("Out of memory, (tried to allocate %u bytes)", len_value);
1589        xwcstoutf(value, w_value, len_value);
1590
1591        /*
1592         * We return `value` which is an allocated value and the caller is NOT
1593         * expecting to have to free it, so we keep a round-robin array,
1594         * invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
1595         */
1596        free(values[value_counter]);
1597        values[value_counter++] = value;
1598        if (value_counter >= ARRAY_SIZE(values))
1599                value_counter = 0;
1600
1601        return value;
1602}
1603
1604int mingw_putenv(const char *namevalue)
1605{
1606        int size;
1607        wchar_t *wide, *equal;
1608        BOOL result;
1609
1610        if (!namevalue || !*namevalue)
1611                return 0;
1612
1613        size = strlen(namevalue) * 2 + 1;
1614        wide = calloc(size, sizeof(wchar_t));
1615        if (!wide)
1616                die("Out of memory, (tried to allocate %u wchar_t's)", size);
1617        xutftowcs(wide, namevalue, size);
1618        equal = wcschr(wide, L'=');
1619        if (!equal)
1620                result = SetEnvironmentVariableW(wide, NULL);
1621        else {
1622                *equal = L'\0';
1623                result = SetEnvironmentVariableW(wide, equal + 1);
1624        }
1625        free(wide);
1626
1627        if (!result)
1628                errno = err_win_to_posix(GetLastError());
1629
1630        return result ? 0 : -1;
1631}
1632
1633/*
1634 * Note, this isn't a complete replacement for getaddrinfo. It assumes
1635 * that service contains a numerical port, or that it is null. It
1636 * does a simple search using gethostbyname, and returns one IPv4 host
1637 * if one was found.
1638 */
1639static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
1640                                   const struct addrinfo *hints,
1641                                   struct addrinfo **res)
1642{
1643        struct hostent *h = NULL;
1644        struct addrinfo *ai;
1645        struct sockaddr_in *sin;
1646
1647        if (node) {
1648                h = gethostbyname(node);
1649                if (!h)
1650                        return WSAGetLastError();
1651        }
1652
1653        ai = xmalloc(sizeof(struct addrinfo));
1654        *res = ai;
1655        ai->ai_flags = 0;
1656        ai->ai_family = AF_INET;
1657        ai->ai_socktype = hints ? hints->ai_socktype : 0;
1658        switch (ai->ai_socktype) {
1659        case SOCK_STREAM:
1660                ai->ai_protocol = IPPROTO_TCP;
1661                break;
1662        case SOCK_DGRAM:
1663                ai->ai_protocol = IPPROTO_UDP;
1664                break;
1665        default:
1666                ai->ai_protocol = 0;
1667                break;
1668        }
1669        ai->ai_addrlen = sizeof(struct sockaddr_in);
1670        if (hints && (hints->ai_flags & AI_CANONNAME))
1671                ai->ai_canonname = h ? xstrdup(h->h_name) : NULL;
1672        else
1673                ai->ai_canonname = NULL;
1674
1675        sin = xcalloc(1, ai->ai_addrlen);
1676        sin->sin_family = AF_INET;
1677        /* Note: getaddrinfo is supposed to allow service to be a string,
1678         * which should be looked up using getservbyname. This is
1679         * currently not implemented */
1680        if (service)
1681                sin->sin_port = htons(atoi(service));
1682        if (h)
1683                sin->sin_addr = *(struct in_addr *)h->h_addr;
1684        else if (hints && (hints->ai_flags & AI_PASSIVE))
1685                sin->sin_addr.s_addr = INADDR_ANY;
1686        else
1687                sin->sin_addr.s_addr = INADDR_LOOPBACK;
1688        ai->ai_addr = (struct sockaddr *)sin;
1689        ai->ai_next = NULL;
1690        return 0;
1691}
1692
1693static void WSAAPI freeaddrinfo_stub(struct addrinfo *res)
1694{
1695        free(res->ai_canonname);
1696        free(res->ai_addr);
1697        free(res);
1698}
1699
1700static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen,
1701                                   char *host, DWORD hostlen,
1702                                   char *serv, DWORD servlen, int flags)
1703{
1704        const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
1705        if (sa->sa_family != AF_INET)
1706                return EAI_FAMILY;
1707        if (!host && !serv)
1708                return EAI_NONAME;
1709
1710        if (host && hostlen > 0) {
1711                struct hostent *ent = NULL;
1712                if (!(flags & NI_NUMERICHOST))
1713                        ent = gethostbyaddr((const char *)&sin->sin_addr,
1714                                            sizeof(sin->sin_addr), AF_INET);
1715
1716                if (ent)
1717                        snprintf(host, hostlen, "%s", ent->h_name);
1718                else if (flags & NI_NAMEREQD)
1719                        return EAI_NONAME;
1720                else
1721                        snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
1722        }
1723
1724        if (serv && servlen > 0) {
1725                struct servent *ent = NULL;
1726                if (!(flags & NI_NUMERICSERV))
1727                        ent = getservbyport(sin->sin_port,
1728                                            flags & NI_DGRAM ? "udp" : "tcp");
1729
1730                if (ent)
1731                        snprintf(serv, servlen, "%s", ent->s_name);
1732                else
1733                        snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
1734        }
1735
1736        return 0;
1737}
1738
1739static HMODULE ipv6_dll = NULL;
1740static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res);
1741static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service,
1742                                      const struct addrinfo *hints,
1743                                      struct addrinfo **res);
1744static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
1745                                      char *host, DWORD hostlen,
1746                                      char *serv, DWORD servlen, int flags);
1747/*
1748 * gai_strerror is an inline function in the ws2tcpip.h header, so we
1749 * don't need to try to load that one dynamically.
1750 */
1751
1752static void socket_cleanup(void)
1753{
1754        WSACleanup();
1755        if (ipv6_dll)
1756                FreeLibrary(ipv6_dll);
1757        ipv6_dll = NULL;
1758        ipv6_freeaddrinfo = freeaddrinfo_stub;
1759        ipv6_getaddrinfo = getaddrinfo_stub;
1760        ipv6_getnameinfo = getnameinfo_stub;
1761}
1762
1763static void ensure_socket_initialization(void)
1764{
1765        WSADATA wsa;
1766        static int initialized = 0;
1767        const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL };
1768        const char **name;
1769
1770        if (initialized)
1771                return;
1772
1773        if (WSAStartup(MAKEWORD(2,2), &wsa))
1774                die("unable to initialize winsock subsystem, error %d",
1775                        WSAGetLastError());
1776
1777        for (name = libraries; *name; name++) {
1778                ipv6_dll = LoadLibraryExA(*name, NULL,
1779                                          LOAD_LIBRARY_SEARCH_SYSTEM32);
1780                if (!ipv6_dll)
1781                        continue;
1782
1783                ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *))
1784                        GetProcAddress(ipv6_dll, "freeaddrinfo");
1785                ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *,
1786                                                   const struct addrinfo *,
1787                                                   struct addrinfo **))
1788                        GetProcAddress(ipv6_dll, "getaddrinfo");
1789                ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *,
1790                                                   socklen_t, char *, DWORD,
1791                                                   char *, DWORD, int))
1792                        GetProcAddress(ipv6_dll, "getnameinfo");
1793                if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
1794                        FreeLibrary(ipv6_dll);
1795                        ipv6_dll = NULL;
1796                } else
1797                        break;
1798        }
1799        if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
1800                ipv6_freeaddrinfo = freeaddrinfo_stub;
1801                ipv6_getaddrinfo = getaddrinfo_stub;
1802                ipv6_getnameinfo = getnameinfo_stub;
1803        }
1804
1805        atexit(socket_cleanup);
1806        initialized = 1;
1807}
1808
1809#undef gethostname
1810int mingw_gethostname(char *name, int namelen)
1811{
1812    ensure_socket_initialization();
1813    return gethostname(name, namelen);
1814}
1815
1816#undef gethostbyname
1817struct hostent *mingw_gethostbyname(const char *host)
1818{
1819        ensure_socket_initialization();
1820        return gethostbyname(host);
1821}
1822
1823void mingw_freeaddrinfo(struct addrinfo *res)
1824{
1825        ipv6_freeaddrinfo(res);
1826}
1827
1828int mingw_getaddrinfo(const char *node, const char *service,
1829                      const struct addrinfo *hints, struct addrinfo **res)
1830{
1831        ensure_socket_initialization();
1832        return ipv6_getaddrinfo(node, service, hints, res);
1833}
1834
1835int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
1836                      char *host, DWORD hostlen, char *serv, DWORD servlen,
1837                      int flags)
1838{
1839        ensure_socket_initialization();
1840        return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
1841}
1842
1843int mingw_socket(int domain, int type, int protocol)
1844{
1845        int sockfd;
1846        SOCKET s;
1847
1848        ensure_socket_initialization();
1849        s = WSASocket(domain, type, protocol, NULL, 0, 0);
1850        if (s == INVALID_SOCKET) {
1851                /*
1852                 * WSAGetLastError() values are regular BSD error codes
1853                 * biased by WSABASEERR.
1854                 * However, strerror() does not know about networking
1855                 * specific errors, which are values beginning at 38 or so.
1856                 * Therefore, we choose to leave the biased error code
1857                 * in errno so that _if_ someone looks up the code somewhere,
1858                 * then it is at least the number that are usually listed.
1859                 */
1860                errno = WSAGetLastError();
1861                return -1;
1862        }
1863        /* convert into a file descriptor */
1864        if ((sockfd = _open_osfhandle(s, O_RDWR|O_BINARY)) < 0) {
1865                closesocket(s);
1866                return error("unable to make a socket file descriptor: %s",
1867                        strerror(errno));
1868        }
1869        return sockfd;
1870}
1871
1872#undef connect
1873int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
1874{
1875        SOCKET s = (SOCKET)_get_osfhandle(sockfd);
1876        return connect(s, sa, sz);
1877}
1878
1879#undef bind
1880int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
1881{
1882        SOCKET s = (SOCKET)_get_osfhandle(sockfd);
1883        return bind(s, sa, sz);
1884}
1885
1886#undef setsockopt
1887int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
1888{
1889        SOCKET s = (SOCKET)_get_osfhandle(sockfd);
1890        return setsockopt(s, lvl, optname, (const char*)optval, optlen);
1891}
1892
1893#undef shutdown
1894int mingw_shutdown(int sockfd, int how)
1895{
1896        SOCKET s = (SOCKET)_get_osfhandle(sockfd);
1897        return shutdown(s, how);
1898}
1899
1900#undef listen
1901int mingw_listen(int sockfd, int backlog)
1902{
1903        SOCKET s = (SOCKET)_get_osfhandle(sockfd);
1904        return listen(s, backlog);
1905}
1906
1907#undef accept
1908int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
1909{
1910        int sockfd2;
1911
1912        SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
1913        SOCKET s2 = accept(s1, sa, sz);
1914
1915        /* convert into a file descriptor */
1916        if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
1917                int err = errno;
1918                closesocket(s2);
1919                return error("unable to make a socket file descriptor: %s",
1920                        strerror(err));
1921        }
1922        return sockfd2;
1923}
1924
1925#undef rename
1926int mingw_rename(const char *pold, const char *pnew)
1927{
1928        DWORD attrs, gle;
1929        int tries = 0;
1930        wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
1931        if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
1932                return -1;
1933
1934        /*
1935         * Try native rename() first to get errno right.
1936         * It is based on MoveFile(), which cannot overwrite existing files.
1937         */
1938        if (!_wrename(wpold, wpnew))
1939                return 0;
1940        if (errno != EEXIST)
1941                return -1;
1942repeat:
1943        if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
1944                return 0;
1945        /* TODO: translate more errors */
1946        gle = GetLastError();
1947        if (gle == ERROR_ACCESS_DENIED &&
1948            (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
1949                if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
1950                        DWORD attrsold = GetFileAttributesW(wpold);
1951                        if (attrsold == INVALID_FILE_ATTRIBUTES ||
1952                            !(attrsold & FILE_ATTRIBUTE_DIRECTORY))
1953                                errno = EISDIR;
1954                        else if (!_wrmdir(wpnew))
1955                                goto repeat;
1956                        return -1;
1957                }
1958                if ((attrs & FILE_ATTRIBUTE_READONLY) &&
1959                    SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
1960                        if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
1961                                return 0;
1962                        gle = GetLastError();
1963                        /* revert file attributes on failure */
1964                        SetFileAttributesW(wpnew, attrs);
1965                }
1966        }
1967        if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
1968                /*
1969                 * We assume that some other process had the source or
1970                 * destination file open at the wrong moment and retry.
1971                 * In order to give the other process a higher chance to
1972                 * complete its operation, we give up our time slice now.
1973                 * If we have to retry again, we do sleep a bit.
1974                 */
1975                Sleep(delay[tries]);
1976                tries++;
1977                goto repeat;
1978        }
1979        if (gle == ERROR_ACCESS_DENIED &&
1980               ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
1981                       "Should I try again?", pold, pnew))
1982                goto repeat;
1983
1984        errno = EACCES;
1985        return -1;
1986}
1987
1988/*
1989 * Note that this doesn't return the actual pagesize, but
1990 * the allocation granularity. If future Windows specific git code
1991 * needs the real getpagesize function, we need to find another solution.
1992 */
1993int mingw_getpagesize(void)
1994{
1995        SYSTEM_INFO si;
1996        GetSystemInfo(&si);
1997        return si.dwAllocationGranularity;
1998}
1999
2000/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
2001enum EXTENDED_NAME_FORMAT {
2002        NameDisplay = 3,
2003        NameUserPrincipal = 8
2004};
2005
2006static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
2007{
2008        DECLARE_PROC_ADDR(secur32.dll, BOOL, GetUserNameExW,
2009                enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
2010        static wchar_t wbuffer[1024];
2011        DWORD len;
2012
2013        if (!INIT_PROC_ADDR(GetUserNameExW))
2014                return NULL;
2015
2016        len = ARRAY_SIZE(wbuffer);
2017        if (GetUserNameExW(type, wbuffer, &len)) {
2018                char *converted = xmalloc((len *= 3));
2019                if (xwcstoutf(converted, wbuffer, len) >= 0)
2020                        return converted;
2021                free(converted);
2022        }
2023
2024        return NULL;
2025}
2026
2027char *mingw_query_user_email(void)
2028{
2029        return get_extended_user_info(NameUserPrincipal);
2030}
2031
2032struct passwd *getpwuid(int uid)
2033{
2034        static unsigned initialized;
2035        static char user_name[100];
2036        static struct passwd *p;
2037        DWORD len;
2038
2039        if (initialized)
2040                return p;
2041
2042        len = sizeof(user_name);
2043        if (!GetUserName(user_name, &len)) {
2044                initialized = 1;
2045                return NULL;
2046        }
2047
2048        p = xmalloc(sizeof(*p));
2049        p->pw_name = user_name;
2050        p->pw_gecos = get_extended_user_info(NameDisplay);
2051        if (!p->pw_gecos)
2052                p->pw_gecos = "unknown";
2053        p->pw_dir = NULL;
2054
2055        initialized = 1;
2056        return p;
2057}
2058
2059static HANDLE timer_event;
2060static HANDLE timer_thread;
2061static int timer_interval;
2062static int one_shot;
2063static sig_handler_t timer_fn = SIG_DFL, sigint_fn = SIG_DFL;
2064
2065/* The timer works like this:
2066 * The thread, ticktack(), is a trivial routine that most of the time
2067 * only waits to receive the signal to terminate. The main thread tells
2068 * the thread to terminate by setting the timer_event to the signalled
2069 * state.
2070 * But ticktack() interrupts the wait state after the timer's interval
2071 * length to call the signal handler.
2072 */
2073
2074static unsigned __stdcall ticktack(void *dummy)
2075{
2076        while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
2077                mingw_raise(SIGALRM);
2078                if (one_shot)
2079                        break;
2080        }
2081        return 0;
2082}
2083
2084static int start_timer_thread(void)
2085{
2086        timer_event = CreateEvent(NULL, FALSE, FALSE, NULL);
2087        if (timer_event) {
2088                timer_thread = (HANDLE) _beginthreadex(NULL, 0, ticktack, NULL, 0, NULL);
2089                if (!timer_thread )
2090                        return errno = ENOMEM,
2091                                error("cannot start timer thread");
2092        } else
2093                return errno = ENOMEM,
2094                        error("cannot allocate resources for timer");
2095        return 0;
2096}
2097
2098static void stop_timer_thread(void)
2099{
2100        if (timer_event)
2101                SetEvent(timer_event);  /* tell thread to terminate */
2102        if (timer_thread) {
2103                int rc = WaitForSingleObject(timer_thread, 1000);
2104                if (rc == WAIT_TIMEOUT)
2105                        error("timer thread did not terminate timely");
2106                else if (rc != WAIT_OBJECT_0)
2107                        error("waiting for timer thread failed: %lu",
2108                              GetLastError());
2109                CloseHandle(timer_thread);
2110        }
2111        if (timer_event)
2112                CloseHandle(timer_event);
2113        timer_event = NULL;
2114        timer_thread = NULL;
2115}
2116
2117static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *i2)
2118{
2119        return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec;
2120}
2121
2122int setitimer(int type, struct itimerval *in, struct itimerval *out)
2123{
2124        static const struct timeval zero;
2125        static int atexit_done;
2126
2127        if (out != NULL)
2128                return errno = EINVAL,
2129                        error("setitimer param 3 != NULL not implemented");
2130        if (!is_timeval_eq(&in->it_interval, &zero) &&
2131            !is_timeval_eq(&in->it_interval, &in->it_value))
2132                return errno = EINVAL,
2133                        error("setitimer: it_interval must be zero or eq it_value");
2134
2135        if (timer_thread)
2136                stop_timer_thread();
2137
2138        if (is_timeval_eq(&in->it_value, &zero) &&
2139            is_timeval_eq(&in->it_interval, &zero))
2140                return 0;
2141
2142        timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000;
2143        one_shot = is_timeval_eq(&in->it_interval, &zero);
2144        if (!atexit_done) {
2145                atexit(stop_timer_thread);
2146                atexit_done = 1;
2147        }
2148        return start_timer_thread();
2149}
2150
2151int sigaction(int sig, struct sigaction *in, struct sigaction *out)
2152{
2153        if (sig != SIGALRM)
2154                return errno = EINVAL,
2155                        error("sigaction only implemented for SIGALRM");
2156        if (out != NULL)
2157                return errno = EINVAL,
2158                        error("sigaction: param 3 != NULL not implemented");
2159
2160        timer_fn = in->sa_handler;
2161        return 0;
2162}
2163
2164#undef signal
2165sig_handler_t mingw_signal(int sig, sig_handler_t handler)
2166{
2167        sig_handler_t old;
2168
2169        switch (sig) {
2170        case SIGALRM:
2171                old = timer_fn;
2172                timer_fn = handler;
2173                break;
2174
2175        case SIGINT:
2176                old = sigint_fn;
2177                sigint_fn = handler;
2178                break;
2179
2180        default:
2181                return signal(sig, handler);
2182        }
2183
2184        return old;
2185}
2186
2187#undef raise
2188int mingw_raise(int sig)
2189{
2190        switch (sig) {
2191        case SIGALRM:
2192                if (timer_fn == SIG_DFL) {
2193                        if (isatty(STDERR_FILENO))
2194                                fputs("Alarm clock\n", stderr);
2195                        exit(128 + SIGALRM);
2196                } else if (timer_fn != SIG_IGN)
2197                        timer_fn(SIGALRM);
2198                return 0;
2199
2200        case SIGINT:
2201                if (sigint_fn == SIG_DFL)
2202                        exit(128 + SIGINT);
2203                else if (sigint_fn != SIG_IGN)
2204                        sigint_fn(SIGINT);
2205                return 0;
2206
2207        default:
2208                return raise(sig);
2209        }
2210}
2211
2212int link(const char *oldpath, const char *newpath)
2213{
2214        wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
2215        if (xutftowcs_path(woldpath, oldpath) < 0 ||
2216                xutftowcs_path(wnewpath, newpath) < 0)
2217                return -1;
2218
2219        if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
2220                errno = err_win_to_posix(GetLastError());
2221                return -1;
2222        }
2223        return 0;
2224}
2225
2226pid_t waitpid(pid_t pid, int *status, int options)
2227{
2228        HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
2229            FALSE, pid);
2230        if (!h) {
2231                errno = ECHILD;
2232                return -1;
2233        }
2234
2235        if (pid > 0 && options & WNOHANG) {
2236                if (WAIT_OBJECT_0 != WaitForSingleObject(h, 0)) {
2237                        CloseHandle(h);
2238                        return 0;
2239                }
2240                options &= ~WNOHANG;
2241        }
2242
2243        if (options == 0) {
2244                struct pinfo_t **ppinfo;
2245                if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
2246                        CloseHandle(h);
2247                        return 0;
2248                }
2249
2250                if (status)
2251                        GetExitCodeProcess(h, (LPDWORD)status);
2252
2253                EnterCriticalSection(&pinfo_cs);
2254
2255                ppinfo = &pinfo;
2256                while (*ppinfo) {
2257                        struct pinfo_t *info = *ppinfo;
2258                        if (info->pid == pid) {
2259                                CloseHandle(info->proc);
2260                                *ppinfo = info->next;
2261                                free(info);
2262                                break;
2263                        }
2264                        ppinfo = &info->next;
2265                }
2266
2267                LeaveCriticalSection(&pinfo_cs);
2268
2269                CloseHandle(h);
2270                return pid;
2271        }
2272        CloseHandle(h);
2273
2274        errno = EINVAL;
2275        return -1;
2276}
2277
2278int mingw_skip_dos_drive_prefix(char **path)
2279{
2280        int ret = has_dos_drive_prefix(*path);
2281        *path += ret;
2282        return ret;
2283}
2284
2285int mingw_offset_1st_component(const char *path)
2286{
2287        char *pos = (char *)path;
2288
2289        /* unc paths */
2290        if (!skip_dos_drive_prefix(&pos) &&
2291                        is_dir_sep(pos[0]) && is_dir_sep(pos[1])) {
2292                /* skip server name */
2293                pos = strpbrk(pos + 2, "\\/");
2294                if (!pos)
2295                        return 0; /* Error: malformed unc path */
2296
2297                do {
2298                        pos++;
2299                } while (*pos && !is_dir_sep(*pos));
2300        }
2301
2302        return pos + is_dir_sep(*pos) - path;
2303}
2304
2305int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
2306{
2307        int upos = 0, wpos = 0;
2308        const unsigned char *utf = (const unsigned char*) utfs;
2309        if (!utf || !wcs || wcslen < 1) {
2310                errno = EINVAL;
2311                return -1;
2312        }
2313        /* reserve space for \0 */
2314        wcslen--;
2315        if (utflen < 0)
2316                utflen = INT_MAX;
2317
2318        while (upos < utflen) {
2319                int c = utf[upos++] & 0xff;
2320                if (utflen == INT_MAX && c == 0)
2321                        break;
2322
2323                if (wpos >= wcslen) {
2324                        wcs[wpos] = 0;
2325                        errno = ERANGE;
2326                        return -1;
2327                }
2328
2329                if (c < 0x80) {
2330                        /* ASCII */
2331                        wcs[wpos++] = c;
2332                } else if (c >= 0xc2 && c < 0xe0 && upos < utflen &&
2333                                (utf[upos] & 0xc0) == 0x80) {
2334                        /* 2-byte utf-8 */
2335                        c = ((c & 0x1f) << 6);
2336                        c |= (utf[upos++] & 0x3f);
2337                        wcs[wpos++] = c;
2338                } else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen &&
2339                                !(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */
2340                                (utf[upos] & 0xc0) == 0x80 &&
2341                                (utf[upos + 1] & 0xc0) == 0x80) {
2342                        /* 3-byte utf-8 */
2343                        c = ((c & 0x0f) << 12);
2344                        c |= ((utf[upos++] & 0x3f) << 6);
2345                        c |= (utf[upos++] & 0x3f);
2346                        wcs[wpos++] = c;
2347                } else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen &&
2348                                wpos + 1 < wcslen &&
2349                                !(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */
2350                                !(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */
2351                                (utf[upos] & 0xc0) == 0x80 &&
2352                                (utf[upos + 1] & 0xc0) == 0x80 &&
2353                                (utf[upos + 2] & 0xc0) == 0x80) {
2354                        /* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */
2355                        c = ((c & 0x07) << 18);
2356                        c |= ((utf[upos++] & 0x3f) << 12);
2357                        c |= ((utf[upos++] & 0x3f) << 6);
2358                        c |= (utf[upos++] & 0x3f);
2359                        c -= 0x10000;
2360                        wcs[wpos++] = 0xd800 | (c >> 10);
2361                        wcs[wpos++] = 0xdc00 | (c & 0x3ff);
2362                } else if (c >= 0xa0) {
2363                        /* invalid utf-8 byte, printable unicode char: convert 1:1 */
2364                        wcs[wpos++] = c;
2365                } else {
2366                        /* invalid utf-8 byte, non-printable unicode: convert to hex */
2367                        static const char *hex = "0123456789abcdef";
2368                        wcs[wpos++] = hex[c >> 4];
2369                        if (wpos < wcslen)
2370                                wcs[wpos++] = hex[c & 0x0f];
2371                }
2372        }
2373        wcs[wpos] = 0;
2374        return wpos;
2375}
2376
2377int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
2378{
2379        if (!wcs || !utf || utflen < 1) {
2380                errno = EINVAL;
2381                return -1;
2382        }
2383        utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL);
2384        if (utflen)
2385                return utflen - 1;
2386        errno = ERANGE;
2387        return -1;
2388}
2389
2390static void setup_windows_environment(void)
2391{
2392        char *tmp = getenv("TMPDIR");
2393
2394        /* on Windows it is TMP and TEMP */
2395        if (!tmp) {
2396                if (!(tmp = getenv("TMP")))
2397                        tmp = getenv("TEMP");
2398                if (tmp) {
2399                        setenv("TMPDIR", tmp, 1);
2400                        tmp = getenv("TMPDIR");
2401                }
2402        }
2403
2404        if (tmp) {
2405                /*
2406                 * Convert all dir separators to forward slashes,
2407                 * to help shell commands called from the Git
2408                 * executable (by not mistaking the dir separators
2409                 * for escape characters).
2410                 */
2411                convert_slashes(tmp);
2412        }
2413
2414        /* simulate TERM to enable auto-color (see color.c) */
2415        if (!getenv("TERM"))
2416                setenv("TERM", "cygwin", 1);
2417}
2418
2419/*
2420 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
2421 * mingw startup code, see init.c in mingw runtime).
2422 */
2423int _CRT_glob = 0;
2424
2425typedef struct {
2426        int newmode;
2427} _startupinfo;
2428
2429extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
2430                _startupinfo *si);
2431
2432static NORETURN void die_startup(void)
2433{
2434        fputs("fatal: not enough memory for initialization", stderr);
2435        exit(128);
2436}
2437
2438static void *malloc_startup(size_t size)
2439{
2440        void *result = malloc(size);
2441        if (!result)
2442                die_startup();
2443        return result;
2444}
2445
2446static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
2447{
2448        len = xwcstoutf(buffer, wcs, len) + 1;
2449        return memcpy(malloc_startup(len), buffer, len);
2450}
2451
2452static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
2453                                      DWORD desired_access, DWORD flags)
2454{
2455        DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
2456        wchar_t buf[MAX_PATH];
2457        DWORD max = ARRAY_SIZE(buf);
2458        HANDLE handle;
2459        DWORD ret = GetEnvironmentVariableW(key, buf, max);
2460
2461        if (!ret || ret >= max)
2462                return;
2463
2464        /* make sure this does not leak into child processes */
2465        SetEnvironmentVariableW(key, NULL);
2466        if (!wcscmp(buf, L"off")) {
2467                close(fd);
2468                handle = GetStdHandle(std_id);
2469                if (handle != INVALID_HANDLE_VALUE)
2470                        CloseHandle(handle);
2471                return;
2472        }
2473        if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
2474                handle = GetStdHandle(STD_OUTPUT_HANDLE);
2475                if (handle == INVALID_HANDLE_VALUE) {
2476                        close(fd);
2477                        handle = GetStdHandle(std_id);
2478                        if (handle != INVALID_HANDLE_VALUE)
2479                                CloseHandle(handle);
2480                } else {
2481                        int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2482                        SetStdHandle(std_id, handle);
2483                        dup2(new_fd, fd);
2484                        /* do *not* close the new_fd: that would close stdout */
2485                }
2486                return;
2487        }
2488        handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
2489                             flags, NULL);
2490        if (handle != INVALID_HANDLE_VALUE) {
2491                int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
2492                SetStdHandle(std_id, handle);
2493                dup2(new_fd, fd);
2494                close(new_fd);
2495        }
2496}
2497
2498static void maybe_redirect_std_handles(void)
2499{
2500        maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
2501                                  GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
2502        maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
2503                                  GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
2504        maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
2505                                  GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
2506}
2507
2508void mingw_startup(void)
2509{
2510        int i, maxlen, argc;
2511        char *buffer;
2512        wchar_t **wenv, **wargv;
2513        _startupinfo si;
2514
2515        maybe_redirect_std_handles();
2516
2517        /* get wide char arguments and environment */
2518        si.newmode = 0;
2519        if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
2520                die_startup();
2521
2522        /* determine size of argv and environ conversion buffer */
2523        maxlen = wcslen(wargv[0]);
2524        for (i = 1; i < argc; i++)
2525                maxlen = max(maxlen, wcslen(wargv[i]));
2526
2527        /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
2528        maxlen = 3 * maxlen + 1;
2529        buffer = malloc_startup(maxlen);
2530
2531        /* convert command line arguments and environment to UTF-8 */
2532        for (i = 0; i < argc; i++)
2533                __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
2534        free(buffer);
2535
2536        /* fix Windows specific environment settings */
2537        setup_windows_environment();
2538
2539        unset_environment_variables = xstrdup("PERL5LIB");
2540
2541        /* initialize critical section for waitpid pinfo_t list */
2542        InitializeCriticalSection(&pinfo_cs);
2543
2544        /* set up default file mode and file modes for stdin/out/err */
2545        _fmode = _O_BINARY;
2546        _setmode(_fileno(stdin), _O_BINARY);
2547        _setmode(_fileno(stdout), _O_BINARY);
2548        _setmode(_fileno(stderr), _O_BINARY);
2549
2550        /* initialize Unicode console */
2551        winansi_init();
2552}
2553
2554int uname(struct utsname *buf)
2555{
2556        unsigned v = (unsigned)GetVersion();
2557        memset(buf, 0, sizeof(*buf));
2558        xsnprintf(buf->sysname, sizeof(buf->sysname), "Windows");
2559        xsnprintf(buf->release, sizeof(buf->release),
2560                 "%u.%u", v & 0xff, (v >> 8) & 0xff);
2561        /* assuming NT variants only.. */
2562        xsnprintf(buf->version, sizeof(buf->version),
2563                  "%u", (v >> 16) & 0x7fff);
2564        return 0;
2565}