PathV1 is deprecated since the 18th of Dec 2010. Remove it.

llvm-svn: 184960
This commit is contained in:
Rafael Espindola 2013-06-26 16:24:35 +00:00
parent b6d9c0001a
commit b0f2eba499
5 changed files with 0 additions and 1571 deletions

View File

@ -1,407 +0,0 @@
//===- llvm/Support/PathV1.h - Path Operating System Concept ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the llvm::sys::Path class.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_SUPPORT_PATHV1_H
#define LLVM_SUPPORT_PATHV1_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/TimeValue.h"
#include <string>
#include <vector>
#define LLVM_PATH_DEPRECATED_MSG(replacement) \
"PathV1 has been deprecated and will be removed as soon as all LLVM and" \
" Clang clients have been moved over to PathV2. Please use `" #replacement \
"` from PathV2 instead."
namespace llvm {
namespace sys {
/// This structure provides basic file system information about a file. It
/// is patterned after the stat(2) Unix operating system call but made
/// platform independent and eliminates many of the unix-specific fields.
/// However, to support llvm-ar, the mode, user, and group fields are
/// retained. These pertain to unix security and may not have a meaningful
/// value on non-Unix platforms. However, the other fields should
/// always be applicable on all platforms. The structure is filled in by
/// the PathWithStatus class.
/// @brief File status structure
class FileStatus {
public:
uint64_t fileSize; ///< Size of the file in bytes
TimeValue modTime; ///< Time of file's modification
uint32_t mode; ///< Mode of the file, if applicable
uint32_t user; ///< User ID of owner, if applicable
uint32_t group; ///< Group ID of owner, if applicable
bool isDir : 1; ///< True if this is a directory.
bool isFile : 1; ///< True if this is a file.
FileStatus() : fileSize(0), modTime(0,0), mode(0777), user(999),
group(999), isDir(false), isFile(false) { }
TimeValue getTimestamp() const { return modTime; }
uint64_t getSize() const { return fileSize; }
uint32_t getMode() const { return mode; }
uint32_t getUser() const { return user; }
uint32_t getGroup() const { return group; }
};
/// This class provides an abstraction for the path to a file or directory
/// in the operating system's filesystem and provides various basic operations
/// on it. Note that this class only represents the name of a path to a file
/// or directory which may or may not be valid for a given machine's file
/// system. The class is patterned after the java.io.File class with various
/// extensions and several omissions (not relevant to LLVM). A Path object
/// ensures that the path it encapsulates is syntactically valid for the
/// operating system it is running on but does not ensure correctness for
/// any particular file system. That is, a syntactically valid path might
/// specify path components that do not exist in the file system and using
/// such a Path to act on the file system could produce errors. There is one
/// invalid Path value which is permitted: the empty path. The class should
/// never allow a syntactically invalid non-empty path name to be assigned.
/// Empty paths are required in order to indicate an error result in some
/// situations. If the path is empty, the isValid operation will return
/// false. All operations will fail if isValid is false. Operations that
/// change the path will either return false if it would cause a syntactically
/// invalid path name (in which case the Path object is left unchanged) or
/// throw an std::string exception indicating the error. The methods are
/// grouped into four basic categories: Path Accessors (provide information
/// about the path without accessing disk), Disk Accessors (provide
/// information about the underlying file or directory), Path Mutators
/// (change the path information, not the disk), and Disk Mutators (change
/// the disk file/directory referenced by the path). The Disk Mutator methods
/// all have the word "disk" embedded in their method name to reinforce the
/// notion that the operation modifies the file system.
/// @since 1.4
/// @brief An abstraction for operating system paths.
class Path {
/// @name Constructors
/// @{
public:
/// Construct a path to a unique temporary directory that is created in
/// a "standard" place for the operating system. The directory is
/// guaranteed to be created on exit from this function. If the directory
/// cannot be created, the function will throw an exception.
/// @returns an invalid path (empty) on error
/// @param ErrMsg Optional place for an error message if an error occurs
/// @brief Construct a path to an new, unique, existing temporary
/// directory.
static Path GetTemporaryDirectory(std::string* ErrMsg = 0);
/// Construct a path to the current directory for the current process.
/// @returns The current working directory.
/// @brief Returns the current working directory.
static Path GetCurrentDirectory();
/// This is one of the very few ways in which a path can be constructed
/// with a syntactically invalid name. The only *legal* invalid name is an
/// empty one. Other invalid names are not permitted. Empty paths are
/// provided so that they can be used to indicate null or error results in
/// other lib/System functionality.
/// @brief Construct an empty (and invalid) path.
Path() : path() {}
Path(const Path &that) : path(that.path) {}
/// This constructor will accept a char* or std::string as a path. No
/// checking is done on this path to determine if it is valid. To
/// determine validity of the path, use the isValid method.
/// @param p The path to assign.
/// @brief Construct a Path from a string.
explicit Path(StringRef p);
/// This constructor will accept a character range as a path. No checking
/// is done on this path to determine if it is valid. To determine
/// validity of the path, use the isValid method.
/// @param StrStart A pointer to the first character of the path name
/// @param StrLen The length of the path name at StrStart
/// @brief Construct a Path from a string.
Path(const char *StrStart, unsigned StrLen);
/// @}
/// @name Operators
/// @{
public:
/// Makes a copy of \p that to \p this.
/// @returns \p this
/// @brief Assignment Operator
Path &operator=(const Path &that) {
path = that.path;
return *this;
}
/// Makes a copy of \p that to \p this.
/// @param that A StringRef denoting the path
/// @returns \p this
/// @brief Assignment Operator
Path &operator=(StringRef that);
/// Compares \p this Path with \p that Path for equality.
/// @returns true if \p this and \p that refer to the same thing.
/// @brief Equality Operator
bool operator==(const Path &that) const;
/// Compares \p this Path with \p that Path for inequality.
/// @returns true if \p this and \p that refer to different things.
/// @brief Inequality Operator
bool operator!=(const Path &that) const { return !(*this == that); }
/// Determines if \p this Path is less than \p that Path. This is required
/// so that Path objects can be placed into ordered collections (e.g.
/// std::map). The comparison is done lexicographically as defined by
/// the std::string::compare method.
/// @returns true if \p this path is lexicographically less than \p that.
/// @brief Less Than Operator
bool operator<(const Path& that) const;
/// @}
/// @name Path Accessors
/// @{
public:
/// This function will use an operating system specific algorithm to
/// determine if the current value of \p this is a syntactically valid
/// path name for the operating system. The path name does not need to
/// exist, validity is simply syntactical. Empty paths are always invalid.
/// @returns true iff the path name is syntactically legal for the
/// host operating system.
/// @brief Determine if a path is syntactically valid or not.
bool isValid() const;
/// This function determines if the contents of the path name are empty.
/// That is, the path name has a zero length. This does NOT determine if
/// if the file is empty. To get the length of the file itself, Use the
/// PathWithStatus::getFileStatus() method and then the getSize() method
/// on the returned FileStatus object.
/// @returns true iff the path is empty.
/// @brief Determines if the path name is empty (invalid).
bool isEmpty() const { return path.empty(); }
/// Obtain a 'C' string for the path name.
/// @returns a 'C' string containing the path name.
/// @brief Returns the path as a C string.
const char *c_str() const { return path.c_str(); }
const std::string &str() const { return path; }
/// size - Return the length in bytes of this path name.
size_t size() const { return path.size(); }
/// empty - Returns true if the path is empty.
unsigned empty() const { return path.empty(); }
/// @}
/// @name Disk Accessors
/// @{
public:
/// This function determines if the path name in the object references an
/// archive file by looking at its magic number.
/// @returns true if the file starts with the magic number for an archive
/// file.
/// @brief Determine if the path references an archive file.
bool isArchive() const;
/// This function determines if the path name in the object references a
/// native Dynamic Library (shared library, shared object) by looking at
/// the file's magic number. The Path object must reference a file, not a
/// directory.
/// @returns true if the file starts with the magic number for a native
/// shared library.
/// @brief Determine if the path references a dynamic library.
bool isDynamicLibrary() const;
/// This function determines if the path name references an existing file
/// or directory in the file system.
/// @returns true if the pathname references an existing file or
/// directory.
/// @brief Determines if the path is a file or directory in
/// the file system.
LLVM_ATTRIBUTE_DEPRECATED(bool exists() const,
LLVM_PATH_DEPRECATED_MSG(fs::exists));
/// This function determines if the path name references an
/// existing directory.
/// @returns true if the pathname references an existing directory.
/// @brief Determines if the path is a directory in the file system.
LLVM_ATTRIBUTE_DEPRECATED(bool isDirectory() const,
LLVM_PATH_DEPRECATED_MSG(fs::is_directory));
/// This function determines if the path name references an
/// existing symbolic link.
/// @returns true if the pathname references an existing symlink.
/// @brief Determines if the path is a symlink in the file system.
LLVM_ATTRIBUTE_DEPRECATED(bool isSymLink() const,
LLVM_PATH_DEPRECATED_MSG(fs::is_symlink));
/// This function checks that what we're trying to work only on a regular
/// file. Check for things like /dev/null, any block special file, or
/// other things that aren't "regular" regular files.
/// @returns true if the file is S_ISREG.
/// @brief Determines if the file is a regular file
bool isRegularFile() const;
/// @}
/// @name Path Mutators
/// @{
public:
/// The path name is cleared and becomes empty. This is an invalid
/// path name but is the *only* invalid path name. This is provided
/// so that path objects can be used to indicate the lack of a
/// valid path being found.
/// @brief Make the path empty.
void clear() { path.clear(); }
/// This method sets the Path object to \p unverified_path. This can fail
/// if the \p unverified_path does not pass the syntactic checks of the
/// isValid() method. If verification fails, the Path object remains
/// unchanged and false is returned. Otherwise true is returned and the
/// Path object takes on the path value of \p unverified_path
/// @returns true if the path was set, false otherwise.
/// @param unverified_path The path to be set in Path object.
/// @brief Set a full path from a StringRef
bool set(StringRef unverified_path);
/// One path component is removed from the Path. If only one component is
/// present in the path, the Path object becomes empty. If the Path object
/// is empty, no change is made.
/// @returns false if the path component could not be removed.
/// @brief Removes the last directory component of the Path.
bool eraseComponent();
/// The \p component is added to the end of the Path if it is a legal
/// name for the operating system. A directory separator will be added if
/// needed.
/// @returns false if the path component could not be added.
/// @brief Appends one path component to the Path.
bool appendComponent(StringRef component);
/// A period and the \p suffix are appended to the end of the pathname.
/// When the \p suffix is empty, no action is performed.
/// @brief Adds a period and the \p suffix to the end of the pathname.
void appendSuffix(StringRef suffix);
/// The suffix of the filename is erased. The suffix begins with and
/// includes the last . character in the filename after the last directory
/// separator and extends until the end of the name. If no . character is
/// after the last directory separator, then the file name is left
/// unchanged (i.e. it was already without a suffix) but the function
/// returns false.
/// @returns false if there was no suffix to remove, true otherwise.
/// @brief Remove the suffix from a path name.
bool eraseSuffix();
/// The current Path name is made unique in the file system. Upon return,
/// the Path will have been changed to make a unique file in the file
/// system or it will not have been changed if the current path name is
/// already unique.
/// @throws std::string if an unrecoverable error occurs.
/// @brief Make the current path name unique in the file system.
bool makeUnique( bool reuse_current /*= true*/, std::string* ErrMsg );
/// The current Path name is made absolute by prepending the
/// current working directory if necessary.
LLVM_ATTRIBUTE_DEPRECATED(
void makeAbsolute(),
LLVM_PATH_DEPRECATED_MSG(fs::make_absolute));
/// @}
/// @name Disk Mutators
/// @{
public:
/// This method attempts to make the file referenced by the Path object
/// available for reading so that the canRead() method will return true.
/// @brief Make the file readable;
bool makeReadableOnDisk(std::string* ErrMsg = 0);
/// This method attempts to make the file referenced by the Path object
/// available for writing so that the canWrite() method will return true.
/// @brief Make the file writable;
bool makeWriteableOnDisk(std::string* ErrMsg = 0);
/// This method allows the last modified time stamp and permission bits
/// to be set on the disk object referenced by the Path.
/// @throws std::string if an error occurs.
/// @returns true on error.
/// @brief Set the status information.
bool setStatusInfoOnDisk(const FileStatus &SI,
std::string *ErrStr = 0) const;
/// This method attempts to create a directory in the file system with the
/// same name as the Path object. The \p create_parents parameter controls
/// whether intermediate directories are created or not. if \p
/// create_parents is true, then an attempt will be made to create all
/// intermediate directories, as needed. If \p create_parents is false,
/// then only the final directory component of the Path name will be
/// created. The created directory will have no entries.
/// @returns true if the directory could not be created, false otherwise
/// @brief Create the directory this Path refers to.
bool createDirectoryOnDisk(
bool create_parents = false, ///< Determines whether non-existent
///< directory components other than the last one (the "parents")
///< are created or not.
std::string* ErrMsg = 0 ///< Optional place to put error messages.
);
/// This is like createFile except that it creates a temporary file. A
/// unique temporary file name is generated based on the contents of
/// \p this before the call. The new name is assigned to \p this and the
/// file is created. Note that this will both change the Path object
/// *and* create the corresponding file. This function will ensure that
/// the newly generated temporary file name is unique in the file system.
/// @returns true if the file couldn't be created, false otherwise.
/// @brief Create a unique temporary file
bool createTemporaryFileOnDisk(
bool reuse_current = false, ///< When set to true, this parameter
///< indicates that if the current file name does not exist then
///< it will be used without modification.
std::string* ErrMsg = 0 ///< Optional place to put error messages
);
/// This method renames the file referenced by \p this as \p newName. The
/// file referenced by \p this must exist. The file referenced by
/// \p newName does not need to exist.
/// @returns true on error, false otherwise
/// @brief Rename one file as another.
bool renamePathOnDisk(const Path& newName, std::string* ErrMsg);
/// This method attempts to destroy the file or directory named by the
/// last component of the Path. If the Path refers to a directory and the
/// \p destroy_contents is false, an attempt will be made to remove just
/// the directory (the final Path component). If \p destroy_contents is
/// true, an attempt will be made to remove the entire contents of the
/// directory, recursively. If the Path refers to a file, the
/// \p destroy_contents parameter is ignored.
/// @param destroy_contents Indicates whether the contents of a destroyed
/// @param Err An optional string to receive an error message.
/// directory should also be destroyed (recursively).
/// @returns false if the file/directory was destroyed, true on error.
/// @brief Removes the file or directory from the filesystem.
bool eraseFromDisk(bool destroy_contents = false,
std::string *Err = 0) const;
/// @}
/// @name Data
/// @{
protected:
// Our win32 implementation relies on this string being mutable.
mutable std::string path; ///< Storage for the path name.
/// @}
};
}
}
#endif

