elimiante some syscalls
[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   std::string actualMagic;
285   if (!getMagicNumber(actualMagic, 4))
286     return false;
287   return actualMagic == "llvc" || actualMagic == "llvm";
288 }
289
290 bool
291 Path::exists() const {
292   DWORD attr = GetFileAttributes(path.c_str());
293   return attr != INVALID_FILE_ATTRIBUTES;
294 }
295
296 bool
297 Path::canRead() const {
298   // FIXME: take security attributes into account.
299   DWORD attr = GetFileAttributes(path.c_str());
300   return attr != INVALID_FILE_ATTRIBUTES;
301 }
302
303 bool
304 Path::canWrite() const {
305   // FIXME: take security attributes into account.
306   DWORD attr = GetFileAttributes(path.c_str());
307   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
308 }
309
310 bool
311 Path::canExecute() const {
312   // FIXME: take security attributes into account.
313   DWORD attr = GetFileAttributes(path.c_str());
314   return attr != INVALID_FILE_ATTRIBUTES;
315 }
316
317 std::string
318 Path::getLast() const {
319   // Find the last slash
320   size_t pos = path.rfind('/');
321
322   // Handle the corner cases
323   if (pos == std::string::npos)
324     return path;
325
326   // If the last character is a slash, we have a root directory
327   if (pos == path.length()-1)
328     return path;
329
330   // Return everything after the last slash
331   return path.substr(pos+1);
332 }
333
334 bool
335 Path::getFileStatus(FileStatus &info, std::string *ErrStr) const {
336   WIN32_FILE_ATTRIBUTE_DATA fi;
337   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
338     return GetError("getStatusInfo():" + std::string(path) +
339                     ": Can't get status: ", ErrStr);
340
341   info.fileSize = fi.nFileSizeHigh;
342   info.fileSize <<= sizeof(fi.nFileSizeHigh)*8;
343   info.fileSize += fi.nFileSizeLow;
344
345   info.mode = fi.dwFileAttributes & FILE_ATTRIBUTE_READONLY ? 0555 : 0777;
346   info.user = 9999;    // Not applicable to Windows, so...
347   info.group = 9999;   // Not applicable to Windows, so...
348
349   __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
350   info.modTime.fromWin32Time(ft);
351
352   info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
353   return false;
354 }
355
356 static bool AddPermissionBits(const std::string& Filename, int bits) {
357   DWORD attr = GetFileAttributes(Filename.c_str());
358
359   // If it doesn't exist, we're done.
360   if (attr == INVALID_FILE_ATTRIBUTES)
361     return false;
362
363   // The best we can do to interpret Unix permission bits is to use
364   // the owner writable bit.
365   if ((attr & FILE_ATTRIBUTE_READONLY) && (bits & 0200)) {
366     if (!SetFileAttributes(Filename.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
367       ThrowError(Filename + ": SetFileAttributes: ");
368   }
369   return true;
370 }
371
372 void Path::makeReadableOnDisk() {
373   // All files are readable on Windows (ignoring security attributes).
374 }
375
376 void Path::makeWriteableOnDisk() {
377   DWORD attr = GetFileAttributes(path.c_str());
378
379   // If it doesn't exist, we're done.
380   if (attr == INVALID_FILE_ATTRIBUTES)
381     return;
382
383   if (attr & FILE_ATTRIBUTE_READONLY) {
384     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
385       ThrowError(std::string(path) + ": Can't make file writable: ");
386   }
387 }
388
389 void Path::makeExecutableOnDisk() {
390   // All files are executable on Windows (ignoring security attributes).
391 }
392
393 bool
394 Path::getDirectoryContents(std::set<Path>& result) const {
395   if (!isDirectory())
396     return false;
397
398   result.clear();
399   WIN32_FIND_DATA fd;
400   std::string searchpath = path;
401   if (path.size() == 0 || searchpath[path.size()-1] == '/')
402     searchpath += "*";
403   else
404     searchpath += "/*";
405
406   HANDLE h = FindFirstFile(searchpath.c_str(), &fd);
407   if (h == INVALID_HANDLE_VALUE) {
408     if (GetLastError() == ERROR_FILE_NOT_FOUND)
409       return true; // not really an error, now is it?
410     ThrowError(path + ": Can't read directory: ");
411   }
412
413   do {
414     if (fd.cFileName[0] == '.')
415       continue;
416     Path aPath(path);
417     aPath.appendComponent(&fd.cFileName[0]);
418     result.insert(aPath);
419   } while (FindNextFile(h, &fd));
420
421   DWORD err = GetLastError();
422   FindClose(h);
423   if (err != ERROR_NO_MORE_FILES) {
424     SetLastError(err);
425     ThrowError(path + ": Can't read directory: ");
426   }
427   return true;
428 }
429
430 bool
431 Path::set(const std::string& a_path) {
432   if (a_path.size() == 0)
433     return false;
434   std::string save(path);
435   path = a_path;
436   FlipBackSlashes(path);
437   if (!isValid()) {
438     path = save;
439     return false;
440   }
441   return true;
442 }
443
444 bool
445 Path::appendComponent(const std::string& name) {
446   if (name.empty())
447     return false;
448   std::string save(path);
449   if (!path.empty()) {
450     size_t last = path.size() - 1;
451     if (path[last] != '/')
452       path += '/';
453   }
454   path += name;
455   if (!isValid()) {
456     path = save;
457     return false;
458   }
459   return true;
460 }
461
462 bool
463 Path::eraseComponent() {
464   size_t slashpos = path.rfind('/',path.size());
465   if (slashpos == path.size() - 1 || slashpos == std::string::npos)
466     return false;
467   std::string save(path);
468   path.erase(slashpos);
469   if (!isValid()) {
470     path = save;
471     return false;
472   }
473   return true;
474 }
475
476 bool
477 Path::appendSuffix(const std::string& suffix) {
478   std::string save(path);
479   path.append(".");
480   path.append(suffix);
481   if (!isValid()) {
482     path = save;
483     return false;
484   }
485   return true;
486 }
487
488 bool
489 Path::eraseSuffix() {
490   size_t dotpos = path.rfind('.',path.size());
491   size_t slashpos = path.rfind('/',path.size());
492   if (dotpos != std::string::npos) {
493     if (slashpos == std::string::npos || dotpos > slashpos+1) {
494       std::string save(path);
495       path.erase(dotpos, path.size()-dotpos);
496       if (!isValid()) {
497         path = save;
498         return false;
499       }
500       return true;
501     }
502   }
503   return false;
504 }
505
506 bool
507 Path::createDirectoryOnDisk(bool create_parents) {
508   // Get a writeable copy of the path name
509   size_t len = path.length();
510   char *pathname = reinterpret_cast<char *>(_alloca(len+2));
511   path.copy(pathname, len);
512   pathname[len] = 0;
513
514   // Make sure it ends with a slash.
515   if (len == 0 || pathname[len - 1] != '/') {
516     pathname[len] = '/';
517     pathname[++len] = 0;
518   }
519
520   // Determine starting point for initial / search.
521   char *next = pathname;
522   if (pathname[0] == '/' && pathname[1] == '/') {
523     // Skip host name.
524     next = strchr(pathname+2, '/');
525     if (next == NULL)
526       throw std::string(pathname) + ": badly formed remote directory";
527     // Skip share name.
528     next = strchr(next+1, '/');
529     if (next == NULL)
530       throw std::string(pathname) + ": badly formed remote directory";
531     next++;
532     if (*next == 0)
533       throw std::string(pathname) + ": badly formed remote directory";
534   } else {
535     if (pathname[1] == ':')
536       next += 2;    // skip drive letter
537     if (*next == '/')
538       next++;       // skip root directory
539   }
540
541   // If we're supposed to create intermediate directories
542   if (create_parents) {
543     // Loop through the directory components until we're done
544     while (*next) {
545       next = strchr(next, '/');
546       *next = 0;
547       if (!CreateDirectory(pathname, NULL))
548           ThrowError(std::string(pathname) + ": Can't create directory: ");
549       *next++ = '/';
550     }
551   } else {
552     // Drop trailing slash.
553     pathname[len-1] = 0;
554     if (!CreateDirectory(pathname, NULL)) {
555       ThrowError(std::string(pathname) + ": Can't create directory: ");
556     }
557   }
558   return true;
559 }
560
561 bool
562 Path::createFileOnDisk() {
563   // Create the file
564   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
565                         FILE_ATTRIBUTE_NORMAL, NULL);
566   if (h == INVALID_HANDLE_VALUE)
567     ThrowError(path + ": Can't create file: ");
568
569   CloseHandle(h);
570   return true;
571 }
572
573 bool
574 Path::eraseFromDisk(bool remove_contents, std::string *ErrStr) const {
575   FileStatus Status;
576   if (getFileStatus(Status, ErrStr))
577     return true;
578     
579   if (Status.isFile) {
580     DWORD attr = GetFileAttributes(path.c_str());
581
582     // If it doesn't exist, we're done.
583     if (attr == INVALID_FILE_ATTRIBUTES)
584       return true;
585
586     // Read-only files cannot be deleted on Windows.  Must remove the read-only
587     // attribute first.
588     if (attr & FILE_ATTRIBUTE_READONLY) {
589       if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
590         return GetError(path + ": Can't destroy file: ", ErrStr);
591     }
592
593     if (!DeleteFile(path.c_str()))
594       ThrowError(path + ": Can't destroy file: ");
595     return false;
596   } else if (Status.isDir) {
597     // If it doesn't exist, we're done.
598     if (!exists())
599       return false;
600
601     char *pathname = reinterpret_cast<char *>(_alloca(path.length()+3));
602     int lastchar = path.length() - 1 ;
603     path.copy(pathname, lastchar+1);
604
605     // Make path end with '/*'.
606     if (pathname[lastchar] != '/')
607       pathname[++lastchar] = '/';
608     pathname[lastchar+1] = '*';
609     pathname[lastchar+2] = 0;
610
611     if (remove_contents) {
612       WIN32_FIND_DATA fd;
613       HANDLE h = FindFirstFile(pathname, &fd);
614
615       // It's a bad idea to alter the contents of a directory while enumerating
616       // its contents. So build a list of its contents first, then destroy them.
617
618       if (h != INVALID_HANDLE_VALUE) {
619         std::vector<Path> list;
620
621         do {
622           if (strcmp(fd.cFileName, ".") == 0)
623             continue;
624           if (strcmp(fd.cFileName, "..") == 0)
625             continue;
626
627           Path aPath(path);
628           aPath.appendComponent(&fd.cFileName[0]);
629           list.push_back(aPath);
630         } while (FindNextFile(h, &fd));
631
632         DWORD err = GetLastError();
633         FindClose(h);
634         if (err != ERROR_NO_MORE_FILES) {
635           SetLastError(err);
636           return GetError(path + ": Can't read directory: ", ErrStr);
637         }
638
639         for (std::vector<Path>::iterator I = list.begin(); I != list.end();
640              ++I) {
641           Path &aPath = *I;
642           aPath.eraseFromDisk(true);
643         }
644       } else {
645         if (GetLastError() != ERROR_FILE_NOT_FOUND)
646           return GetError(path + ": Can't read directory: ", ErrStr);
647       }
648     }
649
650     pathname[lastchar] = 0;
651     if (!RemoveDirectory(pathname))
652       return GetError(std::string(pathname) + ": Can't destroy directory: ",
653                       ErrStr);
654     return false;
655   } else {
656     // It appears the path doesn't exist.
657     return true;
658   }
659 }
660
661 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
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 }