221 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			221 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- Windows.cpp ---------------------------------------------*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
// This file provides Windows support functions
 | 
						|
 | 
						|
#include "lldb/Host/PosixApi.h"
 | 
						|
#include "lldb/Host/windows/windows.h"
 | 
						|
 | 
						|
#include "llvm/Support/ConvertUTF.h"
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <cerrno>
 | 
						|
#include <ctype.h>
 | 
						|
#include <io.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
namespace {
 | 
						|
bool utf8ToWide(const char *utf8, wchar_t *buf, size_t bufSize) {
 | 
						|
  const llvm::UTF8 *sourceStart = reinterpret_cast<const llvm::UTF8 *>(utf8);
 | 
						|
  size_t sourceLen = strlen(utf8) + 1 /* convert null too */;
 | 
						|
  llvm::UTF16 *target = reinterpret_cast<llvm::UTF16 *>(buf);
 | 
						|
  llvm::ConversionFlags flags = llvm::strictConversion;
 | 
						|
  return llvm::ConvertUTF8toUTF16(&sourceStart, sourceStart + sourceLen, &target,
 | 
						|
                            target + bufSize, flags) == llvm::conversionOK;
 | 
						|
}
 | 
						|
 | 
						|
bool wideToUtf8(const wchar_t *wide, char *buf, size_t bufSize) {
 | 
						|
  const llvm::UTF16 *sourceStart = reinterpret_cast<const llvm::UTF16 *>(wide);
 | 
						|
  size_t sourceLen = wcslen(wide) + 1 /* convert null too */;
 | 
						|
  llvm::UTF8 *target = reinterpret_cast<llvm::UTF8 *>(buf);
 | 
						|
  llvm::ConversionFlags flags = llvm::strictConversion;
 | 
						|
  return llvm::ConvertUTF16toUTF8(&sourceStart, sourceStart + sourceLen, &target,
 | 
						|
                            target + bufSize, flags) == llvm::conversionOK;
 | 
						|
}
 | 
						|
}
 | 
						|
 | 
						|
int vasprintf(char **ret, const char *fmt, va_list ap) {
 | 
						|
  char *buf;
 | 
						|
  int len;
 | 
						|
  size_t buflen;
 | 
						|
  va_list ap2;
 | 
						|
 | 
						|
  va_copy(ap2, ap);
 | 
						|
  len = vsnprintf(NULL, 0, fmt, ap2);
 | 
						|
 | 
						|
  if (len >= 0 &&
 | 
						|
      (buf = (char *)malloc((buflen = (size_t)(len + 1)))) != NULL) {
 | 
						|
    len = vsnprintf(buf, buflen, fmt, ap);
 | 
						|
    *ret = buf;
 | 
						|
  } else {
 | 
						|
    *ret = NULL;
 | 
						|
    len = -1;
 | 
						|
  }
 | 
						|
 | 
						|
  va_end(ap2);
 | 
						|
  return len;
 | 
						|
}
 | 
						|
 | 
						|
char *strcasestr(const char *s, const char *find) {
 | 
						|
  char c, sc;
 | 
						|
  size_t len;
 | 
						|
 | 
						|
  if ((c = *find++) != 0) {
 | 
						|
    c = tolower((unsigned char)c);
 | 
						|
    len = strlen(find);
 | 
						|
    do {
 | 
						|
      do {
 | 
						|
        if ((sc = *s++) == 0)
 | 
						|
          return 0;
 | 
						|
      } while ((char)tolower((unsigned char)sc) != c);
 | 
						|
    } while (strncasecmp(s, find, len) != 0);
 | 
						|
    s--;
 | 
						|
  }
 | 
						|
  return const_cast<char *>(s);
 | 
						|
}
 | 
						|
 | 
						|
