Implement Win32 Path::getStatusInfo(), TimeValue::toString()
[oota-llvm.git] / lib / System / Win32 / Path.cpp
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 <fstream>
24 #include <malloc.h>
25
26 static void FlipBackSlashes(std::string& s) {
27   for (size_t i = 0; i < s.size(); i++)
28     if (s[i] == '\\')
29       s[i] = '/';
30 }
31
32 namespace llvm {
33 namespace sys {
34
35 bool
36 Path::isValid() const {
37   if (path.empty())
38     return false;
39
40   // If there is a colon, it must be the second character, preceded by a letter
41   // and followed by something.
42   size_t len = path.size();
43   size_t pos = path.rfind(':',len);
44   if (pos != std::string::npos) {
45     if (pos != 1 || !isalpha(path[0]) || len < 3)
46       return false;
47   }
48
49   // Check for illegal characters.
50   if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
51                          "\013\014\015\016\017\020\021\022\023\024\025\026"
52                          "\027\030\031\032\033\034\035\036\037")
53       != std::string::npos)
54     return false;
55
56   // A file or directory name may not end in a period.
57   if (path[len-1] == '.')
58     return false;
59   if (len >= 2 && path[len-2] == '.' && path[len-1] == '/')
60     return false;
61
62   // A file or directory name may not end in a space.
63   if (path[len-1] == ' ')
64     return false;
65   if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/')
66     return false;
67
68   return true;
69 }
70
71 static Path *TempDirectory = NULL;
72
73 Path
74 Path::GetTemporaryDirectory() {
75   if (TempDirectory)
76     return *TempDirectory;
77
78   char pathname[MAX_PATH];
79   if (!GetTempPath(MAX_PATH, pathname))
80     throw std::string("Can't determine temporary directory");
81
82   Path result;
83   result.setDirectory(pathname);
84
85   // Append a subdirectory passed on our process id so multiple LLVMs don't
86   // step on each other's toes.
87   sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
88   result.appendDirectory(pathname);
89
90   // If there's a directory left over from a previous LLVM execution that
91   // happened to have the same process id, get rid of it.
92   result.destroyDirectory(true);
93
94   // And finally (re-)create the empty directory.
95   result.createDirectory(false);
96   TempDirectory = new Path(result);
97   return *TempDirectory;
98 }
99
100 Path::Path(const std::string& unverified_path)
101   : path(unverified_path)
102 {
103   FlipBackSlashes(path);
104   if (unverified_path.empty())
105     return;
106   if (this->isValid())
107     return;
108   // oops, not valid.
109   path.clear();
110   throw std::string(unverified_path + ": path is not valid");
111 }
112
113 // FIXME: the following set of functions don't map to Windows very well.
114 Path
115 Path::GetRootDirectory() {
116   Path result;
117   result.setDirectory("/");
118   return result;
119 }
120
121 static void getPathList(const char*path, std::vector<sys::Path>& Paths) {
122   const char* at = path;
123   const char* delim = strchr(at, ';');
124   Path tmpPath;
125   while( delim != 0 ) {
126     std::string tmp(at, size_t(delim-at));
127     if (tmpPath.setDirectory(tmp))
128       if (tmpPath.readable())
129         Paths.push_back(tmpPath);
130     at = delim + 1;
131     delim = strchr(at, ';');
132   }
133   if (*at != 0)
134     if (tmpPath.setDirectory(std::string(at)))
135       if (tmpPath.readable())
136         Paths.push_back(tmpPath);
137
138 }
139
140 void 
141 Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
142 #ifdef LTDL_SHLIBPATH_VAR
143   char* env_var = getenv(LTDL_SHLIBPATH_VAR);
144   if (env_var != 0) {
145     getPathList(env_var,Paths);
146   }
147 #endif
148   // FIXME: Should this look at LD_LIBRARY_PATH too?
149   Paths.push_back(sys::Path("C:\\WINDOWS\\SYSTEM32\\"));
150   Paths.push_back(sys::Path("C:\\WINDOWS\\"));
151 }
152
153 void
154 Path::GetBytecodeLibraryPaths(std::vector<sys::Path>& Paths) {
155   char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
156   if (env_var != 0) {
157     getPathList(env_var,Paths);
158   }
159 #ifdef LLVMGCCDIR
160   {
161     Path tmpPath(std::string(LLVMGCCDIR) + "bytecode-libs/");
162     if (tmpPath.readable())
163       Paths.push_back(tmpPath);
164   }
165 #endif
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   return Path("/etc/llvm/");
180 }
181
182 Path
183 Path::GetLLVMConfigDir() {
184   return GetLLVMDefaultConfigDir();
185 }
186
187 Path
188 Path::GetUserHomeDirectory() {
189   const char* home = getenv("HOME");
190   if (home) {
191     Path result;
192     if (result.setDirectory(home))
193       return result;
194   }
195   return GetRootDirectory();
196 }
197 // FIXME: the above set of functions don't map to Windows very well.
198
199 bool
200 Path::isFile() const {
201   return (isValid() && path[path.length()-1] != '/');
202 }
203
204 bool
205 Path::isDirectory() const {
206   return (isValid() && path[path.length()-1] == '/');
207 }
208
209 std::string
210 Path::getBasename() const {
211   // Find the last slash
212   size_t slash = path.rfind('/');
213   if (slash == std::string::npos)
214     slash = 0;
215   else
216     slash++;
217
218   return path.substr(slash, path.rfind('.'));
219 }
220
221 bool Path::hasMagicNumber(const std::string &Magic) const {
222   size_t len = Magic.size();
223   char *buf = reinterpret_cast<char *>(_alloca(len+1));
224   std::ifstream f(path.c_str());
225   f.read(buf, len);
226   buf[len] = '\0';
227   return Magic == buf;
228 }
229
230 bool 
231 Path::isBytecodeFile() const {
232   char buffer[ 4];
233   buffer[0] = 0;
234   std::ifstream f(path.c_str());
235   f.read(buffer, 4);
236   if (f.bad())
237     ThrowErrno("can't read file signature");
238   return 0 == memcmp(buffer,"llvc",4) || 0 == memcmp(buffer,"llvm",4);
239 }
240
241 bool
242 Path::exists() const {
243   DWORD attr = GetFileAttributes(path.c_str());
244   return attr != INVALID_FILE_ATTRIBUTES;
245 }
246
247 bool
248 Path::readable() const {
249   // FIXME: take security attributes into account.
250   DWORD attr = GetFileAttributes(path.c_str());
251   return attr != INVALID_FILE_ATTRIBUTES;
252 }
253
254 bool
255 Path::writable() const {
256   // FIXME: take security attributes into account.
257   DWORD attr = GetFileAttributes(path.c_str());
258   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
259 }
260
261 bool
262 Path::executable() const {
263   // FIXME: take security attributes into account.
264   DWORD attr = GetFileAttributes(path.c_str());
265   return attr != INVALID_FILE_ATTRIBUTES;
266 }
267
268 std::string
269 Path::getLast() const {
270   // Find the last slash
271   size_t pos = path.rfind('/');
272
273   // Handle the corner cases
274   if (pos == std::string::npos)
275     return path;
276
277   // If the last character is a slash
278   if (pos == path.length()-1) {
279     // Find the second to last slash
280     size_t pos2 = path.rfind('/', pos-1);
281     if (pos2 == std::string::npos)
282       return path.substr(0,pos);
283     else
284       return path.substr(pos2+1,pos-pos2-1);
285   }
286   // Return everything after the last slash
287   return path.substr(pos+1);
288 }
289
290 void
291 Path::getStatusInfo(StatusInfo& info) const {
292   WIN32_FILE_ATTRIBUTE_DATA fi;
293   if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fi))
294     ThrowError(std::string(path) + ": Can't get status: ");
295
296   info.fileSize = fi.nFileSizeHigh;
297   info.fileSize <<= 32;
298   info.fileSize += fi.nFileSizeLow;
299
300   info.mode = 0777;    // Not applicable to Windows, so...
301   info.user = 9999;    // Not applicable to Windows, so...
302   info.group = 9999;   // Not applicable to Windows, so...
303
304   __int64 ft = *reinterpret_cast<__int64*>(&fi.ftLastWriteTime);
305   info.modTime.fromWin32Time(ft);
306
307   info.isDir = fi.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
308   if (info.isDir && path[path.length() - 1] != '/')
309     path += '/';
310   else if (!info.isDir && path[path.length() - 1] == '/')
311     path.erase(path.length() - 1);
312 }
313
314 void Path::makeReadable() {
315   // All files are readable on Windows (ignoring security attributes).
316 }
317
318 void Path::makeWriteable() {
319   DWORD attr = GetFileAttributes(path.c_str());
320
321   // If it doesn't exist, we're done.
322   if (attr == INVALID_FILE_ATTRIBUTES)
323     return;
324
325   if (attr & FILE_ATTRIBUTE_READONLY) {
326     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
327       ThrowError(std::string(path) + ": Can't make file writable: ");
328   }
329 }
330
331 void Path::makeExecutable() {
332   // All files are executable on Windows (ignoring security attributes).
333 }
334
335 bool
336 Path::setDirectory(const std::string& a_path) {
337   if (a_path.size() == 0)
338     return false;
339   Path save(*this);
340   path = a_path;
341   FlipBackSlashes(path);
342   size_t last = a_path.size() -1;
343   if (a_path[last] != '/')
344     path += '/';
345   if (!isValid()) {
346     path = save.path;
347     return false;
348   }
349   return true;
350 }
351
352 bool
353 Path::setFile(const std::string& a_path) {
354   if (a_path.size() == 0)
355     return false;
356   Path save(*this);
357   path = a_path;
358   FlipBackSlashes(path);
359   size_t last = a_path.size() - 1;
360   while (last > 0 && a_path[last] == '/')
361     last--;
362   path.erase(last+1);
363   if (!isValid()) {
364     path = save.path;
365     return false;
366   }
367   return true;
368 }
369
370 bool
371 Path::appendDirectory(const std::string& dir) {
372   if (isFile())
373     return false;
374   Path save(*this);
375   path += dir;
376   path += "/";
377   if (!isValid()) {
378     path = save.path;
379     return false;
380   }
381   return true;
382 }
383
384 bool
385 Path::elideDirectory() {
386   if (isFile())
387     return false;
388   size_t slashpos = path.rfind('/',path.size());
389   if (slashpos == 0 || slashpos == std::string::npos)
390     return false;
391   if (slashpos == path.size() - 1)
392     slashpos = path.rfind('/',slashpos-1);
393   if (slashpos == std::string::npos)
394     return false;
395   path.erase(slashpos);
396   return true;
397 }
398
399 bool
400 Path::appendFile(const std::string& file) {
401   if (!isDirectory())
402     return false;
403   Path save(*this);
404   path += file;
405   if (!isValid()) {
406     path = save.path;
407     return false;
408   }
409   return true;
410 }
411
412 bool
413 Path::elideFile() {
414   if (isDirectory())
415     return false;
416   size_t slashpos = path.rfind('/',path.size());
417   if (slashpos == std::string::npos)
418     return false;
419   path.erase(slashpos+1);
420   return true;
421 }
422
423 bool
424 Path::appendSuffix(const std::string& suffix) {
425   if (isDirectory())
426     return false;
427   Path save(*this);
428   path.append(".");
429   path.append(suffix);
430   if (!isValid()) {
431     path = save.path;
432     return false;
433   }
434   return true;
435 }
436
437 bool
438 Path::elideSuffix() {
439   if (isDirectory()) return false;
440   size_t dotpos = path.rfind('.',path.size());
441   size_t slashpos = path.rfind('/',path.size());
442   if (slashpos != std::string::npos && dotpos != std::string::npos &&
443       dotpos > slashpos) {
444     path.erase(dotpos, path.size()-dotpos);
445     return true;
446   }
447   return false;
448 }
449
450
451 bool
452 Path::createDirectory( bool create_parents) {
453   // Make sure we're dealing with a directory
454   if (!isDirectory()) return false;
455
456   // Get a writeable copy of the path name
457   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
458   path.copy(pathname,path.length());
459   pathname[path.length()] = 0;
460
461   // Determine starting point for initial / search.
462   char *next = pathname;
463   if (pathname[0] == '/' && pathname[1] == '/') {
464     // Skip host name.
465     next = strchr(pathname+2, '/');
466     if (next == NULL)
467       throw std::string(pathname) + ": badly formed remote directory";
468     // Skip share name.
469     next = strchr(next+1, '/');
470     if (next == NULL)
471       throw std::string(pathname) + ": badly formed remote directory";
472     next++;
473     if (*next == 0)
474       throw std::string(pathname) + ": badly formed remote directory";
475   } else {
476     if (pathname[1] == ':')
477       next += 2;    // skip drive letter
478     if (*next == '/')
479       next++;       // skip root directory
480   }
481
482   // If we're supposed to create intermediate directories
483   if (create_parents) {
484     // Loop through the directory components until we're done
485     while (*next) {
486       next = strchr(next, '/');
487       *next = 0;
488       if (!CreateDirectory(pathname, NULL))
489           ThrowError(std::string(pathname) + ": Can't create directory: ");
490       *next++ = '/';
491     }
492   } else {
493     // Drop trailing slash.
494     pathname[path.size()-1] = 0;
495     if (!CreateDirectory(pathname, NULL)) {
496       ThrowError(std::string(pathname) + ": Can't create directory: ");
497     }
498   }
499   return true;
500 }
501
502 bool
503 Path::createFile() {
504   // Make sure we're dealing with a file
505   if (!isFile()) return false;
506
507   // Create the file
508   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
509                         FILE_ATTRIBUTE_NORMAL, NULL);
510   if (h == INVALID_HANDLE_VALUE)
511     ThrowError(std::string(path.c_str()) + ": Can't create file: ");
512
513   CloseHandle(h);
514   return true;
515 }
516
517 bool
518 Path::destroyDirectory(bool remove_contents) {
519   // Make sure we're dealing with a directory
520   if (!isDirectory()) return false;
521
522   // If it doesn't exist, we're done.
523   if (!exists()) return true;
524
525   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
526   path.copy(pathname,path.length()+1);
527   int lastchar = path.length() - 1 ;
528   if (pathname[lastchar] == '/')
529     pathname[lastchar] = 0;
530
531   if (remove_contents) {
532     // Recursively descend the directory to remove its content
533     // FIXME: The correct way of doing this on Windows isn't pretty...
534     // but this may work if unix-like utils are present.
535     std::string cmd("rm -rf ");
536     cmd += path;
537     system(cmd.c_str());
538   } else {
539     // Otherwise, try to just remove the one directory
540     if (!RemoveDirectory(pathname))
541       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
542   }
543   return true;
544 }
545
546 bool
547 Path::destroyFile() {
548   if (!isFile()) return false;
549
550   DWORD attr = GetFileAttributes(path.c_str());
551
552   // If it doesn't exist, we're done.
553   if (attr == INVALID_FILE_ATTRIBUTES)
554     return true;
555
556   // Read-only files cannot be deleted on Windows.  Must remove the read-only
557   // attribute first.
558   if (attr & FILE_ATTRIBUTE_READONLY) {
559     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
560       ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
561   }
562
563   if (!DeleteFile(path.c_str()))
564     ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
565   return true;
566 }
567
568 }
569 }
570
571 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
572