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