char *realpath(const char *name, char *resolved) {
 | 
						|
  char *retname = NULL;
 | 
						|
 | 
						|
  /* SUSv3 says we must set `errno = EINVAL', and return NULL,
 | 
						|
  * if `name' is passed as a NULL pointer.
 | 
						|
  */
 | 
						|
  if (name == NULL) {
 | 
						|
    errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  /* Otherwise, `name' must refer to a readable filesystem object,
 | 
						|
  * if we are going to resolve its absolute path name.
 | 
						|
  */
 | 
						|
  wchar_t wideNameBuffer[PATH_MAX];
 | 
						|
  wchar_t *wideName = wideNameBuffer;
 | 
						|
  if (!utf8ToWide(name, wideName, PATH_MAX)) {
 | 
						|
    errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  if (_waccess(wideName, 4) != 0)
 | 
						|
    return NULL;
 | 
						|
 | 
						|
  /* If `name' didn't point to an existing entity,
 | 
						|
  * then we don't get to here; we simply fall past this block,
 | 
						|
  * returning NULL, with `errno' appropriately set by `access'.
 | 
						|
  *
 | 
						|
  * When we _do_ get to here, then we can use `_fullpath' to
 | 
						|
  * resolve the full path for `name' into `resolved', but first,
 | 
						|
  * check that we have a suitable buffer, in which to return it.
 | 
						|
  */
 | 
						|
 | 
						|
  if ((retname = resolved) == NULL) {
 | 
						|
    /* Caller didn't give us a buffer, so we'll exercise the
 | 
						|
    * option granted by SUSv3, and allocate one.
 | 
						|
    *
 | 
						|
    * `_fullpath' would do this for us, but it uses `malloc', and
 | 
						|
    * Microsoft's implementation doesn't set `errno' on failure.
 | 
						|
    * If we don't do this explicitly ourselves, then we will not
 | 
						|
    * know if `_fullpath' fails on `malloc' failure, or for some
 | 
						|
    * other reason, and we want to set `errno = ENOMEM' for the
 | 
						|
    * `malloc' failure case.
 | 
						|
    */
 | 
						|
 | 
						|
    retname = (char *)malloc(PATH_MAX);
 | 
						|
    if (retname == NULL) {
 | 
						|
      errno = ENOMEM;
 | 
						|
      return NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /* Otherwise, when we do have a valid buffer,
 | 
						|
  * `_fullpath' should only fail if the path name is too long.
 | 
						|
  */
 | 
						|
 | 
						|
  wchar_t wideFullPathBuffer[PATH_MAX];
 | 
						|
  wchar_t *wideFullPath;
 | 
						|
  if ((wideFullPath = _wfullpath(wideFullPathBuffer, wideName, PATH_MAX)) ==
 | 
						|
      NULL) {
 | 
						|
    errno = ENAMETOOLONG;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  // Do a LongPath<->ShortPath roundtrip so that case is resolved by OS
 | 
						|
  // FIXME: Check for failure
 | 
						|
  size_t initialLength = wcslen(wideFullPath);
 | 
						|
  GetShortPathNameW(wideFullPath, wideNameBuffer, PATH_MAX);
 | 
						|
  GetLongPathNameW(wideNameBuffer, wideFullPathBuffer, initialLength + 1);
 | 
						|
 | 
						|
  // Convert back to UTF-8
 | 
						|
  if (!wideToUtf8(wideFullPathBuffer, retname, PATH_MAX)) {
 | 
						|
    errno = EINVAL;
 | 
						|
    return NULL;
 | 
						|
  }
 | 
						|
 | 
						|
  // Force drive to be upper case
 | 
						|
  if (retname[1] == ':')
 | 
						|
    retname[0] = toupper(retname[0]);
 | 
						|
 | 
						|
  return retname;
 | 
						|
}
 | 
						|
 | 
						|
#ifdef _MSC_VER
 | 
						|
 | 
						|
char *basename(char *path) {
 | 
						|
  char *l1 = strrchr(path, '\\');
 | 
						|
  char *l2 = strrchr(path, '/');
 | 
						|
  if (l2 > l1)
 | 
						|
    l1 = l2;
 | 
						|
  if (!l1)
 | 
						|
    return path; // no base name
 | 
						|
  return &l1[1];
 | 
						|
}
 | 
						|
 | 
						|
char *dirname(char *path) {
 | 
						|
  char *l1 = strrchr(path, '\\');
 | 
						|
  char *l2 = strrchr(path, '/');
 | 
						|
  if (l2 > l1)
 | 
						|
    l1 = l2;
 | 
						|
  if (!l1)
 | 
						|
    return NULL; // no dir name
 | 
						|
  *l1 = 0;
 | 
						|
  return path;
 | 
						|
}
 | 
						|
 | 
						|
int strcasecmp(const char *s1, const char *s2) { return stricmp(s1, s2); }
 | 
						|
 | 
						|
int strncasecmp(const char *s1, const char *s2, size_t n) {
 | 
						|
  return strnicmp(s1, s2, n);
 | 
						|
}
 | 
						|
 | 
						|
#if _MSC_VER < 1900
 | 
						|
namespace lldb_private {
 | 
						|
int vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) {
 | 
						|
  int old_errno = errno;
 | 
						|
  int r = ::vsnprintf(buffer, count, format, argptr);
 | 
						|
  int new_errno = errno;
 | 
						|
  buffer[count - 1] = '\0';
 | 
						|
  if (r == -1 || r == count) {
 | 
						|
    FILE *nul = fopen("nul", "w");
 | 
						|
    int bytes_written = ::vfprintf(nul, format, argptr);
 | 
						|
    fclose(nul);
 | 
						|
    if (bytes_written < count)
 | 
						|
      errno = new_errno;
 | 
						|
    else {
 | 
						|
      errno = old_errno;
 | 
						|
      r = bytes_written;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return r;
 | 
						|
}
 | 
						|
} // namespace lldb_private
 | 
						|
#endif
 | 
						|
 | 
						|
#endif // _MSC_VER
 |