diff options
Diffstat (limited to 'compat')
-rw-r--r-- | compat/apple-common-crypto.h | 86 | ||||
-rw-r--r-- | compat/bswap.h | 131 | ||||
-rw-r--r-- | compat/fnmatch/fnmatch.c | 494 | ||||
-rw-r--r-- | compat/fnmatch/fnmatch.h | 84 | ||||
-rw-r--r-- | compat/gmtime.c | 29 | ||||
-rw-r--r-- | compat/mingw.c | 717 | ||||
-rw-r--r-- | compat/mingw.h | 197 | ||||
-rw-r--r-- | compat/mmap.c | 4 | ||||
-rw-r--r-- | compat/msvc.h | 15 | ||||
-rw-r--r-- | compat/nedmalloc/malloc.c.h | 2 | ||||
-rw-r--r-- | compat/poll/poll.c | 4 | ||||
-rw-r--r-- | compat/precompose_utf8.c | 7 | ||||
-rw-r--r-- | compat/regex/regcomp.c | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | compat/vcbuild/scripts/clink.pl | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | compat/vcbuild/scripts/lib.pl | 0 | ||||
-rw-r--r-- | compat/win32/alloca.h (renamed from compat/vcbuild/include/alloca.h) | 0 | ||||
-rw-r--r-- | compat/win32/dirent.c | 116 | ||||
-rw-r--r-- | compat/win32/dirent.h | 8 | ||||
-rw-r--r-- | compat/winansi.c | 450 |
19 files changed, 1313 insertions, 1035 deletions
diff --git a/compat/apple-common-crypto.h b/compat/apple-common-crypto.h new file mode 100644 index 0000000000..c8b9b0e1a6 --- /dev/null +++ b/compat/apple-common-crypto.h @@ -0,0 +1,86 @@ +/* suppress inclusion of conflicting openssl functions */ +#define OPENSSL_NO_MD5 +#define HEADER_HMAC_H +#define HEADER_SHA_H +#include <CommonCrypto/CommonHMAC.h> +#define HMAC_CTX CCHmacContext +#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len) +#define HMAC_Update CCHmacUpdate +#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash) +#define HMAC_CTX_cleanup(ignore) +#define EVP_md5(...) kCCHmacAlgMD5 +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define APPLE_LION_OR_NEWER +#include <Security/Security.h> +/* Apple's TYPE_BOOL conflicts with config.c */ +#undef TYPE_BOOL +#endif + +#ifdef APPLE_LION_OR_NEWER +#define git_CC_error_check(pattern, err) \ + do { \ + if (err) { \ + die(pattern, (long)CFErrorGetCode(err)); \ + } \ + } while(0) + +#define EVP_EncodeBlock git_CC_EVP_EncodeBlock +static inline int git_CC_EVP_EncodeBlock(unsigned char *out, + const unsigned char *in, int inlen) +{ + CFErrorRef err; + SecTransformRef encoder; + CFDataRef input, output; + CFIndex length; + + encoder = SecEncodeTransformCreate(kSecBase64Encoding, &err); + git_CC_error_check("SecEncodeTransformCreate failed: %ld", err); + + input = CFDataCreate(kCFAllocatorDefault, in, inlen); + SecTransformSetAttribute(encoder, kSecTransformInputAttributeName, + input, &err); + git_CC_error_check("SecTransformSetAttribute failed: %ld", err); + + output = SecTransformExecute(encoder, &err); + git_CC_error_check("SecTransformExecute failed: %ld", err); + + length = CFDataGetLength(output); + CFDataGetBytes(output, CFRangeMake(0, length), out); + + CFRelease(output); + CFRelease(input); + CFRelease(encoder); + + return (int)strlen((const char *)out); +} + +#define EVP_DecodeBlock git_CC_EVP_DecodeBlock +static int inline git_CC_EVP_DecodeBlock(unsigned char *out, + const unsigned char *in, int inlen) +{ + CFErrorRef err; + SecTransformRef decoder; + CFDataRef input, output; + CFIndex length; + + decoder = SecDecodeTransformCreate(kSecBase64Encoding, &err); + git_CC_error_check("SecEncodeTransformCreate failed: %ld", err); + + input = CFDataCreate(kCFAllocatorDefault, in, inlen); + SecTransformSetAttribute(decoder, kSecTransformInputAttributeName, + input, &err); + git_CC_error_check("SecTransformSetAttribute failed: %ld", err); + + output = SecTransformExecute(decoder, &err); + git_CC_error_check("SecTransformExecute failed: %ld", err); + + length = CFDataGetLength(output); + CFDataGetBytes(output, CFRangeMake(0, length), out); + + CFRelease(output); + CFRelease(input); + CFRelease(decoder); + + return (int)strlen((const char *)out); +} +#endif /* APPLE_LION_OR_NEWER */ diff --git a/compat/bswap.h b/compat/bswap.h index 5061214f73..7fed637ed0 100644 --- a/compat/bswap.h +++ b/compat/bswap.h @@ -17,7 +17,20 @@ static inline uint32_t default_swab32(uint32_t val) ((val & 0x000000ff) << 24)); } +static inline uint64_t default_bswap64(uint64_t val) +{ + return (((val & (uint64_t)0x00000000000000ffULL) << 56) | + ((val & (uint64_t)0x000000000000ff00ULL) << 40) | + ((val & (uint64_t)0x0000000000ff0000ULL) << 24) | + ((val & (uint64_t)0x00000000ff000000ULL) << 8) | + ((val & (uint64_t)0x000000ff00000000ULL) >> 8) | + ((val & (uint64_t)0x0000ff0000000000ULL) >> 24) | + ((val & (uint64_t)0x00ff000000000000ULL) >> 40) | + ((val & (uint64_t)0xff00000000000000ULL) >> 56)); +} + #undef bswap32 +#undef bswap64 #if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) @@ -32,15 +45,42 @@ static inline uint32_t git_bswap32(uint32_t x) return result; } +#define bswap64 git_bswap64 +#if defined(__x86_64__) +static inline uint64_t git_bswap64(uint64_t x) +{ + uint64_t result; + if (__builtin_constant_p(x)) + result = default_bswap64(x); + else + __asm__("bswap %q0" : "=r" (result) : "0" (x)); + return result; +} +#else +static inline uint64_t git_bswap64(uint64_t x) +{ + union { uint64_t i64; uint32_t i32[2]; } tmp, result; + if (__builtin_constant_p(x)) + result.i64 = default_bswap64(x); + else { + tmp.i64 = x; + result.i32[0] = git_bswap32(tmp.i32[1]); + result.i32[1] = git_bswap32(tmp.i32[0]); + } + return result.i64; +} +#endif + #elif defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64)) #include <stdlib.h> #define bswap32(x) _byteswap_ulong(x) +#define bswap64(x) _byteswap_uint64(x) #endif -#ifdef bswap32 +#if defined(bswap32) #undef ntohl #undef htonl @@ -48,3 +88,92 @@ static inline uint32_t git_bswap32(uint32_t x) #define htonl(x) bswap32(x) #endif + +#if defined(bswap64) + +#undef ntohll +#undef htonll +#define ntohll(x) bswap64(x) +#define htonll(x) bswap64(x) + +#else + +#undef ntohll +#undef htonll + +#if defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN) + +# define GIT_BYTE_ORDER __BYTE_ORDER +# define GIT_LITTLE_ENDIAN __LITTLE_ENDIAN +# define GIT_BIG_ENDIAN __BIG_ENDIAN + +#elif defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN) + +# define GIT_BYTE_ORDER BYTE_ORDER +# define GIT_LITTLE_ENDIAN LITTLE_ENDIAN +# define GIT_BIG_ENDIAN BIG_ENDIAN + +#else + +# define GIT_BIG_ENDIAN 4321 +# define GIT_LITTLE_ENDIAN 1234 + +# if defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define GIT_BYTE_ORDER GIT_BIG_ENDIAN +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN +# elif defined(__THW_BIG_ENDIAN__) && !defined(__THW_LITTLE_ENDIAN__) +# define GIT_BYTE_ORDER GIT_BIG_ENDIAN +# elif defined(__THW_LITTLE_ENDIAN__) && !defined(__THW_BIG_ENDIAN__) +# define GIT_BYTE_ORDER GIT_LITTLE_ENDIAN +# else +# error "Cannot determine endianness" +# endif + +#endif + +#if GIT_BYTE_ORDER == GIT_BIG_ENDIAN +# define ntohll(n) (n) +# define htonll(n) (n) +#else +# define ntohll(n) default_bswap64(n) +# define htonll(n) default_bswap64(n) +#endif + +#endif + +/* + * Performance might be improved if the CPU architecture is OK with + * unaligned 32-bit loads and a fast ntohl() is available. + * Otherwise fall back to byte loads and shifts which is portable, + * and is faster on architectures with memory alignment issues. + */ + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(_M_IX86) || defined(_M_X64) || \ + defined(__ppc__) || defined(__ppc64__) || \ + defined(__powerpc__) || defined(__powerpc64__) || \ + defined(__s390__) || defined(__s390x__) + +#define get_be16(p) ntohs(*(unsigned short *)(p)) +#define get_be32(p) ntohl(*(unsigned int *)(p)) +#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0) + +#else + +#define get_be16(p) ( \ + (*((unsigned char *)(p) + 0) << 8) | \ + (*((unsigned char *)(p) + 1) << 0) ) +#define get_be32(p) ( \ + (*((unsigned char *)(p) + 0) << 24) | \ + (*((unsigned char *)(p) + 1) << 16) | \ + (*((unsigned char *)(p) + 2) << 8) | \ + (*((unsigned char *)(p) + 3) << 0) ) +#define put_be32(p, v) do { \ + unsigned int __v = (v); \ + *((unsigned char *)(p) + 0) = __v >> 24; \ + *((unsigned char *)(p) + 1) = __v >> 16; \ + *((unsigned char *)(p) + 2) = __v >> 8; \ + *((unsigned char *)(p) + 3) = __v >> 0; } while (0) + +#endif diff --git a/compat/fnmatch/fnmatch.c b/compat/fnmatch/fnmatch.c deleted file mode 100644 index 378c467401..0000000000 --- a/compat/fnmatch/fnmatch.c +++ /dev/null @@ -1,494 +0,0 @@ -/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#if HAVE_CONFIG_H -# include <config.h> -#endif - -/* Enable GNU extensions in fnmatch.h. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#include <stddef.h> -#include <errno.h> -#include <fnmatch.h> -#include <ctype.h> - -#if HAVE_STRING_H || defined _LIBC -# include <string.h> -#else -# include <strings.h> -#endif - -#if defined STDC_HEADERS || defined _LIBC -# include <stdlib.h> -#endif - -/* For platforms which support the ISO C amendment 1 functionality we - support user defined character classes. */ -#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) -/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */ -# include <wchar.h> -# include <wctype.h> -#endif - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#if defined NO_FNMATCH || defined NO_FNMATCH_CASEFOLD || \ - defined _LIBC || !defined __GNU_LIBRARY__ - - -# if defined STDC_HEADERS || !defined isascii -# define ISASCII(c) 1 -# else -# define ISASCII(c) isascii(c) -# endif - -# ifdef isblank -# define ISBLANK(c) (ISASCII (c) && isblank (c)) -# else -# define ISBLANK(c) ((c) == ' ' || (c) == '\t') -# endif -# ifdef isgraph -# define ISGRAPH(c) (ISASCII (c) && isgraph (c)) -# else -# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) -# endif - -# define ISPRINT(c) (ISASCII (c) && isprint (c)) -# define ISDIGIT(c) (ISASCII (c) && isdigit (c)) -# define ISALNUM(c) (ISASCII (c) && isalnum (c)) -# define ISALPHA(c) (ISASCII (c) && isalpha (c)) -# define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) -# define ISLOWER(c) (ISASCII (c) && islower (c)) -# define ISPUNCT(c) (ISASCII (c) && ispunct (c)) -# define ISSPACE(c) (ISASCII (c) && isspace (c)) -# define ISUPPER(c) (ISASCII (c) && isupper (c)) -# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) - -# define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) - -# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) -/* The GNU C library provides support for user-defined character classes - and the functions from ISO C amendment 1. */ -# ifdef CHARCLASS_NAME_MAX -# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX -# else -/* This shouldn't happen but some implementation might still have this - problem. Use a reasonable default value. */ -# define CHAR_CLASS_MAX_LENGTH 256 -# endif - -# ifdef _LIBC -# define IS_CHAR_CLASS(string) __wctype (string) -# else -# define IS_CHAR_CLASS(string) wctype (string) -# endif -# else -# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ - -# define IS_CHAR_CLASS(string) \ - (STREQ (string, "alpha") || STREQ (string, "upper") \ - || STREQ (string, "lower") || STREQ (string, "digit") \ - || STREQ (string, "alnum") || STREQ (string, "xdigit") \ - || STREQ (string, "space") || STREQ (string, "print") \ - || STREQ (string, "punct") || STREQ (string, "graph") \ - || STREQ (string, "cntrl") || STREQ (string, "blank")) -# endif - -/* Avoid depending on library functions or files - whose names are inconsistent. */ - -# if !defined _LIBC && !defined getenv -extern char *getenv (const char *name); -# endif - -# ifndef errno -extern int errno; -# endif - -# ifndef NULL -# define NULL 0 -# endif - -/* This function doesn't exist on most systems. */ - -# if !defined HAVE___STRCHRNUL && !defined _LIBC -static char * -__strchrnul (const char *s, int c) - - -{ - char *result = strchr (s, c); - if (result == NULL) - result = strchr (s, '\0'); - return result; -} -# endif - -# ifndef internal_function -/* Inside GNU libc we mark some function in a special way. In other - environments simply ignore the marking. */ -# define internal_function -# endif - -/* Match STRING against the filename pattern PATTERN, returning zero if - it matches, nonzero if not. */ -static int internal_fnmatch __P ((const char *pattern, const char *string, - int no_leading_period, int flags)) - internal_function; -static int -internal_function -internal_fnmatch (const char *pattern, const char *string, int no_leading_period, int flags) - - - - -{ - register const char *p = pattern, *n = string; - register unsigned char c; - -/* Note that this evaluates C many times. */ -# ifdef _LIBC -# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c)) -# else -# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c)) -# endif - - while ((c = *p++) != '\0') - { - c = FOLD (c); - - switch (c) - { - case '?': - if (*n == '\0') - return FNM_NOMATCH; - else if (*n == '/' && (flags & FNM_FILE_NAME)) - return FNM_NOMATCH; - else if (*n == '.' && no_leading_period - && (n == string - || (n[-1] == '/' && (flags & FNM_FILE_NAME)))) - return FNM_NOMATCH; - break; - - case '\\': - if (!(flags & FNM_NOESCAPE)) - { - c = *p++; - if (c == '\0') - /* Trailing \ loses. */ - return FNM_NOMATCH; - c = FOLD (c); - } - if (FOLD ((unsigned char) *n) != c) - return FNM_NOMATCH; - break; - - case '*': - if (*n == '.' && no_leading_period - && (n == string - || (n[-1] == '/' && (flags & FNM_FILE_NAME)))) - return FNM_NOMATCH; - - for (c = *p++; c == '?' || c == '*'; c = *p++) - { - if (*n == '/' && (flags & FNM_FILE_NAME)) - /* A slash does not match a wildcard under FNM_FILE_NAME. */ - return FNM_NOMATCH; - else if (c == '?') - { - /* A ? needs to match one character. */ - if (*n == '\0') - /* There isn't another character; no match. */ - return FNM_NOMATCH; - else - /* One character of the string is consumed in matching - this ? wildcard, so *??? won't match if there are - less than three characters. */ - ++n; - } - } - - if (c == '\0') - /* The wildcard(s) is/are the last element of the pattern. - If the name is a file name and contains another slash - this does mean it cannot match. */ - return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL - ? FNM_NOMATCH : 0); - else - { - const char *endp; - - endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0'); - - if (c == '[') - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - - for (--p; n < endp; ++n) - if (internal_fnmatch (p, n, - (no_leading_period - && (n == string - || (n[-1] == '/' - && (flags - & FNM_FILE_NAME)))), - flags2) - == 0) - return 0; - } - else if (c == '/' && (flags & FNM_FILE_NAME)) - { - while (*n != '\0' && *n != '/') - ++n; - if (*n == '/' - && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD, - flags) == 0)) - return 0; - } - else - { - int flags2 = ((flags & FNM_FILE_NAME) - ? flags : (flags & ~FNM_PERIOD)); - - if (c == '\\' && !(flags & FNM_NOESCAPE)) - c = *p; - c = FOLD (c); - for (--p; n < endp; ++n) - if (FOLD ((unsigned char) *n) == c - && (internal_fnmatch (p, n, - (no_leading_period - && (n == string - || (n[-1] == '/' - && (flags - & FNM_FILE_NAME)))), - flags2) == 0)) - return 0; - } - } - - /* If we come here no match is possible with the wildcard. */ - return FNM_NOMATCH; - - case '[': - { - /* Nonzero if the sense of the character class is inverted. */ - static int posixly_correct; - register int not; - char cold; - - if (posixly_correct == 0) - posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; - - if (*n == '\0') - return FNM_NOMATCH; - - if (*n == '.' && no_leading_period && (n == string - || (n[-1] == '/' - && (flags - & FNM_FILE_NAME)))) - return FNM_NOMATCH; - - if (*n == '/' && (flags & FNM_FILE_NAME)) - /* `/' cannot be matched. */ - return FNM_NOMATCH; - - not = (*p == '!' || (posixly_correct < 0 && *p == '^')); - if (not) - ++p; - - c = *p++; - for (;;) - { - unsigned char fn = FOLD ((unsigned char) *n); - - if (!(flags & FNM_NOESCAPE) && c == '\\') - { - if (*p == '\0') - return FNM_NOMATCH; - c = FOLD ((unsigned char) *p); - ++p; - - if (c == fn) - goto matched; - } - else if (c == '[' && *p == ':') - { - /* Leave room for the null. */ - char str[CHAR_CLASS_MAX_LENGTH + 1]; - size_t c1 = 0; -# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) - wctype_t wt; -# endif - const char *startp = p; - - for (;;) - { - if (c1 > CHAR_CLASS_MAX_LENGTH) - /* The name is too long and therefore the pattern - is ill-formed. */ - return FNM_NOMATCH; - - c = *++p; - if (c == ':' && p[1] == ']') - { - p += 2; - break; - } - if (c < 'a' || c >= 'z') - { - /* This cannot possibly be a character class name. - Match it as a normal range. */ - p = startp; - c = '['; - goto normal_bracket; - } - str[c1++] = c; - } - str[c1] = '\0'; - -# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) - wt = IS_CHAR_CLASS (str); - if (wt == 0) - /* Invalid character class name. */ - return FNM_NOMATCH; - - if (__iswctype (__btowc ((unsigned char) *n), wt)) - goto matched; -# else - if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n)) - || (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n)) - || (STREQ (str, "blank") && ISBLANK ((unsigned char) *n)) - || (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n)) - || (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n)) - || (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n)) - || (STREQ (str, "lower") && ISLOWER ((unsigned char) *n)) - || (STREQ (str, "print") && ISPRINT ((unsigned char) *n)) - || (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n)) - || (STREQ (str, "space") && ISSPACE ((unsigned char) *n)) - || (STREQ (str, "upper") && ISUPPER ((unsigned char) *n)) - || (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n))) - goto matched; -# endif - } - else if (c == '\0') - /* [ (unterminated) loses. */ - return FNM_NOMATCH; - else - { - normal_bracket: - if (FOLD (c) == fn) - goto matched; - - cold = c; - c = *p++; - - if (c == '-' && *p != ']') - { - /* It is a range. */ - unsigned char cend = *p++; - if (!(flags & FNM_NOESCAPE) && cend == '\\') - cend = *p++; - if (cend == '\0') - return FNM_NOMATCH; - - if (cold <= fn && fn <= FOLD (cend)) - goto matched; - - c = *p++; - } - } - - if (c == ']') - break; - } - - if (!not) - return FNM_NOMATCH; - break; - - matched: - /* Skip the rest of the [...] that already matched. */ - while (c != ']') - { - if (c == '\0') - /* [... (unterminated) loses. */ - return FNM_NOMATCH; - - c = *p++; - if (!(flags & FNM_NOESCAPE) && c == '\\') - { - if (*p == '\0') - return FNM_NOMATCH; - /* XXX 1003.2d11 is unclear if this is right. */ - ++p; - } - else if (c == '[' && *p == ':') - { - do - if (*++p == '\0') - return FNM_NOMATCH; - while (*p != ':' || p[1] == ']'); - p += 2; - c = *p; - } - } - if (not) - return FNM_NOMATCH; - } - break; - - default: - if (c != FOLD ((unsigned char) *n)) - return FNM_NOMATCH; - } - - ++n; - } - - if (*n == '\0') - return 0; - - if ((flags & FNM_LEADING_DIR) && *n == '/') - /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ - return 0; - - return FNM_NOMATCH; - -# undef FOLD -} - - -int -fnmatch (const char *pattern, const char *string, int flags) - - - -{ - return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags); -} - -#endif /* _LIBC or not __GNU_LIBRARY__. */ diff --git a/compat/fnmatch/fnmatch.h b/compat/fnmatch/fnmatch.h deleted file mode 100644 index cc3ec37940..0000000000 --- a/compat/fnmatch/fnmatch.h +++ /dev/null @@ -1,84 +0,0 @@ -/* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _FNMATCH_H -#define _FNMATCH_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32 -# if !defined __GLIBC__ || !defined __P -# undef __P -# define __P(protos) protos -# endif -#else /* Not C++ or ANSI C. */ -# undef __P -# define __P(protos) () -/* We can get away without defining `const' here only because in this file - it is used only inside the prototype for `fnmatch', which is elided in - non-ANSI C where `const' is problematical. */ -#endif /* C++ or ANSI C. */ - -#ifndef const -# if (defined __STDC__ && __STDC__) || defined __cplusplus -# define __const const -# else -# define __const -# endif -#endif - -/* We #undef these before defining them because some losing systems - (HP-UX A.08.07 for example) define these in <unistd.h>. */ -#undef FNM_PATHNAME -#undef FNM_NOESCAPE -#undef FNM_PERIOD - -/* Bits set in the FLAGS argument to `fnmatch'. */ -#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ -#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ -#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ - -#if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE -# define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ -# define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ -# define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ -#endif - -/* Value returned by `fnmatch' if STRING does not match PATTERN. */ -#define FNM_NOMATCH 1 - -/* This value is returned if the implementation does not support - `fnmatch'. Since this is not the case here it will never be - returned but the conformance test suites still require the symbol - to be defined. */ -#ifdef _XOPEN_SOURCE -# define FNM_NOSYS (-1) -#endif - -/* Match NAME against the filename pattern PATTERN, - returning zero if it matches, FNM_NOMATCH if not. */ -extern int fnmatch __P ((__const char *__pattern, __const char *__name, - int __flags)); - -#ifdef __cplusplus -} -#endif - -#endif /* fnmatch.h */ diff --git a/compat/gmtime.c b/compat/gmtime.c new file mode 100644 index 0000000000..e8362dd2b9 --- /dev/null +++ b/compat/gmtime.c @@ -0,0 +1,29 @@ +#include "../git-compat-util.h" +#undef gmtime +#undef gmtime_r + +struct tm *git_gmtime(const time_t *timep) +{ + static struct tm result; + return git_gmtime_r(timep, &result); +} + +struct tm *git_gmtime_r(const time_t *timep, struct tm *result) +{ + struct tm *ret; + + memset(result, 0, sizeof(*result)); + ret = gmtime_r(timep, result); + + /* + * Rather than NULL, FreeBSD gmtime simply leaves the "struct tm" + * untouched when it encounters overflow. Since "mday" cannot otherwise + * be zero, we can test this very quickly. + */ + if (ret && !ret->tm_mday) { + ret = NULL; + errno = EOVERFLOW; + } + + return ret; +} diff --git a/compat/mingw.c b/compat/mingw.c index bb92c436f7..70f3191a4f 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -1,8 +1,10 @@ #include "../git-compat-util.h" #include "win32.h" #include <conio.h> +#include <wchar.h> #include "../strbuf.h" #include "../run-command.h" +#include "../cache.h" static const int delay[] = { 0, 1, 10, 20, 40 }; @@ -198,14 +200,16 @@ static int ask_yes_no_if_possible(const char *format, ...) } } -#undef unlink int mingw_unlink(const char *pathname) { int ret, tries = 0; + wchar_t wpathname[MAX_PATH]; + if (xutftowcs_path(wpathname, pathname) < 0) + return -1; /* read-only files cannot be removed */ - chmod(pathname, 0666); - while ((ret = unlink(pathname)) == -1 && tries < ARRAY_SIZE(delay)) { + _wchmod(wpathname, 0666); + while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { if (!is_file_in_use_error(GetLastError())) break; /* @@ -221,45 +225,45 @@ int mingw_unlink(const char *pathname) while (ret == -1 && is_file_in_use_error(GetLastError()) && ask_yes_no_if_possible("Unlink of file '%s' failed. " "Should I try again?", pathname)) - ret = unlink(pathname); + ret = _wunlink(wpathname); return ret; } -static int is_dir_empty(const char *path) +static int is_dir_empty(const wchar_t *wpath) { - struct strbuf buf = STRBUF_INIT; - WIN32_FIND_DATAA findbuf; + WIN32_FIND_DATAW findbuf; HANDLE handle; - - strbuf_addf(&buf, "%s\\*", path); - handle = FindFirstFileA(buf.buf, &findbuf); - if (handle == INVALID_HANDLE_VALUE) { - strbuf_release(&buf); + wchar_t wbuf[MAX_PATH + 2]; + wcscpy(wbuf, wpath); + wcscat(wbuf, L"\\*"); + handle = FindFirstFileW(wbuf, &findbuf); + if (handle == INVALID_HANDLE_VALUE) return GetLastError() == ERROR_NO_MORE_FILES; - } - while (!strcmp(findbuf.cFileName, ".") || - !strcmp(findbuf.cFileName, "..")) - if (!FindNextFile(handle, &findbuf)) { - strbuf_release(&buf); - return GetLastError() == ERROR_NO_MORE_FILES; + while (!wcscmp(findbuf.cFileName, L".") || + !wcscmp(findbuf.cFileName, L"..")) + if (!FindNextFileW(handle, &findbuf)) { + DWORD err = GetLastError(); + FindClose(handle); + return err == ERROR_NO_MORE_FILES; } FindClose(handle); - strbuf_release(&buf); return 0; } -#undef rmdir int mingw_rmdir(const char *pathname) { int ret, tries = 0; + wchar_t wpathname[MAX_PATH]; + if (xutftowcs_path(wpathname, pathname) < 0) + return -1; - while ((ret = rmdir(pathname)) == -1 && tries < ARRAY_SIZE(delay)) { + while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) { if (!is_file_in_use_error(GetLastError())) errno = err_win_to_posix(GetLastError()); if (errno != EACCES) break; - if (!is_dir_empty(pathname)) { + if (!is_dir_empty(wpathname)) { errno = ENOTEMPTY; break; } @@ -276,16 +280,26 @@ int mingw_rmdir(const char *pathname) while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) && ask_yes_no_if_possible("Deletion of directory '%s' failed. " "Should I try again?", pathname)) - ret = rmdir(pathname); + ret = _wrmdir(wpathname); + return ret; +} + +int mingw_mkdir(const char *path, int mode) +{ + int ret; + wchar_t wpath[MAX_PATH]; + if (xutftowcs_path(wpath, path) < 0) + return -1; + ret = _wmkdir(wpath); return ret; } -#undef open int mingw_open (const char *filename, int oflags, ...) { va_list args; unsigned mode; int fd; + wchar_t wfilename[MAX_PATH]; va_start(args, oflags); mode = va_arg(args, int); @@ -294,33 +308,18 @@ int mingw_open (const char *filename, int oflags, ...) if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - fd = open(filename, oflags, mode); + if (xutftowcs_path(wfilename, filename) < 0) + return -1; + fd = _wopen(wfilename, oflags, mode); - if (fd < 0 && (oflags & O_CREAT) && errno == EACCES) { - DWORD attrs = GetFileAttributes(filename); + if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) { + DWORD attrs = GetFileAttributesW(wfilename); if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) errno = EISDIR; } return fd; } -#undef write -ssize_t mingw_write(int fd, const void *buf, size_t count) -{ - /* - * While write() calls to a file on a local disk are translated - * into WriteFile() calls with a maximum size of 64KB on Windows - * XP and 256KB on Vista, no such cap is placed on writes to - * files over the network on Windows XP. Unfortunately, there - * seems to be a limit of 32MB-28KB on X64 and 64MB-32KB on x86; - * bigger writes fail on Windows XP. - * So we cap to a nice 31MB here to avoid write failures over - * the net without changing the number of WriteFile() calls in - * the local case. - */ - return write(fd, buf, min(count, 31 * 1024 * 1024)); -} - static BOOL WINAPI ctrl_ignore(DWORD type) { return TRUE; @@ -349,17 +348,28 @@ int mingw_fgetc(FILE *stream) #undef fopen FILE *mingw_fopen (const char *filename, const char *otype) { + FILE *file; + wchar_t wfilename[MAX_PATH], wotype[4]; if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - return fopen(filename, otype); + if (xutftowcs_path(wfilename, filename) < 0 || + xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0) + return NULL; + file = _wfopen(wfilename, wotype); + return file; } -#undef freopen FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream) { + FILE *file; + wchar_t wfilename[MAX_PATH], wotype[4]; if (filename && !strcmp(filename, "/dev/null")) filename = "nul"; - return freopen(filename, otype, stream); + if (xutftowcs_path(wfilename, filename) < 0 || + xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0) + return NULL; + file = _wfreopen(wfilename, wotype, stream); + return file; } #undef fflush @@ -384,6 +394,31 @@ int mingw_fflush(FILE *stream) return ret; } +int mingw_access(const char *filename, int mode) +{ + wchar_t wfilename[MAX_PATH]; + if (xutftowcs_path(wfilename, filename) < 0) + return -1; + /* X_OK is not supported by the MSVCRT version */ + return _waccess(wfilename, mode & ~X_OK); +} + +int mingw_chdir(const char *dirname) +{ + wchar_t wdirname[MAX_PATH]; + if (xutftowcs_path(wdirname, dirname) < 0) + return -1; + return _wchdir(wdirname); +} + +int mingw_chmod(const char *filename, int mode) +{ + wchar_t wfilename[MAX_PATH]; + if (xutftowcs_path(wfilename, filename) < 0) + return -1; + return _wchmod(wfilename, mode); +} + /* * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC. * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch. @@ -409,10 +444,12 @@ static inline time_t filetime_to_time_t(const FILETIME *ft) */ static int do_lstat(int follow, const char *file_name, struct stat *buf) { - int err; WIN32_FILE_ATTRIBUTE_DATA fdata; + wchar_t wfilename[MAX_PATH]; + if (xutftowcs_path(wfilename, file_name) < 0) + return -1; - if (!(err = get_file_attr(file_name, &fdata))) { + if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) { buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; @@ -425,8 +462,8 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf) buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - WIN32_FIND_DATAA findbuf; - HANDLE handle = FindFirstFileA(file_name, &findbuf); + WIN32_FIND_DATAW findbuf; + HANDLE handle = FindFirstFileW(wfilename, &findbuf); if (handle != INVALID_HANDLE_VALUE) { if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { @@ -445,7 +482,23 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf) } return 0; } - errno = err; + switch (GetLastError()) { + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + case ERROR_SHARING_BUFFER_EXCEEDED: + errno = EACCES; + break; + case ERROR_BUFFER_OVERFLOW: + errno = ENAMETOOLONG; + break; + case ERROR_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + default: + errno = ENOENT; + break; + } return -1; } @@ -458,7 +511,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf) static int do_stat_internal(int follow, const char *file_name, struct stat *buf) { int namelen; - static char alt_name[PATH_MAX]; + char alt_name[PATH_MAX]; if (!do_lstat(follow, file_name, buf)) return 0; @@ -491,7 +544,6 @@ int mingw_stat(const char *file_name, struct stat *buf) return do_stat_internal(1, file_name, buf); } -#undef fstat int mingw_fstat(int fd, struct stat *buf) { HANDLE fh = (HANDLE)_get_osfhandle(fd); @@ -534,16 +586,20 @@ int mingw_utime (const char *file_name, const struct utimbuf *times) { FILETIME mft, aft; int fh, rc; + DWORD attrs; + wchar_t wfilename[MAX_PATH]; + if (xutftowcs_path(wfilename, file_name) < 0) + return -1; /* must have write permission */ - DWORD attrs = GetFileAttributes(file_name); + attrs = GetFileAttributesW(wfilename); if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) { /* ignore errors here; open() will report them */ - SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY); + SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY); } - if ((fh = open(file_name, O_RDWR | O_BINARY)) < 0) { + if ((fh = _wopen(wfilename, O_RDWR | O_BINARY)) < 0) { rc = -1; goto revert_attrs; } @@ -566,7 +622,7 @@ revert_attrs: if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) { /* ignore errors again */ - SetFileAttributes(file_name, attrs); + SetFileAttributesW(wfilename, attrs); } return rc; } @@ -577,6 +633,18 @@ unsigned int sleep (unsigned int seconds) return 0; } +char *mingw_mktemp(char *template) +{ + wchar_t wtemplate[MAX_PATH]; + if (xutftowcs_path(wtemplate, template) < 0) + return NULL; + if (!_wmktemp(wtemplate)) + return NULL; + if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0) + return NULL; + return template; +} + int mkstemp(char *template) { char *filename = mktemp(template); @@ -635,17 +703,18 @@ struct tm *localtime_r(const time_t *timep, struct tm *result) return result; } -#undef getcwd char *mingw_getcwd(char *pointer, int len) { int i; - char *ret = getcwd(pointer, len); - if (!ret) - return ret; + wchar_t wpointer[MAX_PATH]; + if (!_wgetcwd(wpointer, ARRAY_SIZE(wpointer))) + return NULL; + if (xwcstoutf(pointer, wpointer, len) < 0) + return NULL; for (i = 0; pointer[i]; i++) if (pointer[i] == '\\') pointer[i] = '/'; - return ret; + return pointer; } /* @@ -830,11 +899,44 @@ static char *path_lookup(const char *cmd, char **path, int exe_only) return prog; } -static int env_compare(const void *a, const void *b) +static int do_putenv(char **env, const char *name, int size, int free_old); + +/* used number of elements of environ array, including terminating NULL */ +static int environ_size = 0; +/* allocated size of environ array, in bytes */ +static int environ_alloc = 0; + +/* + * Create environment block suitable for CreateProcess. Merges current + * process environment and the supplied environment changes. + */ +static wchar_t *make_environment_block(char **deltaenv) { - char *const *ea = a; - char *const *eb = b; - return strcasecmp(*ea, *eb); + wchar_t *wenvblk = NULL; + char **tmpenv; + int i = 0, size = environ_size, wenvsz = 0, wenvpos = 0; + + while (deltaenv && deltaenv[i]) + i++; + + /* copy the environment, leaving space for changes */ + tmpenv = xmalloc((size + i) * sizeof(char*)); + memcpy(tmpenv, environ, size * sizeof(char*)); + + /* merge supplied environment changes into the temporary environment */ + for (i = 0; deltaenv && deltaenv[i]; i++) + size = do_putenv(tmpenv, deltaenv[i], size, 0); + + /* create environment block from temporary environment */ + for (i = 0; tmpenv[i]; i++) { + size = 2 * strlen(tmpenv[i]) + 2; /* +2 for final \0 */ + ALLOC_GROW(wenvblk, (wenvpos + size) * sizeof(wchar_t), wenvsz); + wenvpos += xutftowcs(&wenvblk[wenvpos], tmpenv[i], size) + 1; + } + /* add final \0 terminator */ + wenvblk[wenvpos] = 0; + free(tmpenv); + return wenvblk; } struct pinfo_t { @@ -845,14 +947,15 @@ struct pinfo_t { static struct pinfo_t *pinfo = NULL; CRITICAL_SECTION pinfo_cs; -static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, +static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv, const char *dir, int prepend_cmd, int fhin, int fhout, int fherr) { - STARTUPINFO si; + STARTUPINFOW si; PROCESS_INFORMATION pi; - struct strbuf envblk, args; - unsigned flags; + struct strbuf args; + wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL; + unsigned flags = CREATE_UNICODE_ENVIRONMENT; BOOL ret; /* Determine whether or not we are associated to a console */ @@ -869,7 +972,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, * instead of CREATE_NO_WINDOW to make ssh * recognize that it has no console. */ - flags = DETACHED_PROCESS; + flags |= DETACHED_PROCESS; } else { /* There is already a console. If we specified * DETACHED_PROCESS here, too, Windows would @@ -877,15 +980,19 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, * The same is true for CREATE_NO_WINDOW. * Go figure! */ - flags = 0; CloseHandle(cons); } memset(&si, 0, sizeof(si)); si.cb = sizeof(si); si.dwFlags = STARTF_USESTDHANDLES; - si.hStdInput = (HANDLE) _get_osfhandle(fhin); - si.hStdOutput = (HANDLE) _get_osfhandle(fhout); - si.hStdError = (HANDLE) _get_osfhandle(fherr); + si.hStdInput = winansi_get_osfhandle(fhin); + si.hStdOutput = winansi_get_osfhandle(fhout); + si.hStdError = winansi_get_osfhandle(fherr); + + if (xutftowcs_path(wcmd, cmd) < 0) + return -1; + if (dir && xutftowcs_path(wdir, dir) < 0) + return -1; /* concatenate argv, quoting args as we go */ strbuf_init(&args, 0); @@ -904,33 +1011,18 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, free(quoted); } - if (env) { - int count = 0; - char **e, **sorted_env; - - for (e = env; *e; e++) - count++; - - /* environment must be sorted */ - sorted_env = xmalloc(sizeof(*sorted_env) * (count + 1)); - memcpy(sorted_env, env, sizeof(*sorted_env) * (count + 1)); - qsort(sorted_env, count, sizeof(*sorted_env), env_compare); + wargs = xmalloc((2 * args.len + 1) * sizeof(wchar_t)); + xutftowcs(wargs, args.buf, 2 * args.len + 1); + strbuf_release(&args); - strbuf_init(&envblk, 0); - for (e = sorted_env; *e; e++) { - strbuf_addstr(&envblk, *e); - strbuf_addch(&envblk, '\0'); - } - free(sorted_env); - } + wenvblk = make_environment_block(deltaenv); memset(&pi, 0, sizeof(pi)); - ret = CreateProcess(cmd, args.buf, NULL, NULL, TRUE, flags, - env ? envblk.buf : NULL, dir, &si, &pi); + ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags, + wenvblk, dir ? wdir : NULL, &si, &pi); - if (env) - strbuf_release(&envblk); - strbuf_release(&args); + free(wenvblk); + free(wargs); if (!ret) { errno = ENOENT; @@ -959,13 +1051,12 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env, return (pid_t)pi.dwProcessId; } -static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env, - int prepend_cmd) +static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd) { - return mingw_spawnve_fd(cmd, argv, env, NULL, prepend_cmd, 0, 1, 2); + return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2); } -pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env, +pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv, const char *dir, int fhin, int fhout, int fherr) { @@ -989,14 +1080,14 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env, pid = -1; } else { - pid = mingw_spawnve_fd(iprog, argv, env, dir, 1, + pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1, fhin, fhout, fherr); free(iprog); } argv[0] = argv0; } else - pid = mingw_spawnve_fd(prog, argv, env, dir, 0, + pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0, fhin, fhout, fherr); free(prog); } @@ -1004,7 +1095,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env, return pid; } -static int try_shell_exec(const char *cmd, char *const *argv, char **env) +static int try_shell_exec(const char *cmd, char *const *argv) { const char *interpr = parse_interpreter(cmd); char **path; @@ -1022,7 +1113,7 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env) argv2 = xmalloc(sizeof(*argv) * (argc+1)); argv2[0] = (char *)cmd; /* full path to the script file */ memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc); - pid = mingw_spawnve(prog, argv2, env, 1); + pid = mingw_spawnv(prog, argv2, 1); if (pid >= 0) { int status; if (waitpid(pid, &status, 0) < 0) @@ -1037,19 +1128,20 @@ static int try_shell_exec(const char *cmd, char *const *argv, char **env) return pid; } -static void mingw_execve(const char *cmd, char *const *argv, char *const *env) +int mingw_execv(const char *cmd, char *const *argv) { /* check if git_command is a shell script */ - if (!try_shell_exec(cmd, argv, (char **)env)) { + if (!try_shell_exec(cmd, argv)) { int pid, status; - pid = mingw_spawnve(cmd, (const char **)argv, (char **)env, 0); + pid = mingw_spawnv(cmd, (const char **)argv, 0); if (pid < 0) - return; + return -1; if (waitpid(pid, &status, 0) < 0) status = 255; exit(status); } + return -1; } int mingw_execvp(const char *cmd, char *const *argv) @@ -1058,7 +1150,7 @@ int mingw_execvp(const char *cmd, char *const *argv) char *prog = path_lookup(cmd, path, 0); if (prog) { - mingw_execve(prog, argv, environ); + mingw_execv(prog, argv); free(prog); } else errno = ENOENT; @@ -1067,12 +1159,6 @@ int mingw_execvp(const char *cmd, char *const *argv) return -1; } -int mingw_execv(const char *cmd, char *const *argv) -{ - mingw_execve(cmd, argv, environ); - return -1; -} - int mingw_kill(pid_t pid, int sig) { if (pid > 0 && sig == SIGTERM) { @@ -1086,114 +1172,100 @@ int mingw_kill(pid_t pid, int sig) errno = err_win_to_posix(GetLastError()); CloseHandle(h); return -1; + } else if (pid > 0 && sig == 0) { + HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (h) { + CloseHandle(h); + return 0; + } } errno = EINVAL; return -1; } -static char **copy_environ(void) -{ - char **env; - int i = 0; - while (environ[i]) - i++; - env = xmalloc((i+1)*sizeof(*env)); - for (i = 0; environ[i]; i++) - env[i] = xstrdup(environ[i]); - env[i] = NULL; - return env; -} - -void free_environ(char **env) -{ - int i; - for (i = 0; env[i]; i++) - free(env[i]); - free(env); +/* + * Compare environment entries by key (i.e. stopping at '=' or '\0'). + */ +static int compareenv(const void *v1, const void *v2) +{ + const char *e1 = *(const char**)v1; + const char *e2 = *(const char**)v2; + + for (;;) { + int c1 = *e1++; + int c2 = *e2++; + c1 = (c1 == '=') ? 0 : tolower(c1); + c2 = (c2 == '=') ? 0 : tolower(c2); + if (c1 > c2) + return 1; + if (c1 < c2) + return -1; + if (c1 == 0) + return 0; + } } -static int lookup_env(char **env, const char *name, size_t nmln) +static int bsearchenv(char **env, const char *name, size_t size) { - int i; - - for (i = 0; env[i]; i++) { - if (0 == strncmp(env[i], name, nmln) - && '=' == env[i][nmln]) - /* matches */ - return i; + unsigned low = 0, high = size; + while (low < high) { + unsigned mid = low + ((high - low) >> 1); + int cmp = compareenv(&env[mid], &name); + if (cmp < 0) + low = mid + 1; + else if (cmp > 0) + high = mid; + else + return mid; } - return -1; + return ~low; /* not found, return 1's complement of insert position */ } /* * If name contains '=', then sets the variable, otherwise it unsets it + * Size includes the terminating NULL. Env must have room for size + 1 entries + * (in case of insert). Returns the new size. Optionally frees removed entries. */ -static char **env_setenv(char **env, const char *name) +static int do_putenv(char **env, const char *name, int size, int free_old) { - char *eq = strchrnul(name, '='); - int i = lookup_env(env, name, eq-name); + int i = bsearchenv(env, name, size - 1); - if (i < 0) { - if (*eq) { - for (i = 0; env[i]; i++) - ; - env = xrealloc(env, (i+2)*sizeof(*env)); - env[i] = xstrdup(name); - env[i+1] = NULL; - } - } - else { + /* optionally free removed / replaced entry */ + if (i >= 0 && free_old) free(env[i]); - if (*eq) - env[i] = xstrdup(name); - else - for (; env[i]; i++) - env[i] = env[i+1]; - } - return env; -} - -/* - * Copies global environ and adjusts variables as specified by vars. - */ -char **make_augmented_environ(const char *const *vars) -{ - char **env = copy_environ(); - while (*vars) - env = env_setenv(env, *vars++); - return env; + if (strchr(name, '=')) { + /* if new value ('key=value') is specified, insert or replace entry */ + if (i < 0) { + i = ~i; + memmove(&env[i + 1], &env[i], (size - i) * sizeof(char*)); + size++; + } + env[i] = (char*) name; + } else if (i >= 0) { + /* otherwise ('key') remove existing entry */ + size--; + memmove(&env[i], &env[i + 1], (size - i) * sizeof(char*)); + } + return size; } -#undef getenv - -/* - * The system's getenv looks up the name in a case-insensitive manner. - * This version tries a case-sensitive lookup and falls back to - * case-insensitive if nothing was found. This is necessary because, - * as a prominent example, CMD sets 'Path', but not 'PATH'. - * Warning: not thread-safe. - */ -static char *getenv_cs(const char *name) +char *mingw_getenv(const char *name) { - size_t len = strlen(name); - int i = lookup_env(environ, name, len); - if (i >= 0) - return environ[i] + len + 1; /* skip past name and '=' */ - return getenv(name); + char *value; + int pos = bsearchenv(environ, name, environ_size - 1); + if (pos < 0) + return NULL; + value = strchr(environ[pos], '='); + return value ? &value[1] : NULL; } -char *mingw_getenv(const char *name) +int mingw_putenv(const char *namevalue) { - char *result = getenv_cs(name); - if (!result && !strcmp(name, "TMPDIR")) { - /* on Windows it is TMP and TEMP */ - result = getenv_cs("TMP"); - if (!result) - result = getenv_cs("TEMP"); - } - return result; + ALLOC_GROW(environ, (environ_size + 1) * sizeof(char*), environ_alloc); + environ_size = do_putenv(environ, namevalue, environ_size, 1); + return 0; } /* @@ -1238,8 +1310,7 @@ static int WSAAPI getaddrinfo_stub(const char *node, const char *service, else ai->ai_canonname = NULL; - sin = xmalloc(ai->ai_addrlen); - memset(sin, 0, ai->ai_addrlen); + sin = xcalloc(1, ai->ai_addrlen); sin->sin_family = AF_INET; /* Note: getaddrinfo is supposed to allow service to be a string, * which should be looked up using getservbyname. This is @@ -1493,33 +1564,36 @@ int mingw_rename(const char *pold, const char *pnew) { DWORD attrs, gle; int tries = 0; + wchar_t wpold[MAX_PATH], wpnew[MAX_PATH]; + if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0) + return -1; /* * Try native rename() first to get errno right. * It is based on MoveFile(), which cannot overwrite existing files. */ - if (!rename(pold, pnew)) + if (!_wrename(wpold, wpnew)) return 0; if (errno != EEXIST) return -1; repeat: - if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) + if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING)) return 0; /* TODO: translate more errors */ gle = GetLastError(); if (gle == ERROR_ACCESS_DENIED && - (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { + (attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) { if (attrs & FILE_ATTRIBUTE_DIRECTORY) { errno = EISDIR; return -1; } if ((attrs & FILE_ATTRIBUTE_READONLY) && - SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { - if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) + SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { + if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING)) return 0; gle = GetLastError(); /* revert file attributes on failure */ - SetFileAttributes(pnew, attrs); + SetFileAttributesW(wpnew, attrs); } } if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) { @@ -1765,11 +1839,16 @@ void mingw_open_html(const char *unixpath) int link(const char *oldpath, const char *newpath) { - typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES); + typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); static T create_hard_link = NULL; + wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH]; + if (xutftowcs_path(woldpath, oldpath) < 0 || + xutftowcs_path(wnewpath, newpath) < 0) + return -1; + if (!create_hard_link) { create_hard_link = (T) GetProcAddress( - GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); + GetModuleHandle("kernel32.dll"), "CreateHardLinkW"); if (!create_hard_link) create_hard_link = (T)-1; } @@ -1777,7 +1856,7 @@ int link(const char *oldpath, const char *newpath) errno = ENOSYS; return -1; } - if (!create_hard_link(newpath, oldpath, NULL)) { + if (!create_hard_link(wnewpath, woldpath, NULL)) { errno = err_win_to_posix(GetLastError()); return -1; } @@ -1835,3 +1914,217 @@ pid_t waitpid(pid_t pid, int *status, int options) errno = EINVAL; return -1; } + +int mingw_offset_1st_component(const char *path) +{ + int offset = 0; + if (has_dos_drive_prefix(path)) + offset = 2; + + /* unc paths */ + else if (is_dir_sep(path[0]) && is_dir_sep(path[1])) { + + /* skip server name */ + char *pos = strpbrk(path + 2, "\\/"); + if (!pos) + return 0; /* Error: malformed unc path */ + + do { + pos++; + } while (*pos && !is_dir_sep(*pos)); + + offset = pos - path; + } + + return offset + is_dir_sep(path[offset]); +} + +int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen) +{ + int upos = 0, wpos = 0; + const unsigned char *utf = (const unsigned char*) utfs; + if (!utf || !wcs || wcslen < 1) { + errno = EINVAL; + return -1; + } + /* reserve space for \0 */ + wcslen--; + if (utflen < 0) + utflen = INT_MAX; + + while (upos < utflen) { + int c = utf[upos++] & 0xff; + if (utflen == INT_MAX && c == 0) + break; + + if (wpos >= wcslen) { + wcs[wpos] = 0; + errno = ERANGE; + return -1; + } + + if (c < 0x80) { + /* ASCII */ + wcs[wpos++] = c; + } else if (c >= 0xc2 && c < 0xe0 && upos < utflen && + (utf[upos] & 0xc0) == 0x80) { + /* 2-byte utf-8 */ + c = ((c & 0x1f) << 6); + c |= (utf[upos++] & 0x3f); + wcs[wpos++] = c; + } else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen && + !(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */ + (utf[upos] & 0xc0) == 0x80 && + (utf[upos + 1] & 0xc0) == 0x80) { + /* 3-byte utf-8 */ + c = ((c & 0x0f) << 12); + c |= ((utf[upos++] & 0x3f) << 6); + c |= (utf[upos++] & 0x3f); + wcs[wpos++] = c; + } else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen && + wpos + 1 < wcslen && + !(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */ + !(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */ + (utf[upos] & 0xc0) == 0x80 && + (utf[upos + 1] & 0xc0) == 0x80 && + (utf[upos + 2] & 0xc0) == 0x80) { + /* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */ + c = ((c & 0x07) << 18); + c |= ((utf[upos++] & 0x3f) << 12); + c |= ((utf[upos++] & 0x3f) << 6); + c |= (utf[upos++] & 0x3f); + c -= 0x10000; + wcs[wpos++] = 0xd800 | (c >> 10); + wcs[wpos++] = 0xdc00 | (c & 0x3ff); + } else if (c >= 0xa0) { + /* invalid utf-8 byte, printable unicode char: convert 1:1 */ + wcs[wpos++] = c; + } else { + /* invalid utf-8 byte, non-printable unicode: convert to hex */ + static const char *hex = "0123456789abcdef"; + wcs[wpos++] = hex[c >> 4]; + if (wpos < wcslen) + wcs[wpos++] = hex[c & 0x0f]; + } + } + wcs[wpos] = 0; + return wpos; +} + +int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen) +{ + if (!wcs || !utf || utflen < 1) { + errno = EINVAL; + return -1; + } + utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL); + if (utflen) + return utflen - 1; + errno = ERANGE; + return -1; +} + +/* + * Disable MSVCRT command line wildcard expansion (__getmainargs called from + * mingw startup code, see init.c in mingw runtime). + */ +int _CRT_glob = 0; + +typedef struct { + int newmode; +} _startupinfo; + +extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob, + _startupinfo *si); + +static NORETURN void die_startup() +{ + fputs("fatal: not enough memory for initialization", stderr); + exit(128); +} + +static void *malloc_startup(size_t size) +{ + void *result = malloc(size); + if (!result) + die_startup(); + return result; +} + +static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len) +{ + len = xwcstoutf(buffer, wcs, len) + 1; + return memcpy(malloc_startup(len), buffer, len); +} + +void mingw_startup() +{ + int i, maxlen, argc; + char *buffer; + wchar_t **wenv, **wargv; + _startupinfo si; + + /* get wide char arguments and environment */ + si.newmode = 0; + if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0) + die_startup(); + + /* determine size of argv and environ conversion buffer */ + maxlen = wcslen(_wpgmptr); + for (i = 1; i < argc; i++) + maxlen = max(maxlen, wcslen(wargv[i])); + for (i = 0; wenv[i]; i++) + maxlen = max(maxlen, wcslen(wenv[i])); + + /* + * nedmalloc can't free CRT memory, allocate resizable environment + * list. Note that xmalloc / xmemdupz etc. call getenv, so we cannot + * use it while initializing the environment itself. + */ + environ_size = i + 1; + environ_alloc = alloc_nr(environ_size * sizeof(char*)); + environ = malloc_startup(environ_alloc); + + /* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */ + maxlen = 3 * maxlen + 1; + buffer = malloc_startup(maxlen); + + /* convert command line arguments and environment to UTF-8 */ + __argv[0] = wcstoutfdup_startup(buffer, _wpgmptr, maxlen); + for (i = 1; i < argc; i++) + __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen); + for (i = 0; wenv[i]; i++) + environ[i] = wcstoutfdup_startup(buffer, wenv[i], maxlen); + environ[i] = NULL; + free(buffer); + + /* sort environment for O(log n) getenv / putenv */ + qsort(environ, i, sizeof(char*), compareenv); + + /* fix Windows specific environment settings */ + + /* on Windows it is TMP and TEMP */ + if (!mingw_getenv("TMPDIR")) { + const char *tmp = mingw_getenv("TMP"); + if (!tmp) + tmp = mingw_getenv("TEMP"); + if (tmp) + setenv("TMPDIR", tmp, 1); + } + + /* simulate TERM to enable auto-color (see color.c) */ + if (!getenv("TERM")) + setenv("TERM", "cygwin", 1); + + /* initialize critical section for waitpid pinfo_t list */ + InitializeCriticalSection(&pinfo_cs); + + /* set up default file mode and file modes for stdin/out/err */ + _fmode = _O_BINARY; + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); + + /* initialize Unicode console */ + winansi_init(); +} diff --git a/compat/mingw.h b/compat/mingw.h index bd0a88bc1d..5e499cfb71 100644 --- a/compat/mingw.h +++ b/compat/mingw.h @@ -32,7 +32,12 @@ typedef int socklen_t; #define WEXITSTATUS(x) ((x) & 0xff) #define WTERMSIG(x) SIGTERM +#ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN +#endif +#ifndef ELOOP +#define ELOOP EMLINK +#endif #define SHUT_WR SD_SEND #define SIGHUP 1 @@ -46,8 +51,12 @@ typedef int socklen_t; #define F_SETFD 2 #define FD_CLOEXEC 0x1 +#ifndef EAFNOSUPPORT #define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif +#ifndef ECONNABORTED #define ECONNABORTED WSAECONNABORTED +#endif struct passwd { char *pw_name; @@ -60,7 +69,6 @@ struct sigaction { sig_handler_t sa_handler; unsigned sa_flags; }; -#define sigemptyset(x) (void)0 #define SA_RESTART 0 struct itimerval { @@ -107,15 +115,18 @@ static inline int fcntl(int fd, int cmd, ...) } /* bash cannot reliably detect negative return codes as failure */ #define exit(code) exit((code) & 0xff) +#define sigemptyset(x) (void)0 +static inline int sigaddset(sigset_t *set, int signum) +{ return 0; } +#define SIG_UNBLOCK 0 +static inline int sigprocmask(int how, const sigset_t *set, sigset_t *oldset) +{ return 0; } /* * simple adaptors */ -static inline int mingw_mkdir(const char *path, int mode) -{ - return mkdir(path); -} +int mingw_mkdir(const char *path, int mode); #define mkdir mingw_mkdir #define WNOHANG 1 @@ -174,9 +185,6 @@ int mingw_rmdir(const char *path); int mingw_open (const char *filename, int oflags, ...); #define open mingw_open -ssize_t mingw_write(int fd, const void *buf, size_t count); -#define write mingw_write - int mingw_fgetc(FILE *stream); #define fgetc mingw_fgetc @@ -189,11 +197,27 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream); int mingw_fflush(FILE *stream); #define fflush mingw_fflush +int mingw_access(const char *filename, int mode); +#undef access +#define access mingw_access + +int mingw_chdir(const char *dirname); +#define chdir mingw_chdir + +int mingw_chmod(const char *filename, int mode); +#define chmod mingw_chmod + +char *mingw_mktemp(char *template); +#define mktemp mingw_mktemp + char *mingw_getcwd(char *pointer, int len); #define getcwd mingw_getcwd char *mingw_getenv(const char *name); #define getenv mingw_getenv +int mingw_putenv(const char *namevalue); +#define putenv mingw_putenv +#define unsetenv mingw_putenv int mingw_gethostname(char *host, int namelen); #define gethostname mingw_gethostname @@ -258,19 +282,35 @@ static inline int getrlimit(int resource, struct rlimit *rlp) return 0; } -/* Use mingw_lstat() instead of lstat()/stat() and - * mingw_fstat() instead of fstat() on Windows. +/* + * Use mingw specific stat()/lstat()/fstat() implementations on Windows. */ #define off_t off64_t #define lseek _lseeki64 -#ifndef ALREADY_DECLARED_STAT_FUNCS + +/* use struct stat with 64 bit st_size */ +#ifdef stat +#undef stat +#endif #define stat _stati64 int mingw_lstat(const char *file_name, struct stat *buf); int mingw_stat(const char *file_name, struct stat *buf); int mingw_fstat(int fd, struct stat *buf); +#ifdef fstat +#undef fstat +#endif #define fstat mingw_fstat +#ifdef lstat +#undef lstat +#endif #define lstat mingw_lstat -#define _stati64(x,y) mingw_stat(x,y) + +#ifndef _stati64 +# define _stati64(x,y) mingw_stat(x,y) +#elif defined (_USE_32BIT_TIME_T) +# define _stat32i64(x,y) mingw_stat(x,y) +#else +# define _stat64(x,y) mingw_stat(x,y) #endif int mingw_utime(const char *file_name, const struct utimbuf *times); @@ -298,12 +338,8 @@ int mingw_raise(int sig); * ANSI emulation wrappers */ -int winansi_fputs(const char *str, FILE *stream); -int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2))); -int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); -#define fputs winansi_fputs -#define printf(...) winansi_printf(__VA_ARGS__) -#define fprintf(...) winansi_fprintf(__VA_ARGS__) +void winansi_init(void); +HANDLE winansi_get_osfhandle(int fd); /* * git specific compatibility @@ -320,18 +356,121 @@ static inline char *mingw_find_last_dir_sep(const char *path) return ret; } #define find_last_dir_sep mingw_find_last_dir_sep +int mingw_offset_1st_component(const char *path); +#define offset_1st_component mingw_offset_1st_component #define PATH_SEP ';' #define PRIuMAX "I64u" +#define PRId64 "I64d" void mingw_open_html(const char *path); #define open_html mingw_open_html -/* - * helpers +void mingw_mark_as_git_dir(const char *dir); +#define mark_as_git_dir mingw_mark_as_git_dir + +/** + * Converts UTF-8 encoded string to UTF-16LE. + * + * To support repositories with legacy-encoded file names, invalid UTF-8 bytes + * 0xa0 - 0xff are converted to corresponding printable Unicode chars \u00a0 - + * \u00ff, and invalid UTF-8 bytes 0x80 - 0x9f (which would make non-printable + * Unicode) are converted to hex-code. + * + * Lead-bytes not followed by an appropriate number of trail-bytes, over-long + * encodings and 4-byte encodings > \u10ffff are detected as invalid UTF-8. + * + * Maximum space requirement for the target buffer is two wide chars per UTF-8 + * char (((strlen(utf) * 2) + 1) [* sizeof(wchar_t)]). + * + * The maximum space is needed only if the entire input string consists of + * invalid UTF-8 bytes in range 0x80-0x9f, as per the following table: + * + * | | UTF-8 | UTF-16 | + * Code point | UTF-8 sequence | bytes | words | ratio + * --------------+-------------------+-------+--------+------- + * 000000-00007f | 0-7f | 1 | 1 | 1 + * 000080-0007ff | c2-df + 80-bf | 2 | 1 | 0.5 + * 000800-00ffff | e0-ef + 2 * 80-bf | 3 | 1 | 0.33 + * 010000-10ffff | f0-f4 + 3 * 80-bf | 4 | 2 (a) | 0.5 + * invalid | 80-9f | 1 | 2 (b) | 2 + * invalid | a0-ff | 1 | 1 | 1 + * + * (a) encoded as UTF-16 surrogate pair + * (b) encoded as two hex digits + * + * Note that, while the UTF-8 encoding scheme can be extended to 5-byte, 6-byte + * or even indefinite-byte sequences, the largest valid code point \u10ffff + * encodes as only 4 UTF-8 bytes. + * + * Parameters: + * wcs: wide char target buffer + * utf: string to convert + * wcslen: size of target buffer (in wchar_t's) + * utflen: size of string to convert, or -1 if 0-terminated + * + * Returns: + * length of converted string (_wcslen(wcs)), or -1 on failure + * + * Errors: + * EINVAL: one of the input parameters is invalid (e.g. NULL) + * ERANGE: the output buffer is too small */ +int xutftowcsn(wchar_t *wcs, const char *utf, size_t wcslen, int utflen); -char **make_augmented_environ(const char *const *vars); -void free_environ(char **env); +/** + * Simplified variant of xutftowcsn, assumes input string is \0-terminated. + */ +static inline int xutftowcs(wchar_t *wcs, const char *utf, size_t wcslen) +{ + return xutftowcsn(wcs, utf, wcslen, -1); +} + +/** + * Simplified file system specific variant of xutftowcsn, assumes output + * buffer size is MAX_PATH wide chars and input string is \0-terminated, + * fails with ENAMETOOLONG if input string is too long. + */ +static inline int xutftowcs_path(wchar_t *wcs, const char *utf) +{ + int result = xutftowcsn(wcs, utf, MAX_PATH, -1); + if (result < 0 && errno == ERANGE) + errno = ENAMETOOLONG; + return result; +} + +/** + * Converts UTF-16LE encoded string to UTF-8. + * + * Maximum space requirement for the target buffer is three UTF-8 chars per + * wide char ((_wcslen(wcs) * 3) + 1). + * + * The maximum space is needed only if the entire input string consists of + * UTF-16 words in range 0x0800-0xd7ff or 0xe000-0xffff (i.e. \u0800-\uffff + * modulo surrogate pairs), as per the following table: + * + * | | UTF-16 | UTF-8 | + * Code point | UTF-16 sequence | words | bytes | ratio + * --------------+-----------------------+--------+-------+------- + * 000000-00007f | 0000-007f | 1 | 1 | 1 + * 000080-0007ff | 0080-07ff | 1 | 2 | 2 + * 000800-00ffff | 0800-d7ff / e000-ffff | 1 | 3 | 3 + * 010000-10ffff | d800-dbff + dc00-dfff | 2 | 4 | 2 + * + * Note that invalid code points > 10ffff cannot be represented in UTF-16. + * + * Parameters: + * utf: target buffer + * wcs: wide string to convert + * utflen: size of target buffer + * + * Returns: + * length of converted string, or -1 on failure + * + * Errors: + * EINVAL: one of the input parameters is invalid (e.g. NULL) + * ERANGE: the output buffer is too small + */ +int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen); /* * A critical section used in the implementation of the spawn @@ -341,22 +480,16 @@ void free_environ(char **env); extern CRITICAL_SECTION pinfo_cs; /* - * A replacement of main() that ensures that argv[0] has a path - * and that default fmode and std(in|out|err) are in binary mode + * A replacement of main() that adds win32 specific initialization. */ +void mingw_startup(); #define main(c,v) dummy_decl_mingw_main(); \ static int mingw_main(c,v); \ int main(int argc, char **argv) \ { \ - extern CRITICAL_SECTION pinfo_cs; \ - _fmode = _O_BINARY; \ - _setmode(_fileno(stdin), _O_BINARY); \ - _setmode(_fileno(stdout), _O_BINARY); \ - _setmode(_fileno(stderr), _O_BINARY); \ - argv[0] = xstrdup(_pgmptr); \ - InitializeCriticalSection(&pinfo_cs); \ - return mingw_main(argc, argv); \ + mingw_startup(); \ + return mingw_main(__argc, (void *)__argv); \ } \ static int mingw_main(c,v) diff --git a/compat/mmap.c b/compat/mmap.c index c9d46d1742..7f662fef7b 100644 --- a/compat/mmap.c +++ b/compat/mmap.c @@ -14,7 +14,7 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of } while (n < length) { - ssize_t count = pread(fd, (char *)start + n, length - n, offset + n); + ssize_t count = xpread(fd, (char *)start + n, length - n, offset + n); if (count == 0) { memset((char *)start+n, 0, length-n); @@ -22,8 +22,6 @@ void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t of } if (count < 0) { - if (errno == EAGAIN || errno == EINTR) - continue; free(start); errno = EACCES; return MAP_FAILED; diff --git a/compat/msvc.h b/compat/msvc.h index 96b6d605da..580bb55bf4 100644 --- a/compat/msvc.h +++ b/compat/msvc.h @@ -24,21 +24,6 @@ static __inline int strcasecmp (const char *s1, const char *s2) #undef ERROR -/* Use mingw_lstat() instead of lstat()/stat() and mingw_fstat() instead - * of fstat(). We add the declaration of these functions here, suppressing - * the corresponding declarations in mingw.h, so that we can use the - * appropriate structure type (and function) names from the msvc headers. - */ -#define stat _stat64 -int mingw_lstat(const char *file_name, struct stat *buf); -int mingw_fstat(int fd, struct stat *buf); -#define fstat mingw_fstat -#define lstat mingw_lstat -#define _stat64(x,y) mingw_lstat(x,y) -#define ALREADY_DECLARED_STAT_FUNCS - #include "compat/mingw.h" -#undef ALREADY_DECLARED_STAT_FUNCS - #endif diff --git a/compat/nedmalloc/malloc.c.h b/compat/nedmalloc/malloc.c.h index ed4f1fa5af..f216a2a7d3 100644 --- a/compat/nedmalloc/malloc.c.h +++ b/compat/nedmalloc/malloc.c.h @@ -499,7 +499,9 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP #endif /* WIN32 */ #ifdef WIN32 #define WIN32_LEAN_AND_MEAN +#ifndef _WIN32_WINNT #define _WIN32_WINNT 0x403 +#endif #include <windows.h> #define HAVE_MMAP 1 #define HAVE_MORECORE 0 diff --git a/compat/poll/poll.c b/compat/poll/poll.c index 44103103a4..a9b41d89f4 100644 --- a/compat/poll/poll.c +++ b/compat/poll/poll.c @@ -39,7 +39,7 @@ #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ # define WIN32_NATIVE -# if defined (_MSC_VER) +# if defined (_MSC_VER) && !defined(_WIN32_WINNT) # define _WIN32_WINNT 0x0502 # endif # include <winsock2.h> @@ -605,7 +605,7 @@ restart: if (!rc && timeout == INFTIM) { - SwitchToThread(); + SleepEx (1, TRUE); goto restart; } diff --git a/compat/precompose_utf8.c b/compat/precompose_utf8.c index 7980abd1a7..95fe849e42 100644 --- a/compat/precompose_utf8.c +++ b/compat/precompose_utf8.c @@ -48,11 +48,8 @@ void probe_utf8_pathname_composition(char *path, int len) if (output_fd >= 0) { close(output_fd); strcpy(path + len, auml_nfd); - /* Indicate to the user, that we can configure it to true */ - if (!access(path, R_OK)) - git_config_set("core.precomposeunicode", "false"); - /* To be backward compatible, set precomposed_unicode to 0 */ - precomposed_unicode = 0; + precomposed_unicode = access(path, R_OK) ? 0 : 1; + git_config_set("core.precomposeunicode", precomposed_unicode ? "true" : "false"); strcpy(path + len, auml_nfc); if (unlink(path)) die_errno(_("failed to unlink '%s'"), path); diff --git a/compat/regex/regcomp.c b/compat/regex/regcomp.c index b2c5d465ac..06f3088708 100644 --- a/compat/regex/regcomp.c +++ b/compat/regex/regcomp.c @@ -339,7 +339,7 @@ re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, p = buf; *p++ = dfa->nodes[node].opr.c; while (++node < dfa->nodes_len - && dfa->nodes[node].type == CHARACTER + && dfa->nodes[node].type == CHARACTER && dfa->nodes[node].mb_partial) *p++ = dfa->nodes[node].opr.c; memset (&state, '\0', sizeof (state)); diff --git a/compat/vcbuild/scripts/clink.pl b/compat/vcbuild/scripts/clink.pl index 4374771df2..a87d0da512 100644..100755 --- a/compat/vcbuild/scripts/clink.pl +++ b/compat/vcbuild/scripts/clink.pl @@ -33,6 +33,8 @@ while (@ARGV) { push(@args, "libeay32.lib"); } elsif ("$arg" eq "-lssl") { push(@args, "ssleay32.lib"); + } elsif ("$arg" eq "-lcurl") { + push(@args, "libcurl.lib"); } elsif ("$arg" =~ /^-L/ && "$arg" ne "-LTCG") { $arg =~ s/^-L/-LIBPATH:/; push(@args, $arg); diff --git a/compat/vcbuild/scripts/lib.pl b/compat/vcbuild/scripts/lib.pl index d8054e469f..d8054e469f 100644..100755 --- a/compat/vcbuild/scripts/lib.pl +++ b/compat/vcbuild/scripts/lib.pl diff --git a/compat/vcbuild/include/alloca.h b/compat/win32/alloca.h index c0d7985b7e..c0d7985b7e 100644 --- a/compat/vcbuild/include/alloca.h +++ b/compat/win32/alloca.h diff --git a/compat/win32/dirent.c b/compat/win32/dirent.c index 7a0debe51b..52420ec7d4 100644 --- a/compat/win32/dirent.c +++ b/compat/win32/dirent.c @@ -1,96 +1,81 @@ -#include "../git-compat-util.h" -#include "dirent.h" +#include "../../git-compat-util.h" struct DIR { struct dirent dd_dir; /* includes d_type */ HANDLE dd_handle; /* FindFirstFile handle */ int dd_stat; /* 0-based index */ - char dd_name[1]; /* extend struct */ }; +static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAW *fdata) +{ + /* convert UTF-16 name to UTF-8 */ + xwcstoutf(ent->d_name, fdata->cFileName, sizeof(ent->d_name)); + + /* Set file type, based on WIN32_FIND_DATA */ + if (fdata->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + ent->d_type = DT_DIR; + else + ent->d_type = DT_REG; +} + DIR *opendir(const char *name) { - DWORD attrs = GetFileAttributesA(name); + wchar_t pattern[MAX_PATH + 2]; /* + 2 for '/' '*' */ + WIN32_FIND_DATAW fdata; + HANDLE h; int len; - DIR *p; + DIR *dir; - /* check for valid path */ - if (attrs == INVALID_FILE_ATTRIBUTES) { - errno = ENOENT; + /* convert name to UTF-16 and check length < MAX_PATH */ + if ((len = xutftowcs_path(pattern, name)) < 0) return NULL; - } - /* check if it's a directory */ - if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) { - errno = ENOTDIR; + /* append optional '/' and wildcard '*' */ + if (len && !is_dir_sep(pattern[len - 1])) + pattern[len++] = '/'; + pattern[len++] = '*'; + pattern[len] = 0; + + /* open find handle */ + h = FindFirstFileW(pattern, &fdata); + if (h == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix(err); return NULL; } - /* check that the pattern won't be too long for FindFirstFileA */ - len = strlen(name); - if (is_dir_sep(name[len - 1])) - len--; - if (len + 2 >= MAX_PATH) { - errno = ENAMETOOLONG; - return NULL; - } - - p = malloc(sizeof(DIR) + len + 2); - if (!p) - return NULL; - - memset(p, 0, sizeof(DIR) + len + 2); - strcpy(p->dd_name, name); - p->dd_name[len] = '/'; - p->dd_name[len+1] = '*'; - - p->dd_handle = INVALID_HANDLE_VALUE; - return p; + /* initialize DIR structure and copy first dir entry */ + dir = xmalloc(sizeof(DIR)); + dir->dd_handle = h; + dir->dd_stat = 0; + finddata2dirent(&dir->dd_dir, &fdata); + return dir; } struct dirent *readdir(DIR *dir) { - WIN32_FIND_DATAA buf; - HANDLE handle; - - if (!dir || !dir->dd_handle) { + if (!dir) { errno = EBADF; /* No set_errno for mingw */ return NULL; } - if (dir->dd_handle == INVALID_HANDLE_VALUE && dir->dd_stat == 0) { - DWORD lasterr; - handle = FindFirstFileA(dir->dd_name, &buf); - lasterr = GetLastError(); - dir->dd_handle = handle; - if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES)) { - errno = err_win_to_posix(lasterr); + /* if first entry, dirent has already been set up by opendir */ + if (dir->dd_stat) { + /* get next entry and convert from WIN32_FIND_DATA to dirent */ + WIN32_FIND_DATAW fdata; + if (FindNextFileW(dir->dd_handle, &fdata)) { + finddata2dirent(&dir->dd_dir, &fdata); + } else { + DWORD lasterr = GetLastError(); + /* POSIX says you shouldn't set errno when readdir can't + find any more files; so, if another error we leave it set. */ + if (lasterr != ERROR_NO_MORE_FILES) + errno = err_win_to_posix(lasterr); return NULL; } - } else if (dir->dd_handle == INVALID_HANDLE_VALUE) { - return NULL; - } else if (!FindNextFileA(dir->dd_handle, &buf)) { - DWORD lasterr = GetLastError(); - FindClose(dir->dd_handle); - dir->dd_handle = INVALID_HANDLE_VALUE; - /* POSIX says you shouldn't set errno when readdir can't - find any more files; so, if another error we leave it set. */ - if (lasterr != ERROR_NO_MORE_FILES) - errno = err_win_to_posix(lasterr); - return NULL; } - /* We get here if `buf' contains valid data. */ - strcpy(dir->dd_dir.d_name, buf.cFileName); ++dir->dd_stat; - - /* Set file type, based on WIN32_FIND_DATA */ - dir->dd_dir.d_type = 0; - if (buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - dir->dd_dir.d_type |= DT_DIR; - else - dir->dd_dir.d_type |= DT_REG; - return &dir->dd_dir; } @@ -101,8 +86,7 @@ int closedir(DIR *dir) return -1; } - if (dir->dd_handle != INVALID_HANDLE_VALUE) - FindClose(dir->dd_handle); + FindClose(dir->dd_handle); free(dir); return 0; } diff --git a/compat/win32/dirent.h b/compat/win32/dirent.h index 927a25ca76..058207e4bf 100644 --- a/compat/win32/dirent.h +++ b/compat/win32/dirent.h @@ -9,12 +9,8 @@ typedef struct DIR DIR; #define DT_LNK 3 struct dirent { - long d_ino; /* Always zero. */ - char d_name[FILENAME_MAX]; /* File name. */ - union { - unsigned short d_reclen; /* Always zero. */ - unsigned char d_type; /* Reimplementation adds this */ - }; + unsigned char d_type; /* file type to prevent lstat after readdir */ + char d_name[MAX_PATH * 3]; /* file name (* 3 for UTF-8 conversion) */ }; DIR *opendir(const char *dirname); diff --git a/compat/winansi.c b/compat/winansi.c index dedce2104e..efc5bb3a4b 100644 --- a/compat/winansi.c +++ b/compat/winansi.c @@ -2,15 +2,10 @@ * Copyright 2008 Peter Harris <git@peter.is-a-geek.org> */ +#undef NOGDI #include "../git-compat-util.h" - -/* - Functions to be wrapped: -*/ -#undef printf -#undef fprintf -#undef fputs -/* TODO: write */ +#include <wingdi.h> +#include <winreg.h> /* ANSI codes used by git: m, K @@ -23,29 +18,114 @@ static HANDLE console; static WORD plain_attr; static WORD attr; static int negative; +static int non_ascii_used = 0; +static HANDLE hthread, hread, hwrite; +static HANDLE hconsole1, hconsole2; + +#ifdef __MINGW32__ +typedef struct _CONSOLE_FONT_INFOEX { + ULONG cbSize; + DWORD nFont; + COORD dwFontSize; + UINT FontFamily; + UINT FontWeight; + WCHAR FaceName[LF_FACESIZE]; +} CONSOLE_FONT_INFOEX, *PCONSOLE_FONT_INFOEX; +#endif + +typedef BOOL (WINAPI *PGETCURRENTCONSOLEFONTEX)(HANDLE, BOOL, + PCONSOLE_FONT_INFOEX); + +static void warn_if_raster_font(void) +{ + DWORD fontFamily = 0; + PGETCURRENTCONSOLEFONTEX pGetCurrentConsoleFontEx; + + /* don't bother if output was ascii only */ + if (!non_ascii_used) + return; + + /* GetCurrentConsoleFontEx is available since Vista */ + pGetCurrentConsoleFontEx = (PGETCURRENTCONSOLEFONTEX) GetProcAddress( + GetModuleHandle("kernel32.dll"), + "GetCurrentConsoleFontEx"); + if (pGetCurrentConsoleFontEx) { + CONSOLE_FONT_INFOEX cfi; + cfi.cbSize = sizeof(cfi); + if (pGetCurrentConsoleFontEx(console, 0, &cfi)) + fontFamily = cfi.FontFamily; + } else { + /* pre-Vista: check default console font in registry */ + HKEY hkey; + if (ERROR_SUCCESS == RegOpenKeyExA(HKEY_CURRENT_USER, "Console", + 0, KEY_READ, &hkey)) { + DWORD size = sizeof(fontFamily); + RegQueryValueExA(hkey, "FontFamily", NULL, NULL, + (LPVOID) &fontFamily, &size); + RegCloseKey(hkey); + } + } + + if (!(fontFamily & TMPF_TRUETYPE)) { + const wchar_t *msg = L"\nWarning: Your console font probably " + L"doesn\'t support Unicode. If you experience strange " + L"characters in the output, consider switching to a " + L"TrueType font such as Consolas!\n"; + DWORD dummy; + WriteConsoleW(console, msg, wcslen(msg), &dummy, NULL); + } +} -static void init(void) +static int is_console(int fd) { CONSOLE_SCREEN_BUFFER_INFO sbi; + HANDLE hcon; static int initialized = 0; - if (initialized) - return; - console = GetStdHandle(STD_OUTPUT_HANDLE); - if (console == INVALID_HANDLE_VALUE) - console = NULL; + /* get OS handle of the file descriptor */ + hcon = (HANDLE) _get_osfhandle(fd); + if (hcon == INVALID_HANDLE_VALUE) + return 0; - if (!console) - return; + /* check if its a device (i.e. console, printer, serial port) */ + if (GetFileType(hcon) != FILE_TYPE_CHAR) + return 0; - GetConsoleScreenBufferInfo(console, &sbi); - attr = plain_attr = sbi.wAttributes; - negative = 0; + /* check if its a handle to a console output screen buffer */ + if (!GetConsoleScreenBufferInfo(hcon, &sbi)) + return 0; + + /* initialize attributes */ + if (!initialized) { + console = hcon; + attr = plain_attr = sbi.wAttributes; + negative = 0; + initialized = 1; + } - initialized = 1; + return 1; } +#define BUFFER_SIZE 4096 +#define MAX_PARAMS 16 + +static void write_console(unsigned char *str, size_t len) +{ + /* only called from console_thread, so a static buffer will do */ + static wchar_t wbuf[2 * BUFFER_SIZE + 1]; + DWORD dummy; + + /* convert utf-8 to utf-16 */ + int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len); + + /* write directly to console */ + WriteConsoleW(console, wbuf, wlen, &dummy, NULL); + + /* remember if non-ascii characters are printed */ + if (wlen != len) + non_ascii_used = 1; +} #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) @@ -90,18 +170,13 @@ static void erase_in_line(void) &dummy); } - -static const char *set_attr(const char *str) +static void set_attr(char func, const int *params, int paramlen) { - const char *func; - size_t len = strspn(str, "0123456789;"); - func = str + len; - - switch (*func) { + int i; + switch (func) { case 'm': - do { - long val = strtol(str, (char **)&str, 10); - switch (val) { + for (i = 0; i < paramlen; i++) { + switch (params[i]) { case 0: /* reset */ attr = plain_attr; negative = 0; @@ -224,9 +299,7 @@ static const char *set_attr(const char *str) /* Unsupported code */ break; } - str++; - } while (*(str-1) == ';'); - + } set_console_attr(); break; case 'K': @@ -236,122 +309,271 @@ static const char *set_attr(const char *str) /* Unsupported code */ break; } - - return func + 1; } -static int ansi_emulate(const char *str, FILE *stream) +enum { + TEXT = 0, ESCAPE = 033, BRACKET = '[' +}; + +static DWORD WINAPI console_thread(LPVOID unused) { - int rv = 0; - const char *pos = str; - - while (*pos) { - pos = strstr(str, "\033["); - if (pos) { - size_t len = pos - str; - - if (len) { - size_t out_len = fwrite(str, 1, len, stream); - rv += out_len; - if (out_len < len) - return rv; + unsigned char buffer[BUFFER_SIZE]; + DWORD bytes; + int start, end = 0, c, parampos = 0, state = TEXT; + int params[MAX_PARAMS]; + + while (1) { + /* read next chunk of bytes from the pipe */ + if (!ReadFile(hread, buffer + end, BUFFER_SIZE - end, &bytes, + NULL)) { + /* exit if pipe has been closed or disconnected */ + if (GetLastError() == ERROR_PIPE_NOT_CONNECTED || + GetLastError() == ERROR_BROKEN_PIPE) + break; + /* ignore other errors */ + continue; + } + + /* scan the bytes and handle ANSI control codes */ + bytes += end; + start = end = 0; + while (end < bytes) { + c = buffer[end++]; + switch (state) { + case TEXT: + if (c == ESCAPE) { + /* print text seen so far */ + if (end - 1 > start) + write_console(buffer + start, + end - 1 - start); + + /* then start parsing escape sequence */ + start = end - 1; + memset(params, 0, sizeof(params)); + parampos = 0; + state = ESCAPE; + } + break; + + case ESCAPE: + /* continue if "\033[", otherwise bail out */ + state = (c == BRACKET) ? BRACKET : TEXT; + break; + + case BRACKET: + /* parse [0-9;]* into array of parameters */ + if (c >= '0' && c <= '9') { + params[parampos] *= 10; + params[parampos] += c - '0'; + } else if (c == ';') { + /* + * next parameter, bail out if out of + * bounds + */ + parampos++; + if (parampos >= MAX_PARAMS) + state = TEXT; + } else { + /* + * end of escape sequence, change + * console attributes + */ + set_attr(c, params, parampos + 1); + start = end; + state = TEXT; + } + break; } + } - str = pos + 2; - rv += 2; + /* print remaining text unless parsing an escape sequence */ + if (state == TEXT && end > start) { + /* check for incomplete UTF-8 sequences and fix end */ + if (buffer[end - 1] >= 0x80) { + if (buffer[end -1] >= 0xc0) + end--; + else if (end - 1 > start && + buffer[end - 2] >= 0xe0) + end -= 2; + else if (end - 2 > start && + buffer[end - 3] >= 0xf0) + end -= 3; + } - fflush(stream); + /* print remaining complete UTF-8 sequences */ + if (end > start) + write_console(buffer + start, end - start); - pos = set_attr(str); - rv += pos - str; - str = pos; + /* move remaining bytes to the front */ + if (end < bytes) + memmove(buffer, buffer + end, bytes - end); + end = bytes - end; } else { - rv += strlen(str); - fputs(str, stream); - return rv; + /* all data has been consumed, mark buffer empty */ + end = 0; } } - return rv; + + /* check if the console font supports unicode */ + warn_if_raster_font(); + + CloseHandle(hread); + return 0; } -int winansi_fputs(const char *str, FILE *stream) +static void winansi_exit(void) { - int rv; - - if (!isatty(fileno(stream))) - return fputs(str, stream); + /* flush all streams */ + _flushall(); - init(); + /* signal console thread to exit */ + FlushFileBuffers(hwrite); + DisconnectNamedPipe(hwrite); - if (!console) - return fputs(str, stream); + /* wait for console thread to copy remaining data */ + WaitForSingleObject(hthread, INFINITE); - rv = ansi_emulate(str, stream); + /* cleanup handles... */ + CloseHandle(hwrite); + CloseHandle(hthread); +} - if (rv >= 0) - return 0; - else - return EOF; +static void die_lasterr(const char *fmt, ...) +{ + va_list params; + va_start(params, fmt); + errno = err_win_to_posix(GetLastError()); + die_errno(fmt, params); + va_end(params); } -static int winansi_vfprintf(FILE *stream, const char *format, va_list list) +static HANDLE duplicate_handle(HANDLE hnd) { - int len, rv; - char small_buf[256]; - char *buf = small_buf; - va_list cp; + HANDLE hresult, hproc = GetCurrentProcess(); + if (!DuplicateHandle(hproc, hnd, hproc, &hresult, 0, TRUE, + DUPLICATE_SAME_ACCESS)) + die_lasterr("DuplicateHandle(%li) failed", (long) hnd); + return hresult; +} - if (!isatty(fileno(stream))) - goto abort; - init(); +/* + * Make MSVCRT's internal file descriptor control structure accessible + * so that we can tweak OS handles and flags directly (we need MSVCRT + * to treat our pipe handle as if it were a console). + * + * We assume that the ioinfo structure (exposed by MSVCRT.dll via + * __pioinfo) starts with the OS handle and the flags. The exact size + * varies between MSVCRT versions, so we try different sizes until + * toggling the FDEV bit of _pioinfo(1)->osflags is reflected in + * isatty(1). + */ +typedef struct { + HANDLE osfhnd; + char osflags; +} ioinfo; - if (!console) - goto abort; +extern __declspec(dllimport) ioinfo *__pioinfo[]; - va_copy(cp, list); - len = vsnprintf(small_buf, sizeof(small_buf), format, cp); - va_end(cp); +static size_t sizeof_ioinfo = 0; - if (len > sizeof(small_buf) - 1) { - buf = malloc(len + 1); - if (!buf) - goto abort; +#define IOINFO_L2E 5 +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) - len = vsnprintf(buf, len + 1, format, list); - } +#define FDEV 0x40 - rv = ansi_emulate(buf, stream); +static inline ioinfo* _pioinfo(int fd) +{ + return (ioinfo*)((char*)__pioinfo[fd >> IOINFO_L2E] + + (fd & (IOINFO_ARRAY_ELTS - 1)) * sizeof_ioinfo); +} - if (buf != small_buf) - free(buf); - return rv; +static int init_sizeof_ioinfo() +{ + int istty, wastty; + /* don't init twice */ + if (sizeof_ioinfo) + return sizeof_ioinfo >= 256; + + sizeof_ioinfo = sizeof(ioinfo); + wastty = isatty(1); + while (sizeof_ioinfo < 256) { + /* toggle FDEV flag, check isatty, then toggle back */ + _pioinfo(1)->osflags ^= FDEV; + istty = isatty(1); + _pioinfo(1)->osflags ^= FDEV; + /* return if we found the correct size */ + if (istty != wastty) + return 0; + sizeof_ioinfo += sizeof(void*); + } + error("Tweaking file descriptors doesn't work with this MSVCRT.dll"); + return 1; +} -abort: - rv = vfprintf(stream, format, list); - return rv; +static HANDLE swap_osfhnd(int fd, HANDLE new_handle) +{ + ioinfo *pioinfo; + HANDLE old_handle; + + /* init ioinfo size if we haven't done so */ + if (init_sizeof_ioinfo()) + return INVALID_HANDLE_VALUE; + + /* get ioinfo pointer and change the handles */ + pioinfo = _pioinfo(fd); + old_handle = pioinfo->osfhnd; + pioinfo->osfhnd = new_handle; + return old_handle; } -int winansi_fprintf(FILE *stream, const char *format, ...) +void winansi_init(void) { - va_list list; - int rv; + int con1, con2; + char name[32]; - va_start(list, format); - rv = winansi_vfprintf(stream, format, list); - va_end(list); + /* check if either stdout or stderr is a console output screen buffer */ + con1 = is_console(1); + con2 = is_console(2); + if (!con1 && !con2) + return; - return rv; + /* create a named pipe to communicate with the console thread */ + sprintf(name, "\\\\.\\pipe\\winansi%lu", GetCurrentProcessId()); + hwrite = CreateNamedPipe(name, PIPE_ACCESS_OUTBOUND, + PIPE_TYPE_BYTE | PIPE_WAIT, 1, BUFFER_SIZE, 0, 0, NULL); + if (hwrite == INVALID_HANDLE_VALUE) + die_lasterr("CreateNamedPipe failed"); + + hread = CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); + if (hread == INVALID_HANDLE_VALUE) + die_lasterr("CreateFile for named pipe failed"); + + /* start console spool thread on the pipe's read end */ + hthread = CreateThread(NULL, 0, console_thread, NULL, 0, NULL); + if (hthread == INVALID_HANDLE_VALUE) + die_lasterr("CreateThread(console_thread) failed"); + + /* schedule cleanup routine */ + if (atexit(winansi_exit)) + die_errno("atexit(winansi_exit) failed"); + + /* redirect stdout / stderr to the pipe */ + if (con1) + hconsole1 = swap_osfhnd(1, duplicate_handle(hwrite)); + if (con2) + hconsole2 = swap_osfhnd(2, duplicate_handle(hwrite)); } -int winansi_printf(const char *format, ...) +/* + * Returns the real console handle if stdout / stderr is a pipe redirecting + * to the console. Allows spawn / exec to pass the console to the next process. + */ +HANDLE winansi_get_osfhandle(int fd) { - va_list list; - int rv; - - va_start(list, format); - rv = winansi_vfprintf(stdout, format, list); - va_end(list); - - return rv; + HANDLE hnd = (HANDLE) _get_osfhandle(fd); + if ((fd == 1 || fd == 2) && isatty(fd) + && GetFileType(hnd) == FILE_TYPE_PIPE) + return (fd == 1) ? hconsole1 : hconsole2; + return hnd; } |