View File

@ -73,7 +73,6 @@ add_llvm_library(LLVMSupport
IncludeFile.cpp
Memory.cpp
Mutex.cpp
Path.cpp
PathV2.cpp
Process.cpp
Program.cpp
@ -90,7 +89,6 @@ add_llvm_library(LLVMSupport
Unix/Host.inc
Unix/Memory.inc
Unix/Mutex.inc
Unix/Path.inc
Unix/PathV2.inc
Unix/Process.inc
Unix/Program.inc
@ -104,7 +102,6 @@ add_llvm_library(LLVMSupport
Windows/Host.inc
Windows/Memory.inc
Windows/Mutex.inc
Windows/Path.inc
Windows/PathV2.inc
Windows/Process.inc
Windows/Program.inc

View File

@ -1,78 +0,0 @@
//===-- Path.cpp - Implement OS Path Concept --------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header file implements the operating system Path concept.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Path.h"
#include "llvm/Config/config.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/PathV1.h"
#include <cassert>
#include <cstring>
#include <ostream>
using namespace llvm;
using namespace sys;
namespace {
using support::ulittle32_t;
}
//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only TRULY operating system
//=== independent code.
//===----------------------------------------------------------------------===//
bool Path::operator==(const Path &that) const {
return path == that.path;
}
bool Path::operator<(const Path& that) const {
return path < that.path;
}
bool
Path::isArchive() const {
fs::file_magic type;
if (fs::identify_magic(str(), type))
return false;
return type == fs::file_magic::archive;
}
bool
Path::isDynamicLibrary() const {
fs::file_magic type;
if (fs::identify_magic(str(), type))
return false;
switch (type) {
default: return false;
case fs::file_magic::macho_fixed_virtual_memory_shared_lib:
case fs::file_magic::macho_dynamically_linked_shared_lib:
case fs::file_magic::macho_dynamically_linked_shared_lib_stub:
case fs::file_magic::elf_shared_object:
case fs::file_magic::pecoff_executable: return true;
}
}
void
Path::appendSuffix(StringRef suffix) {
if (!suffix.empty()) {
path.append(".");
path.append(suffix);
}
}
// Include the truly platform-specific parts of this class.
#if defined(LLVM_ON_UNIX)
#include "Unix/Path.inc"
#endif
#if defined(LLVM_ON_WIN32)
#include "Windows/Path.inc"
#endif

View File

@ -1,496 +0,0 @@
//===- llvm/Support/Unix/Path.cpp - Unix 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 implements the Unix specific portion of the Path class.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//=== WARNING: Implementation here must contain only generic UNIX code that
//=== is guaranteed to work on *all* UNIX variants.
//===----------------------------------------------------------------------===//
#include "Unix.h"
#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_UTIME_H
#include <utime.h>
#endif
#if HAVE_TIME_H
#include <time.h>
#endif
#if HAVE_DIRENT_H
# include <dirent.h>
# define NAMLEN(dirent) strlen((dirent)->d_name)
#else
# define dirent direct
# define NAMLEN(dirent) (dirent)->d_namlen
# if HAVE_SYS_NDIR_H
# include <sys/ndir.h>
# endif
# if HAVE_SYS_DIR_H
# include <sys/dir.h>
# endif
# if HAVE_NDIR_H
# include <ndir.h>
# endif
#endif
#if HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
// For GNU Hurd
#if defined(__GNU__) && !defined(MAXPATHLEN)
# define MAXPATHLEN 4096
#endif
// Put in a hack for Cygwin which falsely reports that the mkdtemp function
// is available when it is not.
#ifdef __CYGWIN__
# undef HAVE_MKDTEMP
#endif
namespace {
inline bool lastIsSlash(const std::string& path) {
return !path.empty() && path[path.length() - 1] == '/';
}
}
namespace llvm {
using namespace sys;
Path::Path(StringRef p)
: path(p) {}
Path::Path(const char *StrStart, unsigned StrLen)
: path(StrStart, StrLen) {}
Path&
Path::operator=(StringRef that) {
path.assign(that.data(), that.size());
return *this;
}
bool
Path::isValid() const {
// Empty paths are considered invalid here.
// This code doesn't check MAXPATHLEN because there's no need. Nothing in
// LLVM manipulates Paths with fixed-sizes arrays, and if the OS can't
// handle names longer than some limit, it'll report this on demand using
// ENAMETOLONG.
return !path.empty();
}
Path
Path::GetTemporaryDirectory(std::string *ErrMsg) {
#if defined(HAVE_MKDTEMP)
// The best way is with mkdtemp but that's not available on many systems,
// Linux and FreeBSD have it. Others probably won't.
char pathname[] = "/tmp/llvm_XXXXXX";
if (0 == mkdtemp(pathname)) {
MakeErrMsg(ErrMsg,
std::string(pathname) + ": can't create temporary directory");
return Path();
}
return Path(pathname);
#elif defined(HAVE_MKSTEMP)
// If no mkdtemp is available, mkstemp can be used to create a temporary file
// which is then removed and created as a directory. We prefer this over
// mktemp because of mktemp's inherent security and threading risks. We still
// have a slight race condition from the time the temporary file is created to
// the time it is re-created as a directoy.
char pathname[] = "/tmp/llvm_XXXXXX";
int fd = 0;
if (-1 == (fd = mkstemp(pathname))) {
MakeErrMsg(ErrMsg,
std::string(pathname) + ": can't create temporary directory");
return Path();
}
::close(fd);
::unlink(pathname); // start race condition, ignore errors
if (-1 == ::mkdir(pathname, S_IRWXU)) { // end race condition
MakeErrMsg(ErrMsg,
std::string(pathname) + ": can't create temporary directory");
return Path();
}
return Path(pathname);
#elif defined(HAVE_MKTEMP)
// If a system doesn't have mkdtemp(3) or mkstemp(3) but it does have
// mktemp(3) then we'll assume that system (e.g. AIX) has a reasonable
// implementation of mktemp(3) and doesn't follow BSD 4.3's lead of replacing
// the XXXXXX with the pid of the process and a letter. That leads to only
// twenty six temporary files that can be generated.
char pathname[] = "/tmp/llvm_XXXXXX";
char *TmpName = ::mktemp(pathname);
if (TmpName == 0) {
MakeErrMsg(ErrMsg,
std::string(TmpName) + ": can't create unique directory name");
return Path();
}
if (-1 == ::mkdir(TmpName, S_IRWXU)) {
MakeErrMsg(ErrMsg,
std::string(TmpName) + ": can't create temporary directory");
return Path();
}
return Path(TmpName);
#else
// This is the worst case implementation. tempnam(3) leaks memory unless its
// on an SVID2 (or later) system. On BSD 4.3 it leaks. tmpnam(3) has thread
// issues. The mktemp(3) function doesn't have enough variability in the
// temporary name generated. So, we provide our own implementation that
// increments an integer from a random number seeded by the current time. This
// should be sufficiently unique that we don't have many collisions between
// processes. Generally LLVM processes don't run very long and don't use very
// many temporary files so this shouldn't be a big issue for LLVM.
static time_t num = ::time(0);
char pathname[MAXPATHLEN];
do {
num++;
sprintf(pathname, "/tmp/llvm_%010u", unsigned(num));
} while ( 0 == access(pathname, F_OK ) );
if (-1 == ::mkdir(pathname, S_IRWXU)) {
MakeErrMsg(ErrMsg,
std::string(pathname) + ": can't create temporary directory");
return Path();
}
return Path(pathname);
#endif
}
Path
Path::GetCurrentDirectory() {
char pathname[MAXPATHLEN];
if (!getcwd(pathname, MAXPATHLEN)) {
assert(false && "Could not query current working directory.");
return Path();
}
return Path(pathname);
}
bool
Path::exists() const {
return 0 == access(path.c_str(), F_OK );
}
bool
Path::isDirectory() const {
struct stat buf;
if (0 != stat(path.c_str(), &buf))
return false;
return ((buf.st_mode & S_IFMT) == S_IFDIR) ? true : false;
}
bool
Path::isSymLink() const {
struct stat buf;
if (0 != lstat(path.c_str(), &buf))
return false;
return S_ISLNK(buf.st_mode);
}
bool
Path::isRegularFile() const {
// Get the status so we can determine if it's a file or directory
struct stat buf;
if (0 != stat(path.c_str(), &buf))
return false;
if (S_ISREG(buf.st_mode))
return true;
return false;
}
static bool AddPermissionBits(const Path &File, int bits) {
// Get the umask value from the operating system. We want to use it
// when changing the file's permissions. Since calling umask() sets
// the umask and returns its old value, we must call it a second
// time to reset it to the user's preference.
int mask = umask(0777); // The arg. to umask is arbitrary.
umask(mask); // Restore the umask.
// Get the file's current mode.
struct stat buf;
if (0 != stat(File.c_str(), &buf))
return false;
// Change the file to have whichever permissions bits from 'bits'
// that the umask would not disable.
if ((chmod(File.c_str(), (buf.st_mode | (bits & ~mask)))) == -1)
return false;
return true;
}
bool Path::makeReadableOnDisk(std::string* ErrMsg) {
if (!AddPermissionBits(*this, 0444))
return MakeErrMsg(ErrMsg, path + ": can't make file readable");
return false;
}
bool Path::makeWriteableOnDisk(std::string* ErrMsg) {
if (!AddPermissionBits(*this, 0222))
return MakeErrMsg(ErrMsg, path + ": can't make file writable");
return false;
}
bool
Path::set(StringRef a_path) {
if (a_path.empty())
return false;
path = a_path;
return true;
}
bool
Path::appendComponent(StringRef name) {
if (name.empty())
return false;
if (!lastIsSlash(path))
path += '/';
path += name;
return true;
}
bool
Path::eraseComponent() {
size_t slashpos = path.rfind('/',path.size());
if (slashpos == 0 || slashpos == std::string::npos) {
path.erase();
return true;
}
if (slashpos == path.size() - 1)
slashpos = path.rfind('/',slashpos-1);
if (slashpos == std::string::npos) {
path.erase();
return true;
}
path.erase(slashpos);
return true;
}
bool
Path::eraseSuffix() {
size_t dotpos = path.rfind('.',path.size());
size_t slashpos = path.rfind('/',path.size());
if (dotpos != std::string::npos) {
if (slashpos == std::string::npos || dotpos > slashpos+1) {
path.erase(dotpos, path.size()-dotpos);
return true;
}
}
return false;
}
static bool createDirectoryHelper(char* beg, char* end, bool create_parents) {
if (access(beg, R_OK | W_OK) == 0)
return false;
if (create_parents) {
char* c = end;
for (; c != beg; --c)
if (*c == '/') {
// Recurse to handling the parent directory.
*c = '\0';
bool x = createDirectoryHelper(beg, c, create_parents);
*c = '/';
// Return if we encountered an error.
if (x)
return true;
break;
}
}
return mkdir(beg, S_IRWXU | S_IRWXG) != 0;
}
bool
Path::createDirectoryOnDisk( bool create_parents, std::string* ErrMsg ) {
// Get a writeable copy of the path name
std::string pathname(path);
// Null-terminate the last component
size_t lastchar = path.length() - 1 ;
if (pathname[lastchar] != '/')
++lastchar;
pathname[lastchar] = '\0';
if (createDirectoryHelper(&pathname[0], &pathname[lastchar], create_parents))
return MakeErrMsg(ErrMsg, pathname + ": can't create directory");
return false;
}
bool
Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
// Make this into a unique file name
if (makeUnique( reuse_current, ErrMsg ))
return true;
// create the file
int fd = ::open(path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (fd < 0)
return MakeErrMsg(ErrMsg, path + ": can't create temporary file");
::close(fd);
return false;
}
bool
Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
// Get the status so we can determine if it's a file or directory.
struct stat buf;
if (0 != stat(path.c_str(), &buf)) {
MakeErrMsg(ErrStr, path + ": can't get status of file");
return true;
}
// Note: this check catches strange situations. In all cases, LLVM should
// only be involved in the creation and deletion of regular files. This
// check ensures that what we're trying to erase is a regular file. It
// effectively prevents LLVM from erasing things like /dev/null, any block
// special file, or other things that aren't "regular" files.
if (S_ISREG(buf.st_mode)) {
if (unlink(path.c_str()) != 0)
return MakeErrMsg(ErrStr, path + ": can't destroy file");
return false;
}
if (!S_ISDIR(buf.st_mode)) {
if (ErrStr) *ErrStr = "not a file or directory";
return true;
}
if (remove_contents) {
// Recursively descend the directory to remove its contents.
std::string cmd = "/bin/rm -rf " + path;
if (system(cmd.c_str()) != 0) {
MakeErrMsg(ErrStr, path + ": failed to recursively remove directory.");
return true;
}
return false;
}
// Otherwise, try to just remove the one directory.
std::string pathname(path);
size_t lastchar = path.length() - 1;
if (pathname[lastchar] == '/')
pathname[lastchar] = '\0';
else
pathname[lastchar+1] = '\0';
if (rmdir(pathname.c_str()) != 0)
return MakeErrMsg(ErrStr, pathname + ": can't erase directory");
return false;
}
bool
Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) {
if (0 != ::rename(path.c_str(), newName.c_str()))
return MakeErrMsg(ErrMsg, std::string("can't rename '") + path + "' as '" +
newName.str() + "'");
return false;
}
bool
Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrStr) const {
struct utimbuf utb;
utb.actime = si.modTime.toPosixTime();
utb.modtime = utb.actime;
if (0 != ::utime(path.c_str(),&utb))
return MakeErrMsg(ErrStr, path + ": can't set file modification time");
if (0 != ::chmod(path.c_str(),si.mode))
return MakeErrMsg(ErrStr, path + ": can't set mode");
return false;
}
bool
Path::makeUnique(bool reuse_current, std::string* ErrMsg) {
bool Exists;
if (reuse_current && (fs::exists(path, Exists) || !Exists))
return false; // File doesn't exist already, just use it!
// Append an XXXXXX pattern to the end of the file for use with mkstemp,
// mktemp or our own implementation.
// This uses std::vector instead of SmallVector to avoid a dependence on
// libSupport. And performance isn't critical here.
std::vector<char> Buf;
Buf.resize(path.size()+8);
char *FNBuffer = &Buf[0];
path.copy(FNBuffer,path.size());
bool isdir;
if (!fs::is_directory(path, isdir) && isdir)
strcpy(FNBuffer+path.size(), "/XXXXXX");
else
strcpy(FNBuffer+path.size(), "-XXXXXX");
#if defined(HAVE_MKSTEMP)
int TempFD;
if ((TempFD = mkstemp(FNBuffer)) == -1)
return MakeErrMsg(ErrMsg, path + ": can't make unique filename");
// We don't need to hold the temp file descriptor... we will trust that no one
// will overwrite/delete the file before we can open it again.
close(TempFD);
// Save the name
path = FNBuffer;
// By default mkstemp sets the mode to 0600, so update mode bits now.
AddPermissionBits (*this, 0666);
#elif defined(HAVE_MKTEMP)
// If we don't have mkstemp, use the old and obsolete mktemp function.
if (mktemp(FNBuffer) == 0)
return MakeErrMsg(ErrMsg, path + ": can't make unique filename");
// Save the name
path = FNBuffer;
#else
// Okay, looks like we have to do it all by our lonesome.
static unsigned FCounter = 0;
// Try to initialize with unique value.
if (FCounter == 0) FCounter = ((unsigned)getpid() & 0xFFFF) << 8;
char* pos = strstr(FNBuffer, "XXXXXX");
do {
if (++FCounter > 0xFFFFFF) {
return MakeErrMsg(ErrMsg,
path + ": can't make unique filename: too many files");
}
sprintf(pos, "%06X", FCounter);
path = FNBuffer;
} while (exists());
// POSSIBLE SECURITY BUG: An attacker can easily guess the name and exploit
// LLVM.
#endif
return false;
}
} // end llvm namespace

