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