Ensure that functions like isDirectory don't fail if the file doesn't
[oota-llvm.git] / lib / System / Win32 / Path.inc
1 //===- llvm/System/Linux/Path.cpp - Linux Path Implementation ---*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
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.
7 //
8 // Modified by Henrik Bach to comply with at least MinGW.
9 // Ported to Win32 by Jeff Cohen.
10 //
11 //===----------------------------------------------------------------------===//
12 //
13 // This file provides the Win32 specific implementation of the Path class.
14 //
15 //===----------------------------------------------------------------------===//
16
17 //===----------------------------------------------------------------------===//
18 //=== WARNING: Implementation here must contain only generic Win32 code that
19 //===          is guaranteed to work on *all* Win32 variants.
20 //===----------------------------------------------------------------------===//
21
22 #include "Win32.h"
23 #include <malloc.h>
24
25 // We need to undo a macro defined in Windows.h, otherwise we won't compile:
26 #undef CopyFile
27
28 static void FlipBackSlashes(std::string& s) {
29   for (size_t i = 0; i < s.size(); i++)
30     if (s[i] == '\\')
31       s[i] = '/';
32 }
33
34 namespace llvm {
35 namespace sys {
36
37 bool
38 Path::isValid() const {
39   if (path.empty())
40     return false;
41
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   size_t rootslash = 0;
47   if (pos != std::string::npos) {
48     if (pos != 1 || !isalpha(path[0]) || len < 3)
49       return false;
50       rootslash = 2;
51   }
52
53   // Look for a UNC path, and if found adjust our notion of the root slash.
54   if (len > 3 && path[0] == '/' && path[1] == '/') {
55     rootslash = path.find('/', 2);
56     if (rootslash == std::string::npos)
57       rootslash = 0;
58   }
59
60   // Check for illegal characters.
61   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
62                          "\013\014\015\016\017\020\021\022\023\024\025\026"
63                          "\027\030\031\032\033\034\035\036\037")
64       != std::string::npos)
65     return false;
66
67   // Remove trailing slash, unless it's a root slash.
68   if (len > rootslash+1 && path[len-1] == '/')
69     path.erase(--len);
70
71   // Check each component for legality.
72   for (pos = 0; pos < len; ++pos) {
73     // A component may not end in a space.
74     if (path[pos] == ' ') {
75       if (path[pos+1] == '/' || path[pos+1] == '\0')
76         return false;
77     }
78
79     // A component may not end in a period.
80     if (path[pos] == '.') {
81       if (path[pos+1] == '/' || path[pos+1] == '\0') {
82         // Unless it is the pseudo-directory "."...
83         if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
84           return true;
85         // or "..".
86         if (pos > 0 && path[pos-1] == '.') {
87           if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
88             return true;
89         }
90         return false;
91       }
92     }
93   }
94
95   return true;
96 }
97
98 static Path *TempDirectory = NULL;
99
100 Path
101 Path::GetTemporaryDirectory() {
102   if (TempDirectory)
103     return *TempDirectory;
104
105   char pathname[MAX_PATH];
106   if (!GetTempPath(MAX_PATH, pathname))
107     throw std::string("Can't determine temporary directory");
108
109   Path result;
110   result.set(pathname);
111
112   // Append a subdirectory passed on our process id so multiple LLVMs don't
113   // step on each other's toes.
114   sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
115   result.appendComponent(pathname);
116
117   // If there's a directory left over from a previous LLVM execution that
118   // happened to have the same process id, get rid of it.
119   result.eraseFromDisk(true);
120
121   // And finally (re-)create the empty directory.
122   result.createDirectoryOnDisk(false);
123   TempDirectory = new Path(result);
124   return *TempDirectory;
125 }
126
127 Path::Path(const std::string& unverified_path)
128   : path(unverified_path)
129 {
130   FlipBackSlashes(path);
131   if (unverified_path.empty())
132     return;
133   if (this->isValid())
134     return;
135   // oops, not valid.
136   path.clear();
137   throw std::string(unverified_path + ": path is not valid");
138 }
139
140 // FIXME: the following set of functions don't map to Windows very well.
141 Path
142 Path::GetRootDirectory() {
143   Path result;
144   result.set("C:\\");
145   return result;
146 }
147
148 static void getPathList(const char*path, std::vector<sys::Path>& Paths) {
149   const char* at = path;
150   const char* delim = strchr(at, ';');
151   Path tmpPath;
152   while (delim != 0) {
153     std::string tmp(at, size_t(delim-at));
154     if (tmpPath.set(tmp))
155       if (tmpPath.canRead())
156         Paths.push_back(tmpPath);
157     at = delim + 1;
158     delim = strchr(at, ';');
159   }
160
161   if (*at != 0)
162     if (tmpPath.set(std::string(at)))
163       if (tmpPath.canRead())
164         Paths.push_back(tmpPath);
165 }
166
167 void
168 Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
169   Paths.push_back(sys::Path("C:\\WINDOWS\\SYSTEM32"));
170   Paths.push_back(sys::Path("C:\\WINDOWS"));
171 }
172
173 void
174 Path::GetBytecodeLibraryPaths(std::vector<sys::Path>& Paths) {
175   char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
176   if (env_var != 0) {
177     getPathList(env_var,Paths);
178   }
179 #ifdef LLVM_LIBDIR
180   {
181     Path tmpPath;
182     if (tmpPath.set(LLVM_LIBDIR))
183       if (tmpPath.canRead())
184         Paths.push_back(tmpPath);
185   }
186 #endif
187   GetSystemLibraryPaths(Paths);
188 }
189
190 Path
191 Path::GetLLVMDefaultConfigDir() {
192   // TODO: this isn't going to fly on Windows
193   return Path("/etc/llvm");
194 }
195
196 Path
197 Path::GetUserHomeDirectory() {
198   // TODO: Typical Windows setup doesn't define HOME.
199   const char* home = getenv("HOME");
200   if (home) {
201     Path result;
202     if (result.set(home))
203       return result;
204   }
205   return GetRootDirectory();
206 }
207 // FIXME: the above set of functions don't map to Windows very well.
208
209 bool
210 Path::isFile() const {
211   return !isDirectory();
212 }
213
214 bool
215 Path::isDirectory() const {
216   if (!exists())
217     return false;
218   WIN32_FILE_ATTRIBUTE_DATA fi;
219   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
220     ThrowError(std::string(path) + ": Can't get status: ");
221   return fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
222 }
223
224 bool
225 Path::isHidden() const {
226   if (!exists())
227     return false;
228   WIN32_FILE_ATTRIBUTE_DATA fi;
229   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
230     ThrowError(std::string(path) + ": Can't get status: ");
231   return fi.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN;
232 }
233
234 std::string
235 Path::getBasename() const {
236   // Find the last slash
237   size_t slash = path.rfind('/');
238   if (slash == std::string::npos)
239     slash = 0;
240   else
241     slash++;
242
243   return path.substr(slash, path.rfind('.'));
244 }
245
246 bool Path::hasMagicNumber(const std::string &Magic) const {
247   std::string actualMagic;
248   if (getMagicNumber(actualMagic, Magic.size()))
249     return Magic == actualMagic;
250   return false;
251 }
252
253 bool
254 Path::isBytecodeFile() const {
255   if (!isFile())
256     return false;
257   std::string actualMagic;
258   if (!getMagicNumber(actualMagic, 4))
259     return false;
260   return actualMagic == "llvc" || actualMagic == "llvm";
261 }
262
263 bool
264 Path::exists() const {
265   DWORD attr = GetFileAttributes(path.c_str());
266   return attr != INVALID_FILE_ATTRIBUTES;
267 }
268
269 bool
270 Path::canRead() const {
271   // FIXME: take security attributes into account.
272   DWORD attr = GetFileAttributes(path.c_str());
273   return attr != INVALID_FILE_ATTRIBUTES;
274 }
275
276 bool
277 Path::canWrite() const {
278   // FIXME: take security attributes into account.
279   DWORD attr = GetFileAttributes(path.c_str());
280   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
281 }
282
283 bool
284 Path::canExecute() const {
285   // FIXME: take security attributes into account.
286   DWORD attr = GetFileAttributes(path.c_str());
287   return attr != INVALID_FILE_ATTRIBUTES;
288 }
289
290 std::string
291 Path::getLast() const {
292   // Find the last slash
293   size_t pos = path.rfind('/');
294
295   // Handle the corner cases
296   if (pos == std::string::npos)
297     return path;
298
299   // If the last character is a slash, we have a root directory
300   if (pos == path.length()-1)
301     return path;
302
303   // Return everything after the last slash
304   return path.substr(pos+1);
305 }
306
307 void
308 Path::getStatusInfo(StatusInfo& info) const {
309   WIN32_FILE_ATTRIBUTE_DATA fi;
310   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
311     ThrowError(std::string(path) + ": Can't get status: ");
312
313   info.fileSize = fi.nFileSizeHigh;
314   info.fileSize <<= 32;
315   info.fileSize += fi.nFileSizeLow;
316
317   info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
318   info.user = 9999;    // Not applicable to Windows, so...
319   info.group = 9999;   // Not applicable to Windows, so...
320
321   __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
322   info.modTime.fromWin32Time(ft);
323
324   info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
325 }
326
327 static bool AddPermissionBits(const std::string& Filename, int bits) {
328   DWORD attr = GetFileAttributes(Filename.c_str());
329
330   // If it doesn't exist, we're done.
331   if (attr == INVALID_FILE_ATTRIBUTES)
332     return false;
333
334   // The best we can do to interpret Unix permission bits is to use
335   // the owner writable bit.
336   if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
337     if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
338       ThrowError(Filename + ": SetFileAttributes: ");
339   }
340   return true;
341 }
342
343 void Path::makeReadableOnDisk() {
344   // All files are readable on Windows (ignoring security attributes).
345 }
346
347 void Path::makeWriteableOnDisk() {
348   DWORD attr = GetFileAttributes(path.c_str());
349
350   // If it doesn't exist, we're done.
351   if (attr == INVALID_FILE_ATTRIBUTES)
352     return;
353
354   if (attr & FILE_ATTRIBUTE_READONLY) {
355     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
356       ThrowError(std::string(path) + ": Can't make file writable: ");
357   }
358 }
359
360 void Path::makeExecutableOnDisk() {
361   // All files are executable on Windows (ignoring security attributes).
362 }
363
364 bool
365 Path::getDirectoryContents(std::set<Path>& result) const {
366   if (!isDirectory())
367     return false;
368
369   result.clear();
370   WIN32_FIND_DATA fd;
371   std::string searchpath = path;
372   if (path.size() == 0 || searchpath[path.size()-1] == '/')
373     searchpath += "*";
374   else
375     searchpath += "/*";
376
377   HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
378   if (h == INVALID_HANDLE_VALUE) {
379     if (GetLastError() == ERROR_FILE_NOT_FOUND)
380       return true; // not really an error, now is it?
381     ThrowError(path + ": Can't read directory: ");
382   }
383
384   do {
385     if (fd.cFileName[0] == '.')
386       continue;
387     Path aPath(path);
388     aPath.appendComponent(&fd.cFileName[0]);
389     result.insert(aPath);
390   } while (FindNextFile(h, &fd));
391
392   DWORD err = GetLastError();
393   FindClose(h);
394   if (err != ERROR_NO_MORE_FILES) {
395     SetLastError(err);
396     ThrowError(path + ": Can't read directory: ");
397   }
398   return true;
399 }
400
401 bool
402 Path::set(const std::string& a_path) {
403   if (a_path.size() == 0)
404     return false;
405   std::string save(path);
406   path = a_path;
407   FlipBackSlashes(path);
408   if (!isValid()) {
409     path = save;
410     return false;
411   }
412   return true;
413 }
414
415 bool
416 Path::appendComponent(const std::string& name) {
417   if (name.empty())
418     return false;
419   std::string save(path);
420   if (!path.empty()) {
421     size_t last = path.size() - 1;
422     if (path[last] != '/')
423       path += '/';
424   }
425   path += name;
426   if (!isValid()) {
427     path = save;
428     return false;
429   }
430   return true;
431 }
432
433 bool
434 Path::eraseComponent() {
435   size_t slashpos = path.rfind('/',path.size());
436   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
437     return false;
438   path.erase(slashpos);
439   return true;
440 }
441
442 bool
443 Path::appendSuffix(const std::string& suffix) {
444   std::string save(path);
445   path.append(".");
446   path.append(suffix);
447   if (!isValid()) {
448     path = save;
449     return false;
450   }
451   return true;
452 }
453
454 bool
455 Path::eraseSuffix() {
456   size_t dotpos = path.rfind('.',path.size());
457   size_t slashpos = path.rfind('/',path.size());
458   if (dotpos != std::string::npos) {
459     if (slashpos == std::string::npos || dotpos > slashpos) {
460       path.erase(dotpos, path.size()-dotpos);
461       return true;
462     }
463   }
464   return false;
465 }
466
467 bool
468 Path::createDirectoryOnDisk(bool create_parents) {
469   // Get a writeable copy of the path name
470   size_t len = path.length();
471   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
472   path.copy(pathname, len);
473   pathname[len] = 0;
474
475   // Make sure it ends with a slash.
476   if (len == 0 || pathname[len - 1] != '/') {
477     pathname[len] = '/';
478     pathname[++len] = 0;
479   }
480
481   // Determine starting point for initial / search.
482   char *next = pathname;
483   if (pathname[0] == '/' && pathname[1] == '/') {
484     // Skip host name.
485     next = strchr(pathname+2, '/');
486     if (next == NULL)
487       throw std::string(pathname) + ": badly formed remote directory";
488     // Skip share name.
489     next = strchr(next+1, '/');
490     if (next == NULL)
491       throw std::string(pathname) + ": badly formed remote directory";
492     next++;
493     if (*next == 0)
494       throw std::string(pathname) + ": badly formed remote directory";
495   } else {
496     if (pathname[1] == ':')
497       next += 2;    // skip drive letter
498     if (*next == '/')
499       next++;       // skip root directory
500   }
501
502   // If we're supposed to create intermediate directories
503   if (create_parents) {
504     // Loop through the directory components until we're done
505     while (*next) {
506       next = strchr(next, '/');
507       *next = 0;
508       if (!CreateDirectory(pathname, NULL))
509           ThrowError(std::string(pathname) + ": Can't create directory: ");
510       *next++ = '/';
511     }
512   } else {
513     // Drop trailing slash.
514     pathname[len-1] = 0;
515     if (!CreateDirectory(pathname, NULL)) {
516       ThrowError(std::string(pathname) + ": Can't create directory: ");
517     }
518   }
519   return true;
520 }
521
522 bool
523 Path::createFileOnDisk() {
524   // Create the file
525   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
526                         FILE_ATTRIBUTE_NORMAL, NULL);
527   if (h == INVALID_HANDLE_VALUE)
528     ThrowError(path + ": Can't create file: ");
529
530   CloseHandle(h);
531   return true;
532 }
533
534 bool
535 Path::eraseFromDisk(bool remove_contents) const {
536   if (isFile()) {
537     DWORD attr = GetFileAttributes(path.c_str());
538
539     // If it doesn't exist, we're done.
540     if (attr == INVALID_FILE_ATTRIBUTES)
541       return true;
542
543     // Read-only files cannot be deleted on Windows.  Must remove the read-only
544     // attribute first.
545     if (attr & FILE_ATTRIBUTE_READONLY) {
546       if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
547         ThrowError(path + ": Can't destroy file: ");
548     }
549
550     if (!DeleteFile(path.c_str()))
551       ThrowError(path + ": Can't destroy file: ");
552     return true;
553   } else /* isDirectory() */ {
554     // If it doesn't exist, we're done.
555     if (!exists())
556       return true;
557
558     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
559     int lastchar = path.length() - 1 ;
560     path.copy(pathname, lastchar+1);
561
562     // Make path end with '/*'.
563     if (pathname[lastchar] != '/')
564       pathname[++lastchar] = '/';
565     pathname[lastchar+1] = '*';
566     pathname[lastchar+2] = 0;
567
568     if (remove_contents) {
569       WIN32_FIND_DATA fd;
570       HANDLE h = FindFirstFile(pathname, &fd);
571
572       // It's a bad idea to alter the contents of a directory while enumerating
573       // its contents. So build a list of its contents first, then destroy them.
574
575       if (h != INVALID_HANDLE_VALUE) {
576         std::vector<Path> list;
577
578         do {
579           if (strcmp(fd.cFileName, ".") == 0)
580             continue;
581           if (strcmp(fd.cFileName, "..") == 0)
582             continue;
583
584           Path aPath(path);
585           aPath.appendComponent(&fd.cFileName[0]);
586           list.push_back(aPath);
587         } while (FindNextFile(h, &fd));
588
589         DWORD err = GetLastError();
590         FindClose(h);
591         if (err != ERROR_NO_MORE_FILES) {
592           SetLastError(err);
593           ThrowError(path + ": Can't read directory: ");
594         }
595
596         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
597              ++I) {
598           Path &aPath = *I;
599           aPath.eraseFromDisk(true);
600         }
601       } else {
602         if (GetLastError() != ERROR_FILE_NOT_FOUND)
603           ThrowError(path + ": Can't read directory: ");
604       }
605     }
606
607     pathname[lastchar] = 0;
608     if (!RemoveDirectory(pathname))
609       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
610     return true;
611   }
612 }
613
614 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
615   if (!isFile())
616     return false;
617   assert(len < 1024 && "Request for magic string too long");
618   char* buf = (char*) alloca(1 + len);
619
620   HANDLE h = CreateFile(path.c_str(),
621                         GENERIC_READ,
622                         FILE_SHARE_READ,
623                         NULL,
624                         OPEN_EXISTING,
625                         FILE_ATTRIBUTE_NORMAL,
626                         NULL);
627   if (h == INVALID_HANDLE_VALUE)
628     return false;
629
630   DWORD nRead = 0;
631   BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
632   CloseHandle(h);
633
634   if (!ret || nRead != len)
635     return false;
636
637   buf[len] = '\0';
638   Magic = buf;
639   return true;
640 }
641
642 bool
643 Path::renamePathOnDisk(const Path& newName) {
644   if (!MoveFile(path.c_str(), newName.c_str()))
645     ThrowError("Can't move '" + path +
646                "' to '" + newName.path + "': ");
647   return true;
648 }
649
650 bool
651 Path::setStatusInfoOnDisk(const StatusInfo& si) const {
652   if (!isFile()) return false;
653
654   HANDLE h = CreateFile(path.c_str(),
655                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
656                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
657                         NULL,
658                         OPEN_EXISTING,
659                         FILE_ATTRIBUTE_NORMAL,
660                         NULL);
661   if (h == INVALID_HANDLE_VALUE)
662     return false;
663
664   BY_HANDLE_FILE_INFORMATION bhfi;
665   if (!GetFileInformationByHandle(h, &bhfi)) {
666     DWORD err = GetLastError();
667     CloseHandle(h);
668     SetLastError(err);
669     ThrowError(path + ": GetFileInformationByHandle: ");
670   }
671
672   FILETIME ft;
673   (uint64_t&)ft = si.modTime.toWin32Time();
674   BOOL ret = SetFileTime(h, NULL, &ft, &ft);
675   DWORD err = GetLastError();
676   CloseHandle(h);
677   if (!ret) {
678     SetLastError(err);
679     ThrowError(path + ": SetFileTime: ");
680   }
681
682   // Best we can do with Unix permission bits is to interpret the owner
683   // writable bit.
684   if (si.mode & 0200) {
685     if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
686       if (!SetFileAttributes(path.c_str(),
687               bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
688         ThrowError(path + ": SetFileAttributes: ");
689     }
690   } else {
691     if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
692       if (!SetFileAttributes(path.c_str(),
693               bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
694         ThrowError(path + ": SetFileAttributes: ");
695     }
696   }
697
698   return true;
699 }
700
701 void
702 sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) {
703   // Can't use CopyFile macro defined in Windows.h because it would mess up the
704   // above line.  We use the expansion it would have in a non-UNICODE build.
705   if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
706     ThrowError("Can't copy '" + Src.toString() +
707                "' to '" + Dest.toString() + "': ");
708 }
709
710 void
711 Path::makeUnique(bool reuse_current) {
712   if (reuse_current && !exists())
713     return; // File doesn't exist already, just use it!
714
715   // Reserve space for -XXXXXX at the end.
716   char *FNBuffer = (char*) alloca(path.size()+8);
717   unsigned offset = path.size();
718   path.copy(FNBuffer, offset);
719
720   // Find a numeric suffix that isn't used by an existing file.
721   static unsigned FCounter = 0;
722   do {
723     sprintf(FNBuffer+offset, "-%06u", FCounter);
724     if (++FCounter > 999999)
725       FCounter = 0;
726     path = FNBuffer;
727   } while (exists());
728 }
729
730 bool
731 Path::createTemporaryFileOnDisk(bool reuse_current) {
732   // Make this into a unique file name
733   makeUnique( reuse_current );
734
735   // Now go and create it
736   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
737                         FILE_ATTRIBUTE_NORMAL, NULL);
738   if (h == INVALID_HANDLE_VALUE)
739     return false;
740
741   CloseHandle(h);
742   return true;
743 }
744
745 }
746 }
747
748