Add static cast to unsigned char whenever a character classification function is...
[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 CopyFile
25 #undef GetCurrentDirectory
26
27 // Windows happily accepts either forward or backward slashes, though any path
28 // returned by a Win32 API will have backward slashes.  As LLVM code basically
29 // assumes forward slashes are used, backward slashs are converted where they
30 // can be introduced into a path.
31 //
32 // Another invariant is that a path ends with a slash if and only if the path
33 // is a root directory.  Any other use of a trailing slash is stripped.  Unlike
34 // in Unix, Windows has a rather complicated notion of a root path and this
35 // invariant helps simply the code.
36
37 static void FlipBackSlashes(std::string& s) {
38   for (size_t i = 0; i < s.size(); i++)
39     if (s[i] == '\\')
40       s[i] = '/';
41 }
42
43 namespace llvm {
44 namespace sys {
45
46 const char PathSeparator = ';';
47
48 StringRef Path::GetEXESuffix() {
49   return "exe";
50 }
51
52 Path::Path(llvm::StringRef p)
53   : path(p) {
54   FlipBackSlashes(path);
55 }
56
57 Path::Path(const char *StrStart, unsigned StrLen)
58   : path(StrStart, StrLen) {
59   FlipBackSlashes(path);
60 }
61
62 Path&
63 Path::operator=(StringRef that) {
64   path.assign(that.data(), that.size());
65   FlipBackSlashes(path);
66   return *this;
67 }
68
69 bool
70 Path::isValid() const {
71   if (path.empty())
72     return false;
73
74   size_t len = path.size();
75   // If there is a null character, it and all its successors are ignored.
76   size_t pos = path.find_first_of('\0');
77   if (pos != std::string::npos)
78     len = pos;
79
80   // If there is a colon, it must be the second character, preceded by a letter
81   // and followed by something.
82   pos = path.rfind(':',len);
83   size_t rootslash = 0;
84   if (pos != std::string::npos) {
85     if (pos != 1 || !isalpha(static_cast<unsigned char>(path[0])) || len < 3)
86       return false;
87       rootslash = 2;
88   }
89
90   // Look for a UNC path, and if found adjust our notion of the root slash.
91   if (len > 3 && path[0] == '/' && path[1] == '/') {
92     rootslash = path.find('/', 2);
93     if (rootslash == std::string::npos)
94       rootslash = 0;
95   }
96
97   // Check for illegal characters.
98   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
99                          "\013\014\015\016\017\020\021\022\023\024\025\026"
100                          "\027\030\031\032\033\034\035\036\037")
101       != std::string::npos)
102     return false;
103
104   // Remove trailing slash, unless it's a root slash.
105   if (len > rootslash+1 && path[len-1] == '/')
106     path.erase(--len);
107
108   // Check each component for legality.
109   for (pos = 0; pos < len; ++pos) {
110     // A component may not end in a space.
111     if (path[pos] == ' ') {
112       if (pos+1 == len || path[pos+1] == '/' || path[pos+1] == '\0')
113         return false;
114     }
115
116     // A component may not end in a period.
117     if (path[pos] == '.') {
118       if (pos+1 == len || path[pos+1] == '/') {
119         // Unless it is the pseudo-directory "."...
120         if (pos == 0 || path[pos-1] == '/' || path[pos-1] == ':')
121           return true;
122         // or "..".
123         if (pos > 0 && path[pos-1] == '.') {
124           if (pos == 1 || path[pos-2] == '/' || path[pos-2] == ':')
125             return true;
126         }
127         return false;
128       }
129     }
130   }
131
132   return true;
133 }
134
135 void Path::makeAbsolute() {
136   TCHAR  FullPath[MAX_PATH + 1] = {0};
137   LPTSTR FilePart = NULL;
138
139   DWORD RetLength = ::GetFullPathNameA(path.c_str(),
140                         sizeof(FullPath)/sizeof(FullPath[0]),
141                         FullPath, &FilePart);
142
143   if (0 == RetLength) {
144     // FIXME: Report the error GetLastError()
145     assert(0 && "Unable to make absolute path!");
146   } else if (RetLength > MAX_PATH) {
147     // FIXME: Report too small buffer (needed RetLength bytes).
148     assert(0 && "Unable to make absolute path!");
149   } else {
150     path = FullPath;
151   }
152 }
153
154 bool
155 Path::isAbsolute(const char *NameStart, unsigned NameLen) {
156   assert(NameStart);
157   // FIXME: This does not handle correctly an absolute path starting from
158   // a drive letter or in UNC format.
159   switch (NameLen) {
160   case 0:
161     return false;
162   case 1:
163   case 2:
164     return NameStart[0] == '/';
165   default:
166     return
167       (NameStart[0] == '/' || (NameStart[1] == ':' && NameStart[2] == '/')) ||
168       (NameStart[0] == '\\' || (NameStart[1] == ':' && NameStart[2] == '\\'));
169   }
170 }
171
172 bool
173 Path::isAbsolute() const {
174   // FIXME: This does not handle correctly an absolute path starting from
175   // a drive letter or in UNC format.
176   switch (path.length()) {
177     case 0:
178       return false;
179     case 1:
180     case 2:
181       return path[0] == '/';
182     default:
183       return path[0] == '/' || (path[1] == ':' && path[2] == '/');
184   }
185 }
186
187 static Path *TempDirectory;
188
189 Path
190 Path::GetTemporaryDirectory(std::string* ErrMsg) {
191   if (TempDirectory) {
192 #if defined(_MSC_VER)
193     // Visual Studio gets confused and emits a diagnostic about calling exists,
194     // even though this is the implementation for PathV1.  Temporarily 
195     // disable the deprecated warning message
196     #pragma warning(push)
197     #pragma warning(disable:4996)
198 #endif
199     assert(TempDirectory->exists() && "Who has removed TempDirectory?");
200 #if defined(_MSC_VER)
201     #pragma warning(pop)
202 #endif
203     return *TempDirectory;
204   }
205
206   char pathname[MAX_PATH];
207   if (!GetTempPath(MAX_PATH, pathname)) {
208     if (ErrMsg)
209       *ErrMsg = "Can't determine temporary directory";
210     return Path();
211   }
212
213   Path result;
214   result.set(pathname);
215
216   // Append a subdirectory based on our process id so multiple LLVMs don't
217   // step on each other's toes.
218 #ifdef __MINGW32__
219   // Mingw's Win32 header files are broken.
220   sprintf(pathname, "LLVM_%u", unsigned(GetCurrentProcessId()));
221 #else
222   sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
223 #endif
224   result.appendComponent(pathname);
225
226   // If there's a directory left over from a previous LLVM execution that
227   // happened to have the same process id, get rid of it.
228   result.eraseFromDisk(true);
229
230   // And finally (re-)create the empty directory.
231   result.createDirectoryOnDisk(false);
232   TempDirectory = new Path(result);
233   return *TempDirectory;
234 }
235
236 // FIXME: the following set of functions don't map to Windows very well.
237 Path
238 Path::GetRootDirectory() {
239   // This is the only notion that that Windows has of a root directory. Nothing
240   // is here except for drives.
241   return Path("file:///");
242 }
243
244 void
245 Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
246   char buff[MAX_PATH];
247   // Generic form of C:\Windows\System32
248   HRESULT res =  SHGetFolderPathA(NULL,
249                                   CSIDL_FLAG_CREATE | CSIDL_SYSTEM,
250                                   NULL,
251                                   SHGFP_TYPE_CURRENT,
252                                   buff);
253   if (res != S_OK) {
254     assert(0 && "Failed to get system directory");
255     return;
256   }
257   Paths.push_back(sys::Path(buff));
258
259   // Reset buff.
260   buff[0] = 0;
261   // Generic form of C:\Windows
262   res =  SHGetFolderPathA(NULL,
263                           CSIDL_FLAG_CREATE | CSIDL_WINDOWS,
264                           NULL,
265                           SHGFP_TYPE_CURRENT,
266                           buff);
267   if (res != S_OK) {
268     assert(0 && "Failed to get windows directory");
269     return;
270   }
271   Paths.push_back(sys::Path(buff));
272 }
273
274 void
275 Path::GetBitcodeLibraryPaths(std::vector<sys::Path>& Paths) {
276   char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
277   if (env_var != 0) {
278     getPathList(env_var,Paths);
279   }
280 #ifdef LLVM_LIBDIR
281   {
282     Path tmpPath;
283     if (tmpPath.set(LLVM_LIBDIR))
284       if (tmpPath.canRead())
285         Paths.push_back(tmpPath);
286   }
287 #endif
288   GetSystemLibraryPaths(Paths);
289 }
290
291 Path
292 Path::GetUserHomeDirectory() {
293   char buff[MAX_PATH];
294   HRESULT res = SHGetFolderPathA(NULL,
295                                  CSIDL_FLAG_CREATE | CSIDL_APPDATA,
296                                  NULL,
297                                  SHGFP_TYPE_CURRENT,
298                                  buff);
299   if (res != S_OK)
300     assert(0 && "Failed to get user home directory");
301   return Path(buff);
302 }
303
304 Path
305 Path::GetCurrentDirectory() {
306   char pathname[MAX_PATH];
307   ::GetCurrentDirectoryA(MAX_PATH,pathname);
308   return Path(pathname);
309 }
310
311 /// GetMainExecutable - Return the path to the main executable, given the
312 /// value of argv[0] from program startup.
313 Path Path::GetMainExecutable(const char *argv0, void *MainAddr) {
314   char pathname[MAX_PATH];
315   DWORD ret = ::GetModuleFileNameA(NULL, pathname, MAX_PATH);
316   return ret != MAX_PATH ? Path(pathname) : Path();
317 }
318
319
320 // FIXME: the above set of functions don't map to Windows very well.
321
322
323 StringRef Path::getDirname() const {
324   return getDirnameCharSep(path, "/");
325 }
326
327 StringRef
328 Path::getBasename() const {
329   // Find the last slash
330   size_t slash = path.rfind('/');
331   if (slash == std::string::npos)
332     slash = 0;
333   else
334     slash++;
335
336   size_t dot = path.rfind('.');
337   if (dot == std::string::npos || dot < slash)
338     return StringRef(path).substr(slash);
339   else
340     return StringRef(path).substr(slash, dot - slash);
341 }
342
343 StringRef
344 Path::getSuffix() const {
345   // Find the last slash
346   size_t slash = path.rfind('/');
347   if (slash == std::string::npos)
348     slash = 0;
349   else
350     slash++;
351
352   size_t dot = path.rfind('.');
353   if (dot == std::string::npos || dot < slash)
354     return StringRef("");
355   else
356     return StringRef(path).substr(dot + 1);
357 }
358
359 bool
360 Path::exists() const {
361   DWORD attr = GetFileAttributes(path.c_str());
362   return attr != INVALID_FILE_ATTRIBUTES;
363 }
364
365 bool
366 Path::isDirectory() const {
367   DWORD attr = GetFileAttributes(path.c_str());
368   return (attr != INVALID_FILE_ATTRIBUTES) &&
369          (attr & FILE_ATTRIBUTE_DIRECTORY);
370 }
371
372 bool
373 Path::isSymLink() const {
374   DWORD attributes = GetFileAttributes(path.c_str());
375
376   if (attributes == INVALID_FILE_ATTRIBUTES)
377     // There's no sane way to report this :(.
378     assert(0 && "GetFileAttributes returned INVALID_FILE_ATTRIBUTES");
379
380   // This isn't exactly what defines a NTFS symlink, but it is only true for
381   // paths that act like a symlink.
382   return attributes & FILE_ATTRIBUTE_REPARSE_POINT;
383 }
384
385 bool
386 Path::canRead() const {
387   // FIXME: take security attributes into account.
388   DWORD attr = GetFileAttributes(path.c_str());
389   return attr != INVALID_FILE_ATTRIBUTES;
390 }
391
392 bool
393 Path::canWrite() const {
394   // FIXME: take security attributes into account.
395   DWORD attr = GetFileAttributes(path.c_str());
396   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
397 }
398
399 bool
400 Path::canExecute() const {
401   // FIXME: take security attributes into account.
402   DWORD attr = GetFileAttributes(path.c_str());
403   return attr != INVALID_FILE_ATTRIBUTES;
404 }
405
406 bool
407 Path::isRegularFile() const {
408   bool res;
409   if (fs::is_regular_file(path, res))
410     return false;
411   return res;
412 }
413
414 StringRef
415 Path::getLast() const {
416   // Find the last slash
417   size_t pos = path.rfind('/');
418
419   // Handle the corner cases
420   if (pos == std::string::npos)
421     return path;
422
423   // If the last character is a slash, we have a root directory
424   if (pos == path.length()-1)
425     return path;
426
427   // Return everything after the last slash
428   return StringRef(path).substr(pos+1);
429 }
430
431 const FileStatus *
432 PathWithStatus::getFileStatus(bool update, std::string *ErrStr) const {
433   if (!fsIsValid || update) {
434     WIN32_FILE_ATTRIBUTE_DATA fi;
435     if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) {
436       MakeErrMsg(ErrStr, "getStatusInfo():" + std::string(path) +
437                       ": Can't get status: ");
438       return 0;
439     }
440
441     status.fileSize = fi.nFileSizeHigh;
442     status.fileSize <<= sizeof(fi.nFileSizeHigh)*8;
443     status.fileSize += fi.nFileSizeLow;
444
445     status.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
446     status.user = 9999;    // Not applicable to Windows, so...
447     status.group = 9999;   // Not applicable to Windows, so...
448
449     // FIXME: this is only unique if the file is accessed by the same file path.
450     // How do we do this for C:\dir\file and ..\dir\file ? Unix has inode
451     // numbers, but the concept doesn't exist in Windows.
452     status.uniqueID = 0;
453     for (unsigned i = 0; i < path.length(); ++i)
454       status.uniqueID += path[i];
455
456     ULARGE_INTEGER ui;
457     ui.LowPart = fi.ftLastWriteTime.dwLowDateTime;
458     ui.HighPart = fi.ftLastWriteTime.dwHighDateTime;
459     status.modTime.fromWin32Time(ui.QuadPart);
460
461     status.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
462     fsIsValid = true;
463   }
464   return &status;
465 }
466
467 bool Path::makeReadableOnDisk(std::string* ErrMsg) {
468   // All files are readable on Windows (ignoring security attributes).
469   return false;
470 }
471
472 bool Path::makeWriteableOnDisk(std::string* ErrMsg) {
473   DWORD attr = GetFileAttributes(path.c_str());
474
475   // If it doesn't exist, we're done.
476   if (attr == INVALID_FILE_ATTRIBUTES)
477     return false;
478
479   if (attr & FILE_ATTRIBUTE_READONLY) {
480     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY)) {
481       MakeErrMsg(ErrMsg, std::string(path) + ": Can't make file writable: ");
482       return true;
483     }
484   }
485   return false;
486 }
487
488 bool Path::makeExecutableOnDisk(std::string* ErrMsg) {
489   // All files are executable on Windows (ignoring security attributes).
490   return false;
491 }
492
493 bool
494 Path::getDirectoryContents(std::set<Path>& result, std::string* ErrMsg) const {
495   WIN32_FILE_ATTRIBUTE_DATA fi;
496   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi)) {
497     MakeErrMsg(ErrMsg, path + ": can't get status of file");
498     return true;
499   }
500
501   if (!(fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
502     if (ErrMsg)
503       *ErrMsg = path + ": not a directory";
504     return true;
505   }
506
507   result.clear();
508   WIN32_FIND_DATA fd;
509   std::string searchpath = path;
510   if (path.size() == 0 || searchpath[path.size()-1] == '/')
511     searchpath += "*";
512   else
513     searchpath += "/*";
514
515   HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
516   if (h == INVALID_HANDLE_VALUE) {
517     if (GetLastError() == ERROR_FILE_NOT_FOUND)
518       return true; // not really an error, now is it?
519     MakeErrMsg(ErrMsg, path + ": Can't read directory: ");
520     return true;
521   }
522
523   do {
524     if (fd.cFileName[0] == '.')
525       continue;
526     Path aPath(path);
527     aPath.appendComponent(&fd.cFileName[0]);
528     result.insert(aPath);
529   } while (FindNextFile(h, &fd));
530
531   DWORD err = GetLastError();
532   FindClose(h);
533   if (err != ERROR_NO_MORE_FILES) {
534     SetLastError(err);
535     MakeErrMsg(ErrMsg, path + ": Can't read directory: ");
536     return true;
537   }
538   return false;
539 }
540
541 bool
542 Path::set(StringRef a_path) {
543   if (a_path.empty())
544     return false;
545   std::string save(path);
546   path = a_path;
547   FlipBackSlashes(path);
548   if (!isValid()) {
549     path = save;
550     return false;
551   }
552   return true;
553 }
554
555 bool
556 Path::appendComponent(StringRef name) {
557   if (name.empty())
558     return false;
559   std::string save(path);
560   if (!path.empty()) {
561     size_t last = path.size() - 1;
562     if (path[last] != '/')
563       path += '/';
564   }
565   path += name;
566   if (!isValid()) {
567     path = save;
568     return false;
569   }
570   return true;
571 }
572
573 bool
574 Path::eraseComponent() {
575   size_t slashpos = path.rfind('/',path.size());
576   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
577     return false;
578   std::string save(path);
579   path.erase(slashpos);
580   if (!isValid()) {
581     path = save;
582     return false;
583   }
584   return true;
585 }
586
587 bool
588 Path::eraseSuffix() {
589   size_t dotpos = path.rfind('.',path.size());
590   size_t slashpos = path.rfind('/',path.size());
591   if (dotpos != std::string::npos) {
592     if (slashpos == std::string::npos || dotpos > slashpos+1) {
593       std::string save(path);
594       path.erase(dotpos, path.size()-dotpos);
595       if (!isValid()) {
596         path = save;
597         return false;
598       }
599       return true;
600     }
601   }
602   return false;
603 }
604
605 inline bool PathMsg(std::string* ErrMsg, const char* pathname, const char*msg) {
606   if (ErrMsg)
607     *ErrMsg = std::string(pathname) + ": " + std::string(msg);
608   return true;
609 }
610
611 bool
612 Path::createDirectoryOnDisk(bool create_parents, std::string* ErrMsg) {
613   // Get a writeable copy of the path name
614   size_t len = path.length();
615   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
616   path.copy(pathname, len);
617   pathname[len] = 0;
618
619   // Make sure it ends with a slash.
620   if (len == 0 || pathname[len - 1] != '/') {
621     pathname[len] = '/';
622     pathname[++len] = 0;
623   }
624
625   // Determine starting point for initial / search.
626   char *next = pathname;
627   if (pathname[0] == '/' && pathname[1] == '/') {
628     // Skip host name.
629     next = strchr(pathname+2, '/');
630     if (next == NULL)
631       return PathMsg(ErrMsg, pathname, "badly formed remote directory");
632
633     // Skip share name.
634     next = strchr(next+1, '/');
635     if (next == NULL)
636       return PathMsg(ErrMsg, pathname,"badly formed remote directory");
637
638     next++;
639     if (*next == 0)
640       return PathMsg(ErrMsg, pathname, "badly formed remote directory");
641
642   } else {
643     if (pathname[1] == ':')
644       next += 2;    // skip drive letter
645     if (*next == '/')
646       next++;       // skip root directory
647   }
648
649   // If we're supposed to create intermediate directories
650   if (create_parents) {
651     // Loop through the directory components until we're done
652     while (*next) {
653       next = strchr(next, '/');
654       *next = 0;
655       if (!CreateDirectory(pathname, NULL) &&
656           GetLastError() != ERROR_ALREADY_EXISTS)
657           return MakeErrMsg(ErrMsg,
658             std::string(pathname) + ": Can't create directory: ");
659       *next++ = '/';
660     }
661   } else {
662     // Drop trailing slash.
663     pathname[len-1] = 0;
664     if (!CreateDirectory(pathname, NULL) &&
665         GetLastError() != ERROR_ALREADY_EXISTS) {
666       return MakeErrMsg(ErrMsg, std::string(pathname) +
667                         ": Can't create directory: ");
668     }
669   }
670   return false;
671 }
672
673 bool
674 Path::createFileOnDisk(std::string* ErrMsg) {
675   // Create the file
676   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
677                         FILE_ATTRIBUTE_NORMAL, NULL);
678   if (h == INVALID_HANDLE_VALUE)
679     return MakeErrMsg(ErrMsg, path + ": Can't create file: ");
680
681   CloseHandle(h);
682   return false;
683 }
684
685 bool
686 Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
687   WIN32_FILE_ATTRIBUTE_DATA fi;
688   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
689     return true;
690
691   if (fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
692     // If it doesn't exist, we're done.
693     bool Exists;
694     if (fs::exists(path, Exists) || !Exists)
695       return false;
696
697     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
698     int lastchar = path.length() - 1 ;
699     path.copy(pathname, lastchar+1);
700
701     // Make path end with '/*'.
702     if (pathname[lastchar] != '/')
703       pathname[++lastchar] = '/';
704     pathname[lastchar+1] = '*';
705     pathname[lastchar+2] = 0;
706
707     if (remove_contents) {
708       WIN32_FIND_DATA fd;
709       HANDLE h = FindFirstFile(pathname, &fd);
710
711       // It's a bad idea to alter the contents of a directory while enumerating
712       // its contents. So build a list of its contents first, then destroy them.
713
714       if (h != INVALID_HANDLE_VALUE) {
715         std::vector<Path> list;
716
717         do {
718           if (strcmp(fd.cFileName, ".") == 0)
719             continue;
720           if (strcmp(fd.cFileName, "..") == 0)
721             continue;
722
723           Path aPath(path);
724           aPath.appendComponent(&fd.cFileName[0]);
725           list.push_back(aPath);
726         } while (FindNextFile(h, &fd));
727
728         DWORD err = GetLastError();
729         FindClose(h);
730         if (err != ERROR_NO_MORE_FILES) {
731           SetLastError(err);
732           return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
733         }
734
735         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
736              ++I) {
737           Path &aPath = *I;
738           aPath.eraseFromDisk(true);
739         }
740       } else {
741         if (GetLastError() != ERROR_FILE_NOT_FOUND)
742           return MakeErrMsg(ErrStr, path + ": Can't read directory: ");
743       }
744     }
745
746     pathname[lastchar] = 0;
747     if (!RemoveDirectory(pathname))
748       return MakeErrMsg(ErrStr,
749         std::string(pathname) + ": Can't destroy directory: ");
750     return false;
751   } else {
752     // Read-only files cannot be deleted on Windows.  Must remove the read-only
753     // attribute first.
754     if (fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
755       if (!SetFileAttributes(path.c_str(),
756                              fi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
757         return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
758     }
759
760     if (!DeleteFile(path.c_str()))
761       return MakeErrMsg(ErrStr, path + ": Can't destroy file: ");
762     return false;
763   }
764 }
765
766 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
767   assert(len < 1024 && "Request for magic string too long");
768   char* buf = reinterpret_cast<char*>(alloca(len));
769
770   HANDLE h = CreateFile(path.c_str(),
771                         GENERIC_READ,
772                         FILE_SHARE_READ,
773                         NULL,
774                         OPEN_EXISTING,
775                         FILE_ATTRIBUTE_NORMAL,
776                         NULL);
777   if (h == INVALID_HANDLE_VALUE)
778     return false;
779
780   DWORD nRead = 0;
781   BOOL ret = ReadFile(h, buf, len, &nRead, NULL);
782   CloseHandle(h);
783
784   if (!ret || nRead != len)
785     return false;
786
787   Magic = std::string(buf, len);
788   return true;
789 }
790
791 bool
792 Path::renamePathOnDisk(const Path& newName, std::string* ErrMsg) {
793   if (!MoveFileEx(path.c_str(), newName.c_str(), MOVEFILE_REPLACE_EXISTING))
794     return MakeErrMsg(ErrMsg, "Can't move '" + path + "' to '" + newName.path
795         + "': ");
796   return false;
797 }
798
799 bool
800 Path::setStatusInfoOnDisk(const FileStatus &si, std::string *ErrMsg) const {
801   // FIXME: should work on directories also.
802   if (!si.isFile) {
803     return true;
804   }
805
806   HANDLE h = CreateFile(path.c_str(),
807                         FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
808                         FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
809                         NULL,
810                         OPEN_EXISTING,
811                         FILE_ATTRIBUTE_NORMAL,
812                         NULL);
813   if (h == INVALID_HANDLE_VALUE)
814     return true;
815
816   BY_HANDLE_FILE_INFORMATION bhfi;
817   if (!GetFileInformationByHandle(h, &bhfi)) {
818     DWORD err = GetLastError();
819     CloseHandle(h);
820     SetLastError(err);
821     return MakeErrMsg(ErrMsg, path + ": GetFileInformationByHandle: ");
822   }
823
824   ULARGE_INTEGER ui;
825   ui.QuadPart = si.modTime.toWin32Time();
826   FILETIME ft;
827   ft.dwLowDateTime = ui.LowPart;
828   ft.dwHighDateTime = ui.HighPart;
829   BOOL ret = SetFileTime(h, NULL, &ft, &ft);
830   DWORD err = GetLastError();
831   CloseHandle(h);
832   if (!ret) {
833     SetLastError(err);
834     return MakeErrMsg(ErrMsg, path + ": SetFileTime: ");
835   }
836
837   // Best we can do with Unix permission bits is to interpret the owner
838   // writable bit.
839   if (si.mode & 0200) {
840     if (bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY) {
841       if (!SetFileAttributes(path.c_str(),
842               bhfi.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY))
843         return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
844     }
845   } else {
846     if (!(bhfi.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
847       if (!SetFileAttributes(path.c_str(),
848               bhfi.dwFileAttributes | FILE_ATTRIBUTE_READONLY))
849         return MakeErrMsg(ErrMsg, path + ": SetFileAttributes: ");
850     }
851   }
852
853   return false;
854 }
855
856 bool
857 CopyFile(const sys::Path &Dest, const sys::Path &Src, std::string* ErrMsg) {
858   // Can't use CopyFile macro defined in Windows.h because it would mess up the
859   // above line.  We use the expansion it would have in a non-UNICODE build.
860   if (!::CopyFileA(Src.c_str(), Dest.c_str(), false))
861     return MakeErrMsg(ErrMsg, "Can't copy '" + Src.str() +
862                "' to '" + Dest.str() + "': ");
863   return false;
864 }
865
866 bool
867 Path::makeUnique(bool reuse_current, std::string* ErrMsg) {
868   bool Exists;
869   if (reuse_current && (fs::exists(path, Exists) || !Exists))
870     return false; // File doesn't exist already, just use it!
871
872   // Reserve space for -XXXXXX at the end.
873   char *FNBuffer = (char*) alloca(path.size()+8);
874   unsigned offset = path.size();
875   path.copy(FNBuffer, offset);
876
877   // Find a numeric suffix that isn't used by an existing file.  Assume there
878   // won't be more than 1 million files with the same prefix.  Probably a safe
879   // bet.
880   static int FCounter = -1;
881   if (FCounter < 0) {
882     // Give arbitrary initial seed.
883     // FIXME: We should use sys::fs::unique_file() in future.
884     LARGE_INTEGER cnt64;
885     DWORD x = GetCurrentProcessId();
886     x = (x << 16) | (x >> 16);
887     if (QueryPerformanceCounter(&cnt64))    // RDTSC
888       x ^= cnt64.HighPart ^ cnt64.LowPart;
889     FCounter = x % 1000000;
890   }
891   do {
892     sprintf(FNBuffer+offset, "-%06u", FCounter);
893     if (++FCounter > 999999)
894       FCounter = 0;
895     path = FNBuffer;
896   } while (!fs::exists(path, Exists) && Exists);
897   return false;
898 }
899
900 bool
901 Path::createTemporaryFileOnDisk(bool reuse_current, std::string* ErrMsg) {
902   // Make this into a unique file name
903   makeUnique(reuse_current, ErrMsg);
904
905   // Now go and create it
906   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
907                         FILE_ATTRIBUTE_NORMAL, NULL);
908   if (h == INVALID_HANDLE_VALUE)
909     return MakeErrMsg(ErrMsg, path + ": can't create file");
910
911   CloseHandle(h);
912   return false;
913 }
914
915 /// MapInFilePages - Not yet implemented on win32.
916 const char *Path::MapInFilePages(int FD, size_t FileSize, off_t Offset) {
917   return 0;
918 }
919
920 /// MapInFilePages - Not yet implemented on win32.
921 void Path::UnMapFilePages(const char *Base, size_t FileSize) {
922   assert(0 && "NOT IMPLEMENTED");
923 }
924
925 }
926 }