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 //===----------------------------------------------------------------------===//
23 #include <llvm/System/Path.h>
27 static void FlipBackSlashes(std::string& s) {
28 for (size_t i = 0; i < s.size(); i++)
37 Path::isValid() const {
41 // If there is a colon, it must be the second character, preceded by a letter
42 // and followed by something.
43 size_t len = path.size();
44 size_t pos = path.rfind(':',len);
45 if (pos != std::string::npos) {
46 if (pos != 1 || !isalpha(path[0]) || len < 3)
50 // Check for illegal characters.
51 if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
52 "\013\014\015\016\017\020\021\022\023\024\025\026"
53 "\027\030\031\032\033\034\035\036\037")
57 // A file or directory name may not end in a period.
58 if (path[len-1] == '.')
60 if (len >= 2 && path[len-2] == '.' && path[len-1] == '/')
63 // A file or directory name may not end in a space.
64 if (path[len-1] == ' ')
66 if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/')
72 static Path *TempDirectory = NULL;
75 Path::GetTemporaryDirectory() {
77 return *TempDirectory;
79 char pathname[MAX_PATH];
80 if (!GetTempPath(MAX_PATH, pathname))
81 throw std::string("Can't determine temporary directory");
84 result.setDirectory(pathname);
86 // Append a subdirectory passed on our process id so multiple LLVMs don't
87 // step on each other's toes.
88 sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
89 result.appendDirectory(pathname);
91 // If there's a directory left over from a previous LLVM execution that
92 // happened to have the same process id, get rid of it.
93 result.destroyDirectory(true);
95 // And finally (re-)create the empty directory.
96 result.createDirectory(false);
97 TempDirectory = new Path(result);
98 return *TempDirectory;
101 Path::Path(std::string unverified_path)
102 : path(unverified_path)
104 FlipBackSlashes(path);
105 if (unverified_path.empty())
111 throw std::string(unverified_path + ": path is not valid");
114 // FIXME: the following set of functions don't map to Windows very well.
116 Path::GetRootDirectory() {
118 result.setDirectory("/");
123 Path::GetDLLSuffix() {
127 static inline bool IsLibrary(Path& path, const std::string& basename) {
128 if (path.appendFile(std::string("lib") + basename)) {
129 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
131 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
133 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
135 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
137 } else if (path.elideFile() && path.appendFile(basename)) {
138 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
140 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
142 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
144 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
152 Path::GetLibraryPath(const std::string& basename,
153 const std::vector<std::string>& LibPaths) {
156 // Try the paths provided
157 for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
158 E = LibPaths.end(); I != E; ++I ) {
159 if (result.setDirectory(*I) && IsLibrary(result,basename))
163 // Try the LLVM lib directory in the LLVM install area
164 //if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
168 if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
172 if (result.setDirectory("/lib/") && IsLibrary(result,basename))
175 // Can't find it, give up and return invalid path.
181 Path::GetSystemLibraryPath1() {
182 return Path("/lib/");
186 Path::GetSystemLibraryPath2() {
187 return Path("/usr/lib/");
191 Path::GetLLVMDefaultConfigDir() {
192 return Path("/etc/llvm/");
196 Path::GetLLVMConfigDir() {
197 return GetLLVMDefaultConfigDir();
201 Path::GetUserHomeDirectory() {
202 const char* home = getenv("HOME");
205 if (result.setDirectory(home))
208 return GetRootDirectory();
210 // FIXME: the above set of functions don't map to Windows very well.
213 Path::isFile() const {
214 return (isValid() && path[path.length()-1] != '/');
218 Path::isDirectory() const {
219 return (isValid() && path[path.length()-1] == '/');
223 Path::getBasename() const {
224 // Find the last slash
225 size_t slash = path.rfind('/');
226 if (slash == std::string::npos)
231 return path.substr(slash, path.rfind('.'));
234 bool Path::hasMagicNumber(const std::string &Magic) const {
235 size_t len = Magic.size();
236 char *buf = reinterpret_cast<char *>(_alloca(len+1));
237 std::ifstream f(path.c_str());
244 Path::isBytecodeFile() const {
246 return hasMagicNumber("llvm");
252 Path::isArchive() const {
254 return hasMagicNumber("!<arch>\012");
260 Path::exists() const {
261 DWORD attr = GetFileAttributes(path.c_str());
262 return attr != INVALID_FILE_ATTRIBUTES;
266 Path::readable() const {
267 // FIXME: take security attributes into account.
268 DWORD attr = GetFileAttributes(path.c_str());
269 return attr != INVALID_FILE_ATTRIBUTES;
273 Path::writable() const {
274 // FIXME: take security attributes into account.
275 DWORD attr = GetFileAttributes(path.c_str());
276 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
280 Path::executable() const {
281 // FIXME: take security attributes into account.
282 DWORD attr = GetFileAttributes(path.c_str());
283 return attr != INVALID_FILE_ATTRIBUTES;
287 Path::getLast() const {
288 // Find the last slash
289 size_t pos = path.rfind('/');
291 // Handle the corner cases
292 if (pos == std::string::npos)
295 // If the last character is a slash
296 if (pos == path.length()-1) {
297 // Find the second to last slash
298 size_t pos2 = path.rfind('/', pos-1);
299 if (pos2 == std::string::npos)
300 return path.substr(0,pos);
302 return path.substr(pos2+1,pos-pos2-1);
304 // Return everything after the last slash
305 return path.substr(pos+1);
309 Path::setDirectory(const std::string& a_path) {
310 if (a_path.size() == 0)
314 FlipBackSlashes(path);
315 size_t last = a_path.size() -1;
316 if (last != 0 && a_path[last] != '/')
326 Path::setFile(const std::string& a_path) {
327 if (a_path.size() == 0)
331 FlipBackSlashes(path);
332 size_t last = a_path.size() - 1;
333 while (last > 0 && a_path[last] == '/')
344 Path::appendDirectory(const std::string& dir) {
358 Path::elideDirectory() {
361 size_t slashpos = path.rfind('/',path.size());
362 if (slashpos == 0 || slashpos == std::string::npos)
364 if (slashpos == path.size() - 1)
365 slashpos = path.rfind('/',slashpos-1);
366 if (slashpos == std::string::npos)
368 path.erase(slashpos);
373 Path::appendFile(const std::string& file) {
389 size_t slashpos = path.rfind('/',path.size());
390 if (slashpos == std::string::npos)
392 path.erase(slashpos+1);
397 Path::appendSuffix(const std::string& suffix) {
411 Path::elideSuffix() {
412 if (isDirectory()) return false;
413 size_t dotpos = path.rfind('.',path.size());
414 size_t slashpos = path.rfind('/',path.size());
415 if (slashpos != std::string::npos && dotpos != std::string::npos &&
417 path.erase(dotpos, path.size()-dotpos);
425 Path::createDirectory( bool create_parents) {
426 // Make sure we're dealing with a directory
427 if (!isDirectory()) return false;
429 // Get a writeable copy of the path name
430 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
431 path.copy(pathname,path.length());
432 pathname[path.length()] = 0;
434 // Determine starting point for initial / search.
435 char *next = pathname;
436 if (pathname[0] == '/' && pathname[1] == '/') {
438 next = strchr(pathname+2, '/');
440 throw std::string(pathname) + ": badly formed remote directory";
442 next = strchr(next+1, '/');
444 throw std::string(pathname) + ": badly formed remote directory";
447 throw std::string(pathname) + ": badly formed remote directory";
449 if (pathname[1] == ':')
450 next += 2; // skip drive letter
452 next++; // skip root directory
455 // If we're supposed to create intermediate directories
456 if (create_parents) {
457 // Loop through the directory components until we're done
459 next = strchr(next, '/');
461 if (!CreateDirectory(pathname, NULL))
462 ThrowError(std::string(pathname) + ": Can't create directory: ");
466 // Drop trailing slash.
467 pathname[path.size()-1] = 0;
468 if (!CreateDirectory(pathname, NULL)) {
469 ThrowError(std::string(pathname) + ": Can't create directory: ");
477 // Make sure we're dealing with a file
478 if (!isFile()) return false;
481 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
482 FILE_ATTRIBUTE_NORMAL, NULL);
483 if (h == INVALID_HANDLE_VALUE)
484 ThrowError(std::string(path.c_str()) + ": Can't create file: ");
491 Path::destroyDirectory(bool remove_contents) {
492 // Make sure we're dealing with a directory
493 if (!isDirectory()) return false;
495 // If it doesn't exist, we're done.
496 if (!exists()) return true;
498 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
499 path.copy(pathname,path.length()+1);
500 int lastchar = path.length() - 1 ;
501 if (pathname[lastchar] == '/')
502 pathname[lastchar] = 0;
504 if (remove_contents) {
505 // Recursively descend the directory to remove its content
506 // FIXME: The correct way of doing this on Windows isn't pretty...
507 // but this may work if unix-like utils are present.
508 std::string cmd("rm -rf ");
512 // Otherwise, try to just remove the one directory
513 if (!RemoveDirectory(pathname))
514 ThrowError(std::string(pathname) + ": Can't destroy directory: ");
520 Path::destroyFile() {
521 if (!isFile()) return false;
523 DWORD attr = GetFileAttributes(path.c_str());
525 // If it doesn't exist, we're done.
526 if (attr == INVALID_FILE_ATTRIBUTES)
529 // Read-only files cannot be deleted on Windows. Must remove the read-only
531 if (attr & FILE_ATTRIBUTE_READONLY) {
532 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
533 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
536 if (!DeleteFile(path.c_str()))
537 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
544 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab