Remove sys::GetMainExecutable.
[oota-llvm.git] / lib / Support / Windows / Path.inc
1 //===- llvm/Support/Win32/Path.cpp - Win32 Path Implementation ---*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file provides the Win32 specific implementation of the Path class.
11 //
12 //===----------------------------------------------------------------------===//
13
14 //===----------------------------------------------------------------------===//
15 //=== WARNING: Implementation here must contain only generic Win32 code that
16 //===          is guaranteed to work on *all* Win32 variants.
17 //===----------------------------------------------------------------------===//
18
19 #include "Windows.h"
20 #include <cstdio>
21 #include <malloc.h>
22
23 // We need to undo a macro defined in Windows.h, otherwise we won't compile:
24 #undef GetCurrentDirectory
25
26 // Windows happily accepts either forward or backward slashes, though any path
27 // returned by a Win32 API will have backward slashes.  As LLVM code basically
28 // assumes forward slashes are used, backward slashs are converted where they
29 // can be introduced into a path.
30 //
31 // Another invariant is that a path ends with a slash if and only if the path
32 // is a root directory.  Any other use of a trailing slash is stripped.  Unlike
33 // in Unix, Windows has a rather complicated notion of a root path and this
34 // invariant helps simply the code.
35
36 static void FlipBackSlashes(std::string& s) {
37   for (size_t i = 0; i < s.size(); i++)
38     if (s[i] == '\\')
39       s[i] = '/';
40 }
41
42 namespace llvm {
43 namespace sys {
44
45 Path::Path(llvm::StringRef p)
46   : path(p) {
47   FlipBackSlashes(path);
48 }
49
50 Path::Path(const char *StrStart, unsigned StrLen)
51   : path(StrStart, StrLen) {
52   FlipBackSlashes(path);
53 }
54
55 Path&
56 Path::operator=(StringRef that) {
57   path.assign(that.data(), that.size());
58   FlipBackSlashes(path);
59   return *this;
60 }
61
62 bool
63 Path::isValid() const {
64   if (path.empty())
65     return false;
66
67   size_t len = path.size();
68   // If there is a null character, it and all its successors are ignored.
69   size_t pos = path.find_first_of('\0');
70   if (pos != std::string::npos)
71     len = pos;
72
73   // If there is a colon, it must be the second character, preceded by a letter
74   // and followed by something.
75   pos = path.rfind(':',len);
76   size_t rootslash = 0;
77   if (pos != std::string::npos) {
78     if (pos != 1 || !isalpha(static_cast<unsigned char>(path[0])) || len < 3)
79       return false;
80       rootslash = 2;
81   }
82
83   // Look for a UNC path, and if found adjust our notion of the root slash.
84   if (len > 3 && path[0] == '/' && path[1] == '/') {
85     rootslash = path.find('/', 2);
86     if (rootslash == std::string::npos)
87       rootslash = 0;
88   }
89
90   // Check for illegal characters.
91   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
92                          "\013\014\015\016\017\020\021\022\023\024\025\026"
93                          "\027\030\031\032\033\034\035\036\037")
94       != std::string::npos)
95     return false;
96
97   // Remove trailing slash, unless it's a root slash.
98   if (len > rootslash+1 && path[len-1] == '/')
99     path.erase(--len);
100
101   // Check each component for legality.
102   for (pos = 0; pos < len; ++pos) {
103     // A component may not end in a space.
104     if (path[pos] == ' ') {
105       if (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0')
106         return false;
107     }
108
109     // A component may not end in a period.
110     if (path[pos] == '.') {
111       if (pos+1 == len || path[pos+1] == '/') {
112         // Unless it is the pseudo-directory "."...
113         if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
114           return true;
115         // or "..".
116         if (pos > 0 && path[pos-1] == '.') {
117           if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
118             return true;
119         }
120         return false;
121       }
122     }
123   }
124
125   return true;
126 }
127
128 void Path::makeAbsolute() {
129   TCHAR  FullPath[MAX_PATH + 1] = {0};
130   LPTSTR FilePart = NULL;
131
132   DWORD RetLength = ::GetFullPathNameA(path.c_str(),
133                         sizeof(FullPath)/sizeof(FullPath[0]),
134                         FullPath, &FilePart);
135
136   if (0 == RetLength) {
137     // FIXME: Report the error GetLastError()
138     assert(0 && "Unable to make absolute path!");
139   } else if (RetLength > MAX_PATH) {
140     // FIXME: Report too small buffer (needed RetLength bytes).
141     assert(0 && "Unable to make absolute path!");
142   } else {
143     path = FullPath;
144   }
145 }
146
147 static Path *TempDirectory;
148
149 Path
150 Path::GetTemporaryDirectory(std::string* ErrMsg) {
151   if (TempDirectory) {
152 #if defined(_MSC_VER)
153     // Visual Studio gets confused and emits a diagnostic about calling exists,
154     // even though this is the implementation for PathV1.  Temporarily 
155     // disable the deprecated warning message
156     #pragma warning(push)
157     #pragma warning(disable:4996)
158 #endif
159     assert(TempDirectory->exists() && "Who has removed TempDirectory?");
160 #if defined(_MSC_VER)
161     #pragma warning(pop)
162 #endif
163     return *TempDirectory;
164   }
165
166   char pathname[MAX_PATH];
167   if (!GetTempPath(MAX_PATH, pathname)) {
168     if (ErrMsg)
169       *ErrMsg = "Can't determine temporary directory";
170     return Path();
171   }
172
173   Path result;
174   result.set(pathname);
175
176   // Append a subdirectory based on our process id so multiple LLVMs don't
177   // step on each other's toes.
178 #ifdef __MINGW32__
179   // Mingw's Win32 header files are broken.
180   sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId()));
181 #else
182   sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
183 #endif
184   result.appendComponent(pathname);
185
186   // If there's a directory left over from a previous LLVM execution that
187   // happened to have the same process id, get rid of it.
188   result.eraseFromDisk(true);
189
190   // And finally (re-)create the empty directory.
191   result.createDirectoryOnDisk(false);
192   TempDirectory = new Path(result);
193   return *TempDirectory;
194 }
195
196 Path
197 Path::GetCurrentDirectory() {
198   char pathname[MAX_PATH];
199   ::GetCurrentDirectoryA(MAX_PATH,pathname);
200   return Path(pathname);
201 }
202
203 // FIXME: the above set of functions don't map to Windows very well.
204
205 bool
206 Path::exists() const {
207   DWORD attr = GetFileAttributes(path.c_str());
208   return attr != INVALID_FILE_ATTRIBUTES;
209 }
210
211 bool
212 Path::isDirectory() const {
213   DWORD attr = GetFileAttributes(path.c_str());
214   return (attr != INVALID_FILE_ATTRIBUTES) &&
215          (attr & FILE_ATTRIBUTE_DIRECTORY);
216 }
217
218 bool
219 Path::isSymLink() const {
220   DWORD attributes = GetFileAttributes(path.c_str());
221
222   if (attributes == INVALID_FILE_ATTRIBUTES)
223     // There's no sane way to report this :(.
224     assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES");
225
226   // This isn't exactly what defines a NTFS symlink, but it is only true for
227   // paths that act like a symlink.
228   return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
229 }
230
231 bool
232 Path::isRegularFile() const {
233   bool res;
234   if (fs::is_regular_file(path, res))
235     return false;
236   return res;
237 }
238
239 bool Path::makeReadableOnDisk(std::string* ErrMsg) {
240   // All files are readable on Windows (ignoring security attributes).
241   return false;
242 }
243
244 bool Path::makeWriteableOnDisk(std::string* ErrMsg) {
245   DWORD attr = GetFileAttributes(path.c_str());
246
247   // If it doesn't exist, we're done.
248   if (attr == INVALID_FILE_ATTRIBUTES)
249     return false;
250
251   if (attr & FILE_ATTRIBUTE_READONLY) {
252     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) {
253       MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: ");
254       return true;
255     }
256   }
257   return false;
258 }
259
260 bool
261 Path::set(StringRef a_path) {
262   if (a_path.empty())
263     return false;
264   std::string save(path);
265   path = a_path;
266   FlipBackSlashes(path);
267   if (!isValid()) {
268     path = save;
269     return false;
270   }
271   return true;
272 }
273
274 bool
275 Path::appendComponent(StringRef name) {
276   if (name.empty())
277     return false;
278   std::string save(path);
279   if (!path.empty()) {
280     size_t last = path.size() - 1;
281     if (path[last] != '/')
282       path += '/';
283   }
284   path += name;
285   if (!isValid()) {
286     path = save;
287     return false;
288   }
289   return true;
290 }
291
292 bool
293 Path::eraseComponent() {
294   size_t slashpos = path.rfind('/',path.size());
295   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
296     return false;
297   std::string save(path);
298   path.erase(slashpos);
299   if (!isValid()) {
300     path = save;
301     return false;
302   }
303   return true;
304 }
305
306 bool
307 Path::eraseSuffix() {
308   size_t dotpos = path.rfind('.',path.size());
309   size_t slashpos = path.rfind('/',path.size());
310   if (dotpos != std::string::npos) {
311     if (slashpos == std::string::npos || dotpos > slashpos+1) {
312       std::string save(path);
313       path.erase(dotpos, path.size()-dotpos);
314       if (!isValid()) {
315         path = save;
316         return false;
317       }
318       return true;
319     }
320   }
321   return false;
322 }
323
324 inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) {
325   if (ErrMsg)
326     *ErrMsg = std::string(pathname) + ": " + std::string(msg);
327   return true;
328 }
329
330 bool
331 Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) {
332   // Get a writeable copy of the path name
333   size_t len = path.length();
334   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
335   path.copy(pathname, len);
336   pathname[len] = 0;
337
338   // Make sure it ends with a slash.
339   if (len == 0 || pathname[len - 1] != '/') {
340     pathname[len] = '/';
341     pathname[++len] = 0;
342   }
343
344   // Determine starting point for initial / search.
345   char *next = pathname;
346   if (pathname[0] == '/' && pathname[1] == '/') {
347     // Skip host name.
348     next = strchr(pathname+2, '/');
349     if (next == NULL)
350       return PathMsg(ErrMsg, pathname, "badly formed remote directory");
351
352     // Skip share name.
353     next = strchr(next+1, '/');
354     if (next == NULL)
355       return PathMsg(ErrMsg, pathname,"badly formed remote directory");
356
357     next++;
358     if (*next == 0)
359       return PathMsg(ErrMsg, pathname, "badly formed remote directory");
360
361   } else {
362     if (pathname[1] == ':')
363       next += 2;    // skip drive letter
364     if (*next == '/')
365       next++;       // skip root directory
366   }
367
368   // If we're supposed to create intermediate directories
369   if (create_parents) {
370     // Loop through the directory components until we're done
371     while (*next) {
372       next = strchr(next, '/');
373       *next = 0;
374       if (!CreateDirectory(pathname, NULL) &&
375           GetLastError() != ERROR_ALREADY_EXISTS)
376           return MakeErrMsg(ErrMsg,
377             std::string(pathname) + ": Can't create directory: ");
378       *next++ = '/';
379     }
380   } else {
381     // Drop trailing slash.
382     pathname[len-1] = 0;
383     if (!CreateDirectory(pathname, NULL) &&
384         GetLastError() != ERROR_ALREADY_EXISTS) {
385       return MakeErrMsg(ErrMsg, std::string(pathname) +
386                         ": Can't create directory: ");
387     }
388   }
389   return false;
390 }
391
392 bool
393 Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
394   WIN32_FILE_ATTRIBUTE_DATA fi;
395   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
396     return true;
397
398   if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
399     // If it doesn't exist, we're done.
400     bool Exists;
401     if (fs::exists(path, Exists) || !Exists)
402       return false;
403
404     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
405     int lastchar = path.length() - 1 ;
406     path.copy(pathname, lastchar+1);
407
408     // Make path end with '/*'.
409     if (pathname[lastchar] != '/')
410       pathname[++lastchar] = '/';
411     pathname[lastchar+1] = '*';
412     pathname[lastchar+2] = 0;
413
414     if (remove_contents) {
415       WIN32_FIND_DATA fd;
416       HANDLE h = FindFirstFile(pathname, &fd);
417
418       // It's a bad idea to alter the contents of a directory while enumerating
419       // its contents. So build a list of its contents first, then destroy them.
420
421       if (h != INVALID_HANDLE_VALUE) {
422         std::vector<Path> list;
423
424         do {
425           if (strcmp(fd.cFileName, ".") == 0)
426             continue;
427           if (strcmp(fd.cFileName, "..") == 0)
428             continue;
429
430           Path aPath(path);
431           aPath.appendComponent(&fd.cFileName[0]);
432           list.push_back(aPath);
433         } while (FindNextFile(h, &fd));
434
435         DWORD err = GetLastError();
436         FindClose(h);
437         if (err != ERROR_NO_MORE_FILES) {
438           SetLastError(err);
439           return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
440         }
441
442         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
443              ++I) {
444           Path &aPath = *I;
445           aPath.eraseFromDisk(true);
446         }
447       } else {
448         if (GetLastError() != ERROR_FILE_NOT_FOUND)
449           return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
450       }
451     }
452
453     pathname[lastchar] = 0;
454     if (!RemoveDirectory(pathname))
455       return MakeErrMsg(ErrStr,
456         std::string(pathname) + ": Can't destroy directory: ");
457     return false;
458   } else {
459     // Read-only files cannot be deleted on Windows.  Must remove the read-only
460     // attribute first.
461     if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
462       if (!SetFileAttributes(path.c_str(),
463                              fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
464         return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
465     }
466
467     if (!DeleteFile(path.c_str()))
468       return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
469     return false;
470   }
471 }
472
473 bool
474 Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) {
475   if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING))
476     return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path
477         + "': ");
478   return false;
479 }
480
481 bool
482 Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const {
483   // FIXME: should work on directories also.
484   if (!si.isFile) {
485     return true;
486   }
487
488   HANDLE h = CreateFile(path.c_str(),
489                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
490                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
491                         NULL,
492                         OPEN_EXISTING,
493                         FILE_ATTRIBUTE_NORMAL,
494                         NULL);
495   if (h == INVALID_HANDLE_VALUE)
496     return true;
497
498   BY_HANDLE_FILE_INFORMATION bhfi;
499   if (!GetFileInformationByHandle(h, &bhfi)) {
500     DWORD err = GetLastError();
501     CloseHandle(h);
502     SetLastError(err);
503     return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: ");
504   }
505
506   ULARGE_INTEGER ui;
507   ui.QuadPart = si.modTime.toWin32Time();
508   FILETIME ft;
509   ft.dwLowDateTime = ui.LowPart;
510   ft.dwHighDateTime = ui.HighPart;
511   BOOL ret = SetFileTime(h, NULL, &ft, &ft);
512   DWORD err = GetLastError();
513   CloseHandle(h);
514   if (!ret) {
515     SetLastError(err);
516     return MakeErrMsg(ErrMsg, path + ": SetFileTime: ");
517   }
518
519   // Best we can do with Unix permission bits is to interpret the owner
520   // writable bit.
521   if (si.mode & 0200) {
522     if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
523       if (!SetFileAttributes(path.c_str(),
524               bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
525         return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
526     }
527   } else {
528     if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
529       if (!SetFileAttributes(path.c_str(),
530               bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
531         return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
532     }
533   }
534
535   return false;
536 }
537
538 bool
539 Path::makeUnique(bool reuse_current, std::string* ErrMsg) {
540   bool Exists;
541   if (reuse_current && (fs::exists(path, Exists) || !Exists))
542     return false; // File doesn't exist already, just use it!
543
544   // Reserve space for -XXXXXX at the end.
545   char *FNBuffer = (char*) alloca(path.size()+8);
546   unsigned offset = path.size();
547   path.copy(FNBuffer, offset);
548
549   // Find a numeric suffix that isn't used by an existing file.  Assume there
550   // won't be more than 1 million files with the same prefix.  Probably a safe
551   // bet.
552   static int FCounter = -1;
553   if (FCounter < 0) {
554     // Give arbitrary initial seed.
555     // FIXME: We should use sys::fs::unique_file() in future.
556     LARGE_INTEGER cnt64;
557     DWORD x = GetCurrentProcessId();
558     x = (x << 16) | (x >> 16);
559     if (QueryPerformanceCounter(&cnt64))    // RDTSC
560       x ^= cnt64.HighPart ^ cnt64.LowPart;
561     FCounter = x % 1000000;
562   }
563   do {
564     sprintf(FNBuffer+offset, "-%06u", FCounter);
565     if (++FCounter > 999999)
566       FCounter = 0;
567     path = FNBuffer;
568   } while (!fs::exists(path, Exists) && Exists);
569   return false;
570 }
571
572 bool
573 Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
574   // Make this into a unique file name
575   makeUnique(reuse_current, ErrMsg);
576
577   // Now go and create it
578   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
579                         FILE_ATTRIBUTE_NORMAL, NULL);
580   if (h == INVALID_HANDLE_VALUE)
581     return MakeErrMsg(ErrMsg, path + ": can't create file");
582
583   CloseHandle(h);
584   return false;
585 }
586 }
587 }