1 //===- llvm/System/Linux/Path.cpp - Linux Path Implementation ---*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file was developed by Reid Spencer and is distributed under the
6 // University of Illinois Open Source License. See LICENSE.TXT for details.
8 // Modified by Henrik Bach to comply with at least MinGW.
9 // Ported to Win32 by Jeff Cohen.
11 //===----------------------------------------------------------------------===//
13 // This file provides the Win32 specific implementation of the Path class.
15 //===----------------------------------------------------------------------===//
17 //===----------------------------------------------------------------------===//
18 //=== WARNING: Implementation here must contain only generic Win32 code that
19 //=== is guaranteed to work on *all* Win32 variants.
20 //===----------------------------------------------------------------------===//
25 // We need to undo a macro defined in Windows.h, otherwise we won't compile:
28 static void FlipBackSlashes(std::string& s) {
29 for (size_t i = 0; i < s.size(); i++)
38 Path::isValid() const {
42 // If there is a colon, it must be the second character, preceded by a letter
43 // and followed by something.
44 size_t len = path.size();
45 size_t pos = path.rfind(':',len);
46 if (pos != std::string::npos) {
47 if (pos != 1 || !isalpha(path[0]) || len < 3)
51 // Check for illegal characters.
52 if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
53 "\013\014\015\016\017\020\021\022\023\024\025\026"
54 "\027\030\031\032\033\034\035\036\037")
58 // Check each component for legality.
59 for (pos = 0; pos < len; ++pos) {
60 // A component may not end in a space.
61 if (path[pos] == ' ') {
62 if (path[pos+1] == '/' || path[pos+1] == '\0')
66 // A component may not end in a period.
67 if (path[pos] == '.') {
68 if (path[pos+1] == '/' || path[pos+1] == '\0') {
69 // Unless it is the pseudo-directory "."...
70 if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
73 if (pos > 0 && path[pos-1] == '.') {
74 if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
85 static Path *TempDirectory = NULL;
88 Path::GetTemporaryDirectory() {
90 return *TempDirectory;
92 char pathname[MAX_PATH];
93 if (!GetTempPath(MAX_PATH, pathname))
94 throw std::string("Can't determine temporary directory");
97 result.setDirectory(pathname);
99 // Append a subdirectory passed on our process id so multiple LLVMs don't
100 // step on each other's toes.
101 sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
102 result.appendDirectory(pathname);
104 // If there's a directory left over from a previous LLVM execution that
105 // happened to have the same process id, get rid of it.
106 result.destroyDirectory(true);
108 // And finally (re-)create the empty directory.
109 result.createDirectory(false);
110 TempDirectory = new Path(result);
111 return *TempDirectory;
114 Path::Path(const std::string& unverified_path)
115 : path(unverified_path)
117 FlipBackSlashes(path);
118 if (unverified_path.empty())
124 throw std::string(unverified_path + ": path is not valid");
127 // FIXME: the following set of functions don't map to Windows very well.
129 Path::GetRootDirectory() {
131 result.setDirectory("/");
135 static void getPathList(const char*path, std::vector<sys::Path>& Paths) {
136 const char* at = path;
137 const char* delim = strchr(at, ';');
139 while( delim != 0 ) {
140 std::string tmp(at, size_t(delim-at));
141 if (tmpPath.setDirectory(tmp))
142 if (tmpPath.canRead())
143 Paths.push_back(tmpPath);
145 delim = strchr(at, ';');
148 if (tmpPath.setDirectory(std::string(at)))
149 if (tmpPath.canRead())
150 Paths.push_back(tmpPath);
155 Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
156 Paths.push_back(sys::Path("C:\\WINDOWS\\SYSTEM32\\"));
157 Paths.push_back(sys::Path("C:\\WINDOWS\\"));
161 Path::GetBytecodeLibraryPaths(std::vector<sys::Path>& Paths) {
162 char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
164 getPathList(env_var,Paths);
169 if (tmpPath.setDirectory(LLVM_LIBDIR))
170 if (tmpPath.canRead())
171 Paths.push_back(tmpPath);
174 GetSystemLibraryPaths(Paths);
178 Path::GetLLVMDefaultConfigDir() {
179 // TODO: this isn't going to fly on Windows
180 return Path("/etc/llvm/");
184 Path::GetUserHomeDirectory() {
185 // TODO: Typical Windows setup doesn't define HOME.
186 const char* home = getenv("HOME");
189 if (result.setDirectory(home))
192 return GetRootDirectory();
194 // FIXME: the above set of functions don't map to Windows very well.
197 Path::isFile() const {
198 WIN32_FILE_ATTRIBUTE_DATA fi;
199 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
200 ThrowError(std::string(path) + ": Can't get status: ");
201 return fi.dwFileAttributes & FILE_ATTRIBUTE_NORMAL;
205 Path::isDirectory() const {
206 WIN32_FILE_ATTRIBUTE_DATA fi;
207 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
208 ThrowError(std::string(path) + ": Can't get status: ");
209 return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
213 Path::getBasename() const {
214 // Find the last slash
215 size_t slash = path.rfind('/');
216 if (slash == std::string::npos)
221 return path.substr(slash, path.rfind('.'));
224 bool Path::hasMagicNumber(const std::string &Magic) const {
225 std::string actualMagic;
226 if (getMagicNumber(actualMagic, Magic.size()))
227 return Magic == actualMagic;
232 Path::isBytecodeFile() const {
233 std::string actualMagic;
234 if (!getMagicNumber(actualMagic, 4))
236 return actualMagic == "llvc" || actualMagic == "llvm";
240 Path::exists() const {
241 DWORD attr = GetFileAttributes(path.c_str());
242 return attr != INVALID_FILE_ATTRIBUTES;
246 Path::canRead() const {
247 // FIXME: take security attributes into account.
248 DWORD attr = GetFileAttributes(path.c_str());
249 return attr != INVALID_FILE_ATTRIBUTES;
253 Path::canWrite() const {
254 // FIXME: take security attributes into account.
255 DWORD attr = GetFileAttributes(path.c_str());
256 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
260 Path::canExecute() const {
261 // FIXME: take security attributes into account.
262 DWORD attr = GetFileAttributes(path.c_str());
263 return attr != INVALID_FILE_ATTRIBUTES;
267 Path::getLast() const {
268 // Find the last slash
269 size_t pos = path.rfind('/');
271 // Handle the corner cases
272 if (pos == std::string::npos)
275 // If the last character is a slash
276 if (pos == path.length()-1) {
277 // Find the second to last slash
278 size_t pos2 = path.rfind('/', pos-1);
279 if (pos2 == std::string::npos)
280 return path.substr(0,pos);
282 return path.substr(pos2+1,pos-pos2-1);
284 // Return everything after the last slash
285 return path.substr(pos+1);
289 Path::getStatusInfo(StatusInfo& info) const {
290 WIN32_FILE_ATTRIBUTE_DATA fi;
291 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
292 ThrowError(std::string(path) + ": Can't get status: ");
294 info.fileSize = fi.nFileSizeHigh;
295 info.fileSize <<= 32;
296 info.fileSize += fi.nFileSizeLow;
298 info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
299 info.user = 9999; // Not applicable to Windows, so...
300 info.group = 9999; // Not applicable to Windows, so...
302 __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
303 info.modTime.fromWin32Time(ft);
305 info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
306 if (info.isDir && path[path.length() - 1] != '/')
308 else if (!info.isDir && path[path.length() - 1] == '/')
309 path.erase(path.length() - 1);
312 static bool AddPermissionBits(const std::string& Filename, int bits) {
313 DWORD attr = GetFileAttributes(Filename.c_str());
315 // If it doesn't exist, we're done.
316 if (attr == INVALID_FILE_ATTRIBUTES)
319 // The best we can do to interpret Unix permission bits is to use
320 // the owner writable bit.
321 if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
322 if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
323 ThrowError(Filename + ": SetFileAttributes: ");
328 void Path::makeReadable() {
329 // All files are readable on Windows (ignoring security attributes).
332 void Path::makeWriteable() {
333 DWORD attr = GetFileAttributes(path.c_str());
335 // If it doesn't exist, we're done.
336 if (attr == INVALID_FILE_ATTRIBUTES)
339 if (attr & FILE_ATTRIBUTE_READONLY) {
340 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
341 ThrowError(std::string(path) + ": Can't make file writable: ");
345 void Path::makeExecutable() {
346 // All files are executable on Windows (ignoring security attributes).
350 Path::getDirectoryContents(std::set<Path>& result) const {
356 std::string searchpath = path + "*";
357 HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
358 if (h == INVALID_HANDLE_VALUE) {
359 if (GetLastError() == ERROR_FILE_NOT_FOUND)
360 return true; // not really an error, now is it?
361 ThrowError(path + ": Can't read directory: ");
365 if (fd.cFileName[0] == '.')
367 Path aPath(path + &fd.cFileName[0]);
368 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
370 result.insert(aPath);
371 } while (FindNextFile(h, &fd));
373 DWORD err = GetLastError();
375 if (err != ERROR_NO_MORE_FILES) {
377 ThrowError(path + ": Can't read directory: ");
383 Path::setDirectory(const std::string& a_path) {
384 if (a_path.size() == 0)
388 FlipBackSlashes(path);
389 size_t last = a_path.size() -1;
390 if (a_path[last] != '/')
400 Path::setFile(const std::string& a_path) {
401 if (a_path.size() == 0)
405 FlipBackSlashes(path);
406 size_t last = a_path.size() - 1;
407 while (last > 0 && a_path[last] == '/')
418 Path::appendDirectory(const std::string& dir) {
432 Path::elideDirectory() {
435 size_t slashpos = path.rfind('/',path.size());
436 if (slashpos == 0 || slashpos == std::string::npos)
438 if (slashpos == path.size() - 1)
439 slashpos = path.rfind('/',slashpos-1);
440 if (slashpos == std::string::npos)
442 path.erase(slashpos);
447 Path::appendFile(const std::string& file) {
463 size_t slashpos = path.rfind('/',path.size());
464 if (slashpos == std::string::npos)
466 path.erase(slashpos+1);
471 Path::appendSuffix(const std::string& suffix) {
485 Path::elideSuffix() {
486 if (isDirectory()) return false;
487 size_t dotpos = path.rfind('.',path.size());
488 size_t slashpos = path.rfind('/',path.size());
489 if (slashpos != std::string::npos && dotpos != std::string::npos &&
491 path.erase(dotpos, path.size()-dotpos);
499 Path::createDirectory( bool create_parents) {
500 // Make sure we're dealing with a directory
501 if (!isDirectory()) return false;
503 // Get a writeable copy of the path name
504 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
505 path.copy(pathname,path.length());
506 pathname[path.length()] = 0;
508 // Determine starting point for initial / search.
509 char *next = pathname;
510 if (pathname[0] == '/' && pathname[1] == '/') {
512 next = strchr(pathname+2, '/');
514 throw std::string(pathname) + ": badly formed remote directory";
516 next = strchr(next+1, '/');
518 throw std::string(pathname) + ": badly formed remote directory";
521 throw std::string(pathname) + ": badly formed remote directory";
523 if (pathname[1] == ':')
524 next += 2; // skip drive letter
526 next++; // skip root directory
529 // If we're supposed to create intermediate directories
530 if (create_parents) {
531 // Loop through the directory components until we're done
533 next = strchr(next, '/');
535 if (!CreateDirectory(pathname, NULL))
536 ThrowError(std::string(pathname) + ": Can't create directory: ");
540 // Drop trailing slash.
541 pathname[path.size()-1] = 0;
542 if (!CreateDirectory(pathname, NULL)) {
543 ThrowError(std::string(pathname) + ": Can't create directory: ");
551 // Make sure we're dealing with a file
552 if (!isFile()) return false;
555 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
556 FILE_ATTRIBUTE_NORMAL, NULL);
557 if (h == INVALID_HANDLE_VALUE)
558 ThrowError(path + ": Can't create file: ");
565 Path::destroyDirectory(bool remove_contents) const {
566 // Make sure we're dealing with a directory
567 if (!isDirectory()) return false;
569 // If it doesn't exist, we're done.
570 if (!exists()) return true;
572 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+2));
573 int lastchar = path.length() - 1 ;
574 path.copy(pathname,lastchar+2);
576 // Make path end with '/*'.
577 pathname[lastchar+1] = '*';
578 pathname[lastchar+2] = 0;
580 if (remove_contents) {
582 HANDLE h = FindFirstFile(pathname, &fd);
584 // It's a bad idea to alter the contents of a directory while enumerating
585 // its contents. So build a list of its contents first, then destroy them.
587 if (h != INVALID_HANDLE_VALUE) {
588 std::vector<Path> list;
591 if (strcmp(fd.cFileName, ".") == 0)
593 if (strcmp(fd.cFileName, "..") == 0)
596 Path aPath(path + &fd.cFileName[0]);
597 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
599 list.push_back(aPath);
600 } while (FindNextFile(h, &fd));
602 DWORD err = GetLastError();
604 if (err != ERROR_NO_MORE_FILES) {
606 ThrowError(path + ": Can't read directory: ");
609 for (std::vector<Path>::iterator I = list.begin(); I != list.end(); ++I) {
611 if (aPath.isDirectory())
612 aPath.destroyDirectory(true);
617 if (GetLastError() != ERROR_FILE_NOT_FOUND)
618 ThrowError(path + ": Can't read directory: ");
622 pathname[lastchar] = 0;
623 if (!RemoveDirectory(pathname))
624 ThrowError(std::string(pathname) + ": Can't destroy directory: ");
629 Path::destroyFile() const {
630 if (!isFile()) return false;
632 DWORD attr = GetFileAttributes(path.c_str());
634 // If it doesn't exist, we're done.
635 if (attr == INVALID_FILE_ATTRIBUTES)
638 // Read-only files cannot be deleted on Windows. Must remove the read-only
640 if (attr & FILE_ATTRIBUTE_READONLY) {
641 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
642 ThrowError(path + ": Can't destroy file: ");
645 if (!DeleteFile(path.c_str()))
646 ThrowError(path + ": Can't destroy file: ");
650 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
653 assert(len < 1024 && "Request for magic string too long");
654 char* buf = (char*) alloca(1 + len);
656 HANDLE h = CreateFile(path.c_str(),
661 FILE_ATTRIBUTE_NORMAL,
663 if (h == INVALID_HANDLE_VALUE)
667 BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
670 if (!ret || nRead != len)
679 Path::renameFile(const Path& newName) {
680 if (!isFile()) return false;
681 if (!MoveFile(path.c_str(), newName.c_str()))
682 ThrowError("Can't move '" + path +
683 "' to '" + newName.path + "': ");
688 Path::setStatusInfo(const StatusInfo& si) const {
689 if (!isFile()) return false;
691 HANDLE h = CreateFile(path.c_str(),
692 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
693 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
696 FILE_ATTRIBUTE_NORMAL,
698 if (h == INVALID_HANDLE_VALUE)
701 BY_HANDLE_FILE_INFORMATION bhfi;
702 if (!GetFileInformationByHandle(h, &bhfi)) {
703 DWORD err = GetLastError();
706 ThrowError(path + ": GetFileInformationByHandle: ");
710 (uint64_t&)ft = si.modTime.toWin32Time();
711 BOOL ret = SetFileTime(h, NULL, &ft, &ft);
712 DWORD err = GetLastError();
716 ThrowError(path + ": SetFileTime: ");
719 // Best we can do with Unix permission bits is to interpret the owner
721 if (si.mode & 0200) {
722 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
723 if (!SetFileAttributes(path.c_str(),
724 bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
725 ThrowError(path + ": SetFileAttributes: ");
728 if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
729 if (!SetFileAttributes(path.c_str(),
730 bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
731 ThrowError(path + ": SetFileAttributes: ");
739 sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) {
740 // Can't use CopyFile macro defined in Windows.h because it would mess up the
741 // above line. We use the expansion it would have in a non-UNICODE build.
742 if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
743 ThrowError("Can't copy '" + Src.toString() +
744 "' to '" + Dest.toString() + "': ");
748 Path::makeUnique(bool reuse_current) {
749 if (reuse_current && !exists())
750 return; // File doesn't exist already, just use it!
752 // Reserve space for -XXXXXX at the end.
753 char *FNBuffer = (char*) alloca(path.size()+8);
754 unsigned offset = path.size();
755 path.copy(FNBuffer, offset);
757 // Find a numeric suffix that isn't used by an existing file.
758 static unsigned FCounter = 0;
760 sprintf(FNBuffer+offset, "-%06u", FCounter);
761 if (++FCounter > 999999)
768 Path::createTemporaryFile(bool reuse_current) {
769 // Make sure we're dealing with a file
773 // Make this into a unique file name
774 makeUnique( reuse_current );
776 // Now go and create it
777 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
778 FILE_ATTRIBUTE_NORMAL, NULL);
779 if (h == INVALID_HANDLE_VALUE)