//===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides the Win32 specific implementation of the Path class.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only generic Win32 code that
//=== is guaranteed to work on *all* Win32 variants.
//===----------------------------------------------------------------------===//
#include "Windows.h"
#include <malloc.h>
#include <cstdio>
// We need to undo a macro defined in Windows.h, otherwise we won't compile:
#undef CopyFile
#undef GetCurrentDirectory
// Windows happily accepts either forward or backward slashes, though any path
// returned by a Win32 API will have backward slashes. As LLVM code basically
// assumes forward slashes are used, backward slashs are converted where they
// can be introduced into a path.
//
// Another invariant is that a path ends with a slash if and only if the path
// is a root directory. Any other use of a trailing slash is stripped. Unlike
// in Unix, Windows has a rather complicated notion of a root path and this
// invariant helps simply the code.
static void FlipBackSlashes(std::string& s) {
for (size_t i = 0; i < s.size(); i++)
if (s[i] == '\\')
s[i] = '/';
}
namespace llvm {
namespace sys {
const char PathSeparator = ';';
StringRef Path::GetEXESuffix() {
return "exe";
}
Path::Path(llvm::StringRef p)
: path(p) {
FlipBackSlashes(path);
}
Path::Path(const char *StrStart, unsigned StrLen)
: path(StrStart, StrLen) {
FlipBackSlashes(path);
}
Path&
Path::operator=(StringRef that) {
path.assign(that.data(), that.size());
FlipBackSlashes(path);
return *this;
}
// push_back 0 on create, and pop_back on delete.
struct ScopedNullTerminator {
std::string &str;
ScopedNullTerminator(std::string &s) : str(s) { str.push_back(0); }
~ScopedNullTerminator() {
// str.pop_back(); But wait, C++03 doesn't have this...
assert(!str.empty() && str[str.size() - 1] == 0
&& "Null char not present!");
str.resize(str.size() - 1);
}
};
bool
Path::isValid() const {
if (path.empty())
return false;
// If there is a colon, it must be the second character, preceded by a letter
// and followed by something.
size_t len = path.size();
// This code assumes that path is null terminated, so make sure it is.
ScopedNullTerminator snt(path);
size_t pos = path.rfind(':',len);
size_t rootslash = 0;
if (pos != std::string::npos) {
if (pos != 1 || !isalpha(path[0]) || len < 3)
return false;
rootslash = 2;
}
// Look for a UNC path, and if found adjust our notion of the root slash.
if (len > 3 && path[0] == '/' && path[1] == '/') {
rootslash = path.find('/', 2);
if (rootslash == std::string::npos)
rootslash = 0;
}
// Check for illegal characters.
if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
"\013\014\015\016\017\020\021\022\023\024\025\026"
"\027\030\031\032\033\034\035\036\037")
!= std::string::npos)
return false;
// Remove trailing slash, unless it's a root slash.
if (len > rootslash+1 && path[len-1] == '/')
path.erase(--len);
// Check each component for legality.
for (pos = 0; pos < len; ++pos) {
// A component may not end in a space.
if (path[pos] == ' ') {
if (path[pos+1] == '/' || path[pos+1] == '\0')
return false;
}
// A component may not end in a period.
if (path[pos] == '.') {
if (path[pos+1] == '/' || path[pos+1] == '\0') {
// Unless it is the pseudo-directory "."...
if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
return true;
// or "..".
if (pos > 0 && path[pos-1] == '.') {