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