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