View File

@ -1,587 +0,0 @@
//===- 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 <cstdio>
#include <malloc.h>
// We need to undo a macro defined in Windows.h, otherwise we won't compile:
#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 {
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;
}
bool
Path::isValid() const {
if (path.empty())
return false;
size_t len = path.size();
// If there is a null character, it and all its successors are ignored.
size_t pos = path.find_first_of('\0');
if (pos != std::string::npos)
len = pos;
// If there is a colon, it must be the second character, preceded by a letter
// and followed by something.
pos = path.rfind(':',len);
size_t rootslash = 0;
if (pos != std::string::npos) {
if (pos != 1 || !isalpha(static_cast<unsigned char>(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 (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0')
return false;
}
// A component may not end in a period.
if (path[pos] == '.') {
if (pos+1 == len || path[pos+1] == '/') {
// Unless it is the pseudo-directory "."...
if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
return true;
// or "..".
if (pos > 0 && path[pos-1] == '.') {
if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
return true;
}
return false;
}
}
}
return true;
}
void Path::makeAbsolute() {
TCHAR FullPath[MAX_PATH + 1] = {0};
LPTSTR FilePart = NULL;
DWORD RetLength = ::GetFullPathNameA(path.c_str(),
sizeof(FullPath)/sizeof(FullPath[0]),
FullPath, &FilePart);
if (0 == RetLength) {
// FIXME: Report the error GetLastError()
assert(0 && "Unable to make absolute path!");
} else if (RetLength > MAX_PATH) {
// FIXME: Report too small buffer (needed RetLength bytes).
assert(0 && "Unable to make absolute path!");
} else {
path = FullPath;
}
}
static Path *TempDirectory;
Path
Path::GetTemporaryDirectory(std::string* ErrMsg) {
if (TempDirectory) {
#if defined(_MSC_VER)
// Visual Studio gets confused and emits a diagnostic about calling exists,
// even though this is the implementation for PathV1. Temporarily
// disable the deprecated warning message
#pragma warning(push)
#pragma warning(disable:4996)
#endif
assert(TempDirectory->exists() && "Who has removed TempDirectory?");
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
return *TempDirectory;
}
char pathname[MAX_PATH];
if (!GetTempPath(MAX_PATH, pathname)) {
if (ErrMsg)
*ErrMsg = "Can't determine temporary directory";
return Path();
}
Path result;
result.set(pathname);
// Append a subdirectory based on our process id so multiple LLVMs don't
// step on each other's toes.
#ifdef __MINGW32__
// Mingw's Win32 header files are broken.
sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId()));
#else
sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
#endif
result.appendComponent(pathname);
// If there's a directory left over from a previous LLVM execution that
// happened to have the same process id, get rid of it.
result.eraseFromDisk(true);
// And finally (re-)create the empty directory.
result.createDirectoryOnDisk(false);
TempDirectory = new Path(result);
return *TempDirectory;
}
Path
Path::GetCurrentDirectory() {
char pathname[MAX_PATH];
::GetCurrentDirectoryA(MAX_PATH,pathname);
return Path(pathname);
}
// FIXME: the above set of functions don't map to Windows very well.
bool
Path::exists() const {
DWORD attr = GetFileAttributes(path.c_str());
return attr != INVALID_FILE_ATTRIBUTES;
}
bool
Path::isDirectory() const {
DWORD attr = GetFileAttributes(path.c_str());
return (attr != INVALID_FILE_ATTRIBUTES) &&
(attr & FILE_ATTRIBUTE_DIRECTORY);
}
bool
Path::isSymLink() const {
DWORD attributes = GetFileAttributes(path.c_str());
if (attributes == INVALID_FILE_ATTRIBUTES)
// There's no sane way to report this :(.
assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES");
// This isn't exactly what defines a NTFS symlink, but it is only true for
// paths that act like a symlink.
return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
}
bool
Path::isRegularFile() const {
bool res;
if (fs::is_regular_file(path, res))
return false;
return res;
}
bool Path::makeReadableOnDisk(std::string* ErrMsg) {
// All files are readable on Windows (ignoring security attributes).
return false;
}
bool Path::makeWriteableOnDisk(std::string* ErrMsg) {
DWORD attr = GetFileAttributes(path.c_str());
// If it doesn't exist, we're done.
if (attr == INVALID_FILE_ATTRIBUTES)
return false;
if (attr & FILE_ATTRIBUTE_READONLY) {
if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) {
MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: ");
return true;
}
}
return false;
}
bool
Path::set(StringRef a_path) {
if (a_path.empty())
return false;
std::string save(path);
path = a_path;
FlipBackSlashes(path);
if (!isValid()) {
path = save;
return false;
}
return true;
}
bool
Path::appendComponent(StringRef name) {
if (name.empty())
return false;
std::string save(path);
if (!path.empty()) {
size_t last = path.size() - 1;
if (path[last] != '/')
path += '/';
}
path += name;
if (!isValid()) {
path = save;
return false;
}
return true;
}
bool
Path::eraseComponent() {
size_t slashpos = path.rfind('/',path.size());
if (slashpos == path.size() - 1 || slashpos == std::string::npos)
return false;
std::string save(path);
path.erase(slashpos);
if (!isValid()) {
path = save;
return false;
}
return true;
}
bool
Path::eraseSuffix() {
size_t dotpos = path.rfind('.',path.size());
size_t slashpos = path.rfind('/',path.size());
if (dotpos != std::string::npos) {
if (slashpos == std::string::npos || dotpos > slashpos+1) {
std::string save(path);
path.erase(dotpos, path.size()-dotpos);
if (!isValid()) {
path = save;
return false;
}
return true;
}
}
return false;
}
inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) {
if (ErrMsg)
*ErrMsg = std::string(pathname) + ": " + std::string(msg);
return true;
}
bool
Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) {
// Get a writeable copy of the path name
size_t len = path.length();
char *pathname = reinterpret_cast<char *>(_alloca(len+2));
path.copy(pathname, len);
pathname[len] = 0;
// Make sure it ends with a slash.
if (len == 0 || pathname[len - 1] != '/') {
pathname[len] = '/';
pathname[++len] = 0;
}
// Determine starting point for initial / search.
char *next = pathname;
if (pathname[0] == '/' && pathname[1] == '/') {
// Skip host name.
next = strchr(pathname+2, '/');
if (next == NULL)
return PathMsg(ErrMsg, pathname, "badly formed remote directory");
// Skip share name.
next = strchr(next+1, '/');
if (next == NULL)
return PathMsg(ErrMsg, pathname,"badly formed remote directory");
next++;
if (*next == 0)
return PathMsg(ErrMsg, pathname, "badly formed remote directory");
} else {
if (pathname[1] == ':')
next += 2; // skip drive letter
if (*next == '/')
next++; // skip root directory
}
// If we're supposed to create intermediate directories
if (create_parents) {
// Loop through the directory components until we're done
while (*next) {
next = strchr(next, '/');
*next = 0;
if (!CreateDirectory(pathname, NULL) &&
GetLastError() != ERROR_ALREADY_EXISTS)
return MakeErrMsg(ErrMsg,
std::string(pathname) + ": Can't create directory: ");
*next++ = '/';
}
} else {
// Drop trailing slash.
pathname[len-1] = 0;
if (!CreateDirectory(pathname, NULL) &&
GetLastError() != ERROR_ALREADY_EXISTS) {
return MakeErrMsg(ErrMsg, std::string(pathname) +
": Can't create directory: ");
}
}
return false;
}
bool
Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
WIN32_FILE_ATTRIBUTE_DATA fi;
if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
return true;
if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// If it doesn't exist, we're done.
bool Exists;
if (fs::exists(path, Exists) || !Exists)
return false;
char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
int lastchar = path.length() - 1 ;
path.copy(pathname, lastchar+1);
// Make path end with '/*'.
if (pathname[lastchar] != '/')
pathname[++lastchar] = '/';
pathname[lastchar+1] = '*';
pathname[lastchar+2] = 0;
if (remove_contents) {
WIN32_FIND_DATA fd;
HANDLE h = FindFirstFile(pathname, &fd);
// It's a bad idea to alter the contents of a directory while enumerating
// its contents. So build a list of its contents first, then destroy them.
if (h != INVALID_HANDLE_VALUE) {
std::vector<Path> list;
do {
if (strcmp(fd.cFileName, ".") == 0)
continue;
if (strcmp(fd.cFileName, "..") == 0)
continue;
Path aPath(path);
aPath.appendComponent(&fd.cFileName[0]);
list.push_back(aPath);
} while (FindNextFile(h, &fd));
DWORD err = GetLastError();
FindClose(h);
if (err != ERROR_NO_MORE_FILES) {
SetLastError(err);
return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
}
for (std::vector<Path>::iterator I = list.begin(); I != list.end();
++I) {
Path &aPath = *I;
aPath.eraseFromDisk(true);
}
} else {
if (GetLastError() != ERROR_FILE_NOT_FOUND)
return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
}
}
pathname[lastchar] = 0;
if (!RemoveDirectory(pathname))
return MakeErrMsg(ErrStr,
std::string(pathname) + ": Can't destroy directory: ");
return false;
} else {
// Read-only files cannot be deleted on Windows. Must remove the read-only
// attribute first.
if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
if (!SetFileAttributes(path.c_str(),
fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
}
if (!DeleteFile(path.c_str()))
return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
return false;
}
}
bool
Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) {
if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING))
return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path
+ "': ");
return false;
}
bool
Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const {
// FIXME: should work on directories also.
if (!si.isFile) {
return true;
}
HANDLE h = CreateFile(path.c_str(),
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (h == INVALID_HANDLE_VALUE)
return true;
BY_HANDLE_FILE_INFORMATION bhfi;
if (!GetFileInformationByHandle(h, &bhfi)) {
DWORD err = GetLastError();
CloseHandle(h);
SetLastError(err);
return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: ");
}
ULARGE_INTEGER ui;
ui.QuadPart = si.modTime.toWin32Time();
FILETIME ft;
ft.dwLowDateTime = ui.LowPart;
ft.dwHighDateTime = ui.HighPart;
BOOL ret = SetFileTime(h, NULL, &ft, &ft);
DWORD err = GetLastError();
CloseHandle(h);
if (!ret) {
SetLastError(err);
return MakeErrMsg(ErrMsg, path + ": SetFileTime: ");
}
// Best we can do with Unix permission bits is to interpret the owner
// writable bit.
if (si.mode & 0200) {
if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
if (!SetFileAttributes(path.c_str(),
bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
}
} else {
if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
if (!SetFileAttributes(path.c_str(),
bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
}
}
return false;
}
bool
Path::makeUnique(bool reuse_current, std::string* ErrMsg) {
bool Exists;
if (reuse_current && (fs::exists(path, Exists) || !Exists))
return false; // File doesn't exist already, just use it!
// Reserve space for -XXXXXX at the end.
char *FNBuffer = (char*) alloca(path.size()+8);
unsigned offset = path.size();
path.copy(FNBuffer, offset);
// Find a numeric suffix that isn't used by an existing file. Assume there
// won't be more than 1 million files with the same prefix. Probably a safe
// bet.
static int FCounter = -1;
if (FCounter < 0) {
// Give arbitrary initial seed.
// FIXME: We should use sys::fs::unique_file() in future.
LARGE_INTEGER cnt64;
DWORD x = GetCurrentProcessId();
x = (x << 16) | (x >> 16);
if (QueryPerformanceCounter(&cnt64)) // RDTSC
x ^= cnt64.HighPart ^ cnt64.LowPart;
FCounter = x % 1000000;
}
do {
sprintf(FNBuffer+offset, "-%06u", FCounter);
if (++FCounter > 999999)
FCounter = 0;
path = FNBuffer;
} while (!fs::exists(path, Exists) && Exists);
return false;
}
bool
Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
// Make this into a unique file name
makeUnique(reuse_current, ErrMsg);
// Now go and create it
HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
return MakeErrMsg(ErrMsg, path + ": can't create file");
CloseHandle(h);
return false;
}
}
}