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.readable())
143 Paths.push_back(tmpPath);
145 delim = strchr(at, ';');
148 if (tmpPath.setDirectory(std::string(at)))
149 if (tmpPath.readable())
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.readable())
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 return (isValid() && path[path.length()-1] != '/');
202 Path::isDirectory() const {
203 return (isValid() && path[path.length()-1] == '/');
207 Path::getBasename() const {
208 // Find the last slash
209 size_t slash = path.rfind('/');
210 if (slash == std::string::npos)
215 return path.substr(slash, path.rfind('.'));
218 bool Path::hasMagicNumber(const std::string &Magic) const {
219 std::string actualMagic;
220 if (getMagicNumber(actualMagic, Magic.size()))
221 return Magic == actualMagic;
226 Path::isBytecodeFile() const {
227 std::string actualMagic;
228 if (!getMagicNumber(actualMagic, 4))
230 return actualMagic == "llvc" || actualMagic == "llvm";
234 Path::exists() const {
235 DWORD attr = GetFileAttributes(path.c_str());
236 return attr != INVALID_FILE_ATTRIBUTES;
240 Path::readable() const {
241 // FIXME: take security attributes into account.
242 DWORD attr = GetFileAttributes(path.c_str());
243 return attr != INVALID_FILE_ATTRIBUTES;
247 Path::writable() const {
248 // FIXME: take security attributes into account.
249 DWORD attr = GetFileAttributes(path.c_str());
250 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
254 Path::executable() const {
255 // FIXME: take security attributes into account.
256 DWORD attr = GetFileAttributes(path.c_str());
257 return attr != INVALID_FILE_ATTRIBUTES;
261 Path::getLast() const {
262 // Find the last slash
263 size_t pos = path.rfind('/');
265 // Handle the corner cases
266 if (pos == std::string::npos)
269 // If the last character is a slash
270 if (pos == path.length()-1) {
271 // Find the second to last slash
272 size_t pos2 = path.rfind('/', pos-1);
273 if (pos2 == std::string::npos)
274 return path.substr(0,pos);
276 return path.substr(pos2+1,pos-pos2-1);
278 // Return everything after the last slash
279 return path.substr(pos+1);
283 Path::getStatusInfo(StatusInfo& info) const {
284 WIN32_FILE_ATTRIBUTE_DATA fi;
285 if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
286 ThrowError(std::string(path) + ": Can't get status: ");
288 info.fileSize = fi.nFileSizeHigh;
289 info.fileSize <<= 32;
290 info.fileSize += fi.nFileSizeLow;
292 info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
293 info.user = 9999; // Not applicable to Windows, so...
294 info.group = 9999; // Not applicable to Windows, so...
296 __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
297 info.modTime.fromWin32Time(ft);
299 info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
300 if (info.isDir && path[path.length() - 1] != '/')
302 else if (!info.isDir && path[path.length() - 1] == '/')
303 path.erase(path.length() - 1);
306 static bool AddPermissionBits(const std::string& Filename, int bits) {
307 DWORD attr = GetFileAttributes(Filename.c_str());
309 // If it doesn't exist, we're done.
310 if (attr == INVALID_FILE_ATTRIBUTES)
313 // The best we can do to interpret Unix permission bits is to use
314 // the owner writable bit.
315 if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
316 if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
317 ThrowError(Filename + ": SetFileAttributes: ");
322 void Path::makeReadable() {
323 // All files are readable on Windows (ignoring security attributes).
326 void Path::makeWriteable() {
327 DWORD attr = GetFileAttributes(path.c_str());
329 // If it doesn't exist, we're done.
330 if (attr == INVALID_FILE_ATTRIBUTES)
333 if (attr & FILE_ATTRIBUTE_READONLY) {
334 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
335 ThrowError(std::string(path) + ": Can't make file writable: ");
339 void Path::makeExecutable() {
340 // All files are executable on Windows (ignoring security attributes).
344 Path::getDirectoryContents(std::set<Path>& result) const {
350 std::string searchpath = path + "*";
351 HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
352 if (h == INVALID_HANDLE_VALUE) {
353 if (GetLastError() == ERROR_FILE_NOT_FOUND)
354 return true; // not really an error, now is it?
355 ThrowError(path + ": Can't read directory: ");
359 if (fd.cFileName[0] == '.')
361 Path aPath(path + &fd.cFileName[0]);
362 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
364 result.insert(aPath);
365 } while (FindNextFile(h, &fd));
367 DWORD err = GetLastError();
369 if (err != ERROR_NO_MORE_FILES) {
371 ThrowError(path + ": Can't read directory: ");
377 Path::setDirectory(const std::string& a_path) {
378 if (a_path.size() == 0)
382 FlipBackSlashes(path);
383 size_t last = a_path.size() -1;
384 if (a_path[last] != '/')
394 Path::setFile(const std::string& a_path) {
395 if (a_path.size() == 0)
399 FlipBackSlashes(path);
400 size_t last = a_path.size() - 1;
401 while (last > 0 && a_path[last] == '/')
412 Path::appendDirectory(const std::string& dir) {
426 Path::elideDirectory() {
429 size_t slashpos = path.rfind('/',path.size());
430 if (slashpos == 0 || slashpos == std::string::npos)
432 if (slashpos == path.size() - 1)
433 slashpos = path.rfind('/',slashpos-1);
434 if (slashpos == std::string::npos)
436 path.erase(slashpos);
441 Path::appendFile(const std::string& file) {
457 size_t slashpos = path.rfind('/',path.size());
458 if (slashpos == std::string::npos)
460 path.erase(slashpos+1);
465 Path::appendSuffix(const std::string& suffix) {
479 Path::elideSuffix() {
480 if (isDirectory()) return false;
481 size_t dotpos = path.rfind('.',path.size());
482 size_t slashpos = path.rfind('/',path.size());
483 if (slashpos != std::string::npos && dotpos != std::string::npos &&
485 path.erase(dotpos, path.size()-dotpos);
493 Path::createDirectory( bool create_parents) {
494 // Make sure we're dealing with a directory
495 if (!isDirectory()) return false;
497 // Get a writeable copy of the path name
498 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
499 path.copy(pathname,path.length());
500 pathname[path.length()] = 0;
502 // Determine starting point for initial / search.
503 char *next = pathname;
504 if (pathname[0] == '/' && pathname[1] == '/') {
506 next = strchr(pathname+2, '/');
508 throw std::string(pathname) + ": badly formed remote directory";
510 next = strchr(next+1, '/');
512 throw std::string(pathname) + ": badly formed remote directory";
515 throw std::string(pathname) + ": badly formed remote directory";
517 if (pathname[1] == ':')
518 next += 2; // skip drive letter
520 next++; // skip root directory
523 // If we're supposed to create intermediate directories
524 if (create_parents) {
525 // Loop through the directory components until we're done
527 next = strchr(next, '/');
529 if (!CreateDirectory(pathname, NULL))
530 ThrowError(std::string(pathname) + ": Can't create directory: ");
534 // Drop trailing slash.
535 pathname[path.size()-1] = 0;
536 if (!CreateDirectory(pathname, NULL)) {
537 ThrowError(std::string(pathname) + ": Can't create directory: ");
545 // Make sure we're dealing with a file
546 if (!isFile()) return false;
549 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
550 FILE_ATTRIBUTE_NORMAL, NULL);
551 if (h == INVALID_HANDLE_VALUE)
552 ThrowError(path + ": Can't create file: ");
559 Path::destroyDirectory(bool remove_contents) const {
560 // Make sure we're dealing with a directory
561 if (!isDirectory()) return false;
563 // If it doesn't exist, we're done.
564 if (!exists()) return true;
566 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+2));
567 int lastchar = path.length() - 1 ;
568 path.copy(pathname,lastchar+2);
570 // Make path end with '/*'.
571 pathname[lastchar+1] = '*';
572 pathname[lastchar+2] = 0;
574 if (remove_contents) {
576 HANDLE h = FindFirstFile(pathname, &fd);
578 // It's a bad idea to alter the contents of a directory while enumerating
579 // its contents. So build a list of its contents first, then destroy them.
581 if (h != INVALID_HANDLE_VALUE) {
582 std::vector<Path> list;
585 if (strcmp(fd.cFileName, ".") == 0)
587 if (strcmp(fd.cFileName, "..") == 0)
590 Path aPath(path + &fd.cFileName[0]);
591 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
593 list.push_back(aPath);
594 } while (FindNextFile(h, &fd));
596 DWORD err = GetLastError();
598 if (err != ERROR_NO_MORE_FILES) {
600 ThrowError(path + ": Can't read directory: ");
603 for (std::vector<Path>::iterator I = list.begin(); I != list.end(); ++I) {
605 if (aPath.isDirectory())
606 aPath.destroyDirectory(true);
611 if (GetLastError() != ERROR_FILE_NOT_FOUND)
612 ThrowError(path + ": Can't read directory: ");
616 pathname[lastchar] = 0;
617 if (!RemoveDirectory(pathname))
618 ThrowError(std::string(pathname) + ": Can't destroy directory: ");
623 Path::destroyFile() const {
624 if (!isFile()) return false;
626 DWORD attr = GetFileAttributes(path.c_str());
628 // If it doesn't exist, we're done.
629 if (attr == INVALID_FILE_ATTRIBUTES)
632 // Read-only files cannot be deleted on Windows. Must remove the read-only
634 if (attr & FILE_ATTRIBUTE_READONLY) {
635 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
636 ThrowError(path + ": Can't destroy file: ");
639 if (!DeleteFile(path.c_str()))
640 ThrowError(path + ": Can't destroy file: ");
644 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
647 assert(len < 1024 && "Request for magic string too long");
648 char* buf = (char*) alloca(1 + len);
650 HANDLE h = CreateFile(path.c_str(),
655 FILE_ATTRIBUTE_NORMAL,
657 if (h == INVALID_HANDLE_VALUE)
661 BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
664 if (!ret || nRead != len)
673 Path::renameFile(const Path& newName) {
674 if (!isFile()) return false;
675 if (!MoveFile(path.c_str(), newName.c_str()))
676 ThrowError("Can't move '" + path +
677 "' to '" + newName.path + "': ");
682 Path::setStatusInfo(const StatusInfo& si) const {
683 if (!isFile()) return false;
685 HANDLE h = CreateFile(path.c_str(),
686 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
687 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
690 FILE_ATTRIBUTE_NORMAL,
692 if (h == INVALID_HANDLE_VALUE)
695 BY_HANDLE_FILE_INFORMATION bhfi;
696 if (!GetFileInformationByHandle(h, &bhfi)) {
697 DWORD err = GetLastError();
700 ThrowError(path + ": GetFileInformationByHandle: ");
704 (uint64_t&)ft = si.modTime.toWin32Time();
705 BOOL ret = SetFileTime(h, NULL, &ft, &ft);
706 DWORD err = GetLastError();
710 ThrowError(path + ": SetFileTime: ");
713 // Best we can do with Unix permission bits is to interpret the owner
715 if (si.mode & 0200) {
716 if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
717 if (!SetFileAttributes(path.c_str(),
718 bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
719 ThrowError(path + ": SetFileAttributes: ");
722 if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
723 if (!SetFileAttributes(path.c_str(),
724 bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
725 ThrowError(path + ": SetFileAttributes: ");
733 sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) {
734 // Can't use CopyFile macro defined in Windows.h because it would mess up the
735 // above line. We use the expansion it would have in a non-UNICODE build.
736 if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
737 ThrowError("Can't copy '" + Src.toString() +
738 "' to '" + Dest.toString() + "': ");
742 Path::makeUnique(bool reuse_current) {
743 if (reuse_current && !exists())
744 return; // File doesn't exist already, just use it!
746 // Reserve space for -XXXXXX at the end.
747 char *FNBuffer = (char*) alloca(path.size()+8);
748 unsigned offset = path.size();
749 path.copy(FNBuffer, offset);
751 // Find a numeric suffix that isn't used by an existing file.
752 static unsigned FCounter = 0;
754 sprintf(FNBuffer+offset, "-%06u", FCounter);
755 if (++FCounter > 999999)
762 Path::createTemporaryFile(bool reuse_current) {
763 // Make sure we're dealing with a file
767 // Make this into a unique file name
768 makeUnique( reuse_current );
770 // Now go and create it
771 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
772 FILE_ATTRIBUTE_NORMAL, NULL);
773 if (h == INVALID_HANDLE_VALUE)
783 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab