For PR351:
[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 <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 Path::makeReadable() {
291 }
292
293 void Path::makeWriteable() {
294 }
295
296 void Path::makeExecutable() {
297 }
298
299 bool
300 Path::setDirectory(const std::string& a_path) {
301   if (a_path.size() == 0)
302     return false;
303   Path save(*this);
304   path = a_path;
305   FlipBackSlashes(path);
306   size_t last = a_path.size() -1;
307   if (a_path[last] != '/')
308     path += '/';
309   if (!isValid()) {
310     path = save.path;
311     return false;
312   }
313   return true;
314 }
315
316 bool
317 Path::setFile(const std::string& a_path) {
318   if (a_path.size() == 0)
319     return false;
320   Path save(*this);
321   path = a_path;
322   FlipBackSlashes(path);
323   size_t last = a_path.size() - 1;
324   while (last > 0 && a_path[last] == '/')
325     last--;
326   path.erase(last+1);
327   if (!isValid()) {
328     path = save.path;
329     return false;
330   }
331   return true;
332 }
333
334 bool
335 Path::appendDirectory(const std::string& dir) {
336   if (isFile())
337     return false;
338   Path save(*this);
339   path += dir;
340   path += "/";
341   if (!isValid()) {
342     path = save.path;
343     return false;
344   }
345   return true;
346 }
347
348 bool
349 Path::elideDirectory() {
350   if (isFile())
351     return false;
352   size_t slashpos = path.rfind('/',path.size());
353   if (slashpos == 0 || slashpos == std::string::npos)
354     return false;
355   if (slashpos == path.size() - 1)
356     slashpos = path.rfind('/',slashpos-1);
357   if (slashpos == std::string::npos)
358     return false;
359   path.erase(slashpos);
360   return true;
361 }
362
363 bool
364 Path::appendFile(const std::string& file) {
365   if (!isDirectory())
366     return false;
367   Path save(*this);
368   path += file;
369   if (!isValid()) {
370     path = save.path;
371     return false;
372   }
373   return true;
374 }
375
376 bool
377 Path::elideFile() {
378   if (isDirectory())
379     return false;
380   size_t slashpos = path.rfind('/',path.size());
381   if (slashpos == std::string::npos)
382     return false;
383   path.erase(slashpos+1);
384   return true;
385 }
386
387 bool
388 Path::appendSuffix(const std::string& suffix) {
389   if (isDirectory())
390     return false;
391   Path save(*this);
392   path.append(".");
393   path.append(suffix);
394   if (!isValid()) {
395     path = save.path;
396     return false;
397   }
398   return true;
399 }
400
401 bool
402 Path::elideSuffix() {
403   if (isDirectory()) return false;
404   size_t dotpos = path.rfind('.',path.size());
405   size_t slashpos = path.rfind('/',path.size());
406   if (slashpos != std::string::npos && dotpos != std::string::npos &&
407       dotpos > slashpos) {
408     path.erase(dotpos, path.size()-dotpos);
409     return true;
410   }
411   return false;
412 }
413
414
415 bool
416 Path::createDirectory( bool create_parents) {
417   // Make sure we're dealing with a directory
418   if (!isDirectory()) return false;
419
420   // Get a writeable copy of the path name
421   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
422   path.copy(pathname,path.length());
423   pathname[path.length()] = 0;
424
425   // Determine starting point for initial / search.
426   char *next = pathname;
427   if (pathname[0] == '/' && pathname[1] == '/') {
428     // Skip host name.
429     next = strchr(pathname+2, '/');
430     if (next == NULL)
431       throw std::string(pathname) + ": badly formed remote directory";
432     // Skip share name.
433     next = strchr(next+1, '/');
434     if (next == NULL)
435       throw std::string(pathname) + ": badly formed remote directory";
436     next++;
437     if (*next == 0)
438       throw std::string(pathname) + ": badly formed remote directory";
439   } else {
440     if (pathname[1] == ':')
441       next += 2;    // skip drive letter
442     if (*next == '/')
443       next++;       // skip root directory
444   }
445
446   // If we're supposed to create intermediate directories
447   if (create_parents) {
448     // Loop through the directory components until we're done
449     while (*next) {
450       next = strchr(next, '/');
451       *next = 0;
452       if (!CreateDirectory(pathname, NULL))
453           ThrowError(std::string(pathname) + ": Can't create directory: ");
454       *next++ = '/';
455     }
456   } else {
457     // Drop trailing slash.
458     pathname[path.size()-1] = 0;
459     if (!CreateDirectory(pathname, NULL)) {
460       ThrowError(std::string(pathname) + ": Can't create directory: ");
461     }
462   }
463   return true;
464 }
465
466 bool
467 Path::createFile() {
468   // Make sure we're dealing with a file
469   if (!isFile()) return false;
470
471   // Create the file
472   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
473                         FILE_ATTRIBUTE_NORMAL, NULL);
474   if (h == INVALID_HANDLE_VALUE)
475     ThrowError(std::string(path.c_str()) + ": Can't create file: ");
476
477   CloseHandle(h);
478   return true;
479 }
480
481 bool
482 Path::destroyDirectory(bool remove_contents) {
483   // Make sure we're dealing with a directory
484   if (!isDirectory()) return false;
485
486   // If it doesn't exist, we're done.
487   if (!exists()) return true;
488
489   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
490   path.copy(pathname,path.length()+1);
491   int lastchar = path.length() - 1 ;
492   if (pathname[lastchar] == '/')
493     pathname[lastchar] = 0;
494
495   if (remove_contents) {
496     // Recursively descend the directory to remove its content
497     // FIXME: The correct way of doing this on Windows isn't pretty...
498     // but this may work if unix-like utils are present.
499     std::string cmd("rm -rf ");
500     cmd += path;
501     system(cmd.c_str());
502   } else {
503     // Otherwise, try to just remove the one directory
504     if (!RemoveDirectory(pathname))
505       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
506   }
507   return true;
508 }
509
510 bool
511 Path::destroyFile() {
512   if (!isFile()) return false;
513
514   DWORD attr = GetFileAttributes(path.c_str());
515
516   // If it doesn't exist, we're done.
517   if (attr == INVALID_FILE_ATTRIBUTES)
518     return true;
519
520   // Read-only files cannot be deleted on Windows.  Must remove the read-only
521   // attribute first.
522   if (attr & FILE_ATTRIBUTE_READONLY) {
523     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
524       ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
525   }
526
527   if (!DeleteFile(path.c_str()))
528     ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
529   return true;
530 }
531
532 }
533 }
534
535 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
536