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