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