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