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