Dynamic Library abstraction. This makes the abstraction of a single dynamic
[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(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 std::string
122 Path::GetDLLSuffix() {
123   return "dll";
124 }
125
126 static inline bool IsLibrary(Path& path, const std::string& basename) {
127   if (path.appendFile(std::string("lib") + basename)) {
128     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
129       return true;
130     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
131       return true;
132     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
133       return true;
134     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
135       return true;
136   } else if (path.elideFile() && path.appendFile(basename)) {
137     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
138       return true;
139     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
140       return true;
141     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
142       return true;
143     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
144       return true;
145   }
146   path.clear();
147   return false;
148 }
149
150 Path 
151 Path::GetLibraryPath(const std::string& basename, 
152                      const std::vector<std::string>& LibPaths) {
153   Path result;
154
155   // Try the paths provided
156   for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
157        E = LibPaths.end(); I != E; ++I ) {
158     if (result.setDirectory(*I) && IsLibrary(result,basename))
159       return result;
160   }
161
162   // Try the LLVM lib directory in the LLVM install area
163   //if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
164   //  return result;
165
166   // Try /usr/lib
167   if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
168     return result;
169
170   // Try /lib
171   if (result.setDirectory("/lib/") && IsLibrary(result,basename))
172     return result;
173
174   // Can't find it, give up and return invalid path.
175   result.clear();
176   return result;
177 }
178
179 Path
180 Path::GetSystemLibraryPath1() {
181   return Path("/lib/");
182 }
183
184 Path
185 Path::GetSystemLibraryPath2() {
186   return Path("/usr/lib/");
187 }
188
189 Path
190 Path::GetLLVMDefaultConfigDir() {
191   return Path("/etc/llvm/");
192 }
193
194 Path
195 Path::GetLLVMConfigDir() {
196   return GetLLVMDefaultConfigDir();
197 }
198
199 Path
200 Path::GetUserHomeDirectory() {
201   const char* home = getenv("HOME");
202   if (home) {
203     Path result;
204     if (result.setDirectory(home))
205       return result;
206   }
207   return GetRootDirectory();
208 }
209 // FIXME: the above set of functions don't map to Windows very well.
210
211 bool
212 Path::isFile() const {
213   return (isValid() && path[path.length()-1] != '/');
214 }
215
216 bool
217 Path::isDirectory() const {
218   return (isValid() && path[path.length()-1] == '/');
219 }
220
221 std::string
222 Path::getBasename() const {
223   // Find the last slash
224   size_t slash = path.rfind('/');
225   if (slash == std::string::npos)
226     slash = 0;
227   else
228     slash++;
229
230   return path.substr(slash, path.rfind('.'));
231 }
232
233 bool Path::hasMagicNumber(const std::string &Magic) const {
234   size_t len = Magic.size();
235   char *buf = reinterpret_cast<char *>(_alloca(len+1));
236   std::ifstream f(path.c_str());
237   f.read(buf, len);
238   buf[len] = '\0';
239   return Magic == buf;
240 }
241
242 bool 
243 Path::isBytecodeFile() const {
244   char buffer[ 4];
245   buffer[0] = 0;
246   std::ifstream f(path.c_str());
247   f.read(buffer, 4);
248   if (f.bad())
249     ThrowErrno("can't read file signature");
250   return 0 == memcmp(buffer,"llvc",4) || 0 == memcmp(buffer,"llvm",4);
251 }
252
253 bool
254 Path::isArchive() const {
255   if (readable()) {
256     return hasMagicNumber("!<arch>\012");
257   }
258   return false;
259 }
260
261 bool
262 Path::exists() const {
263   DWORD attr = GetFileAttributes(path.c_str());
264   return attr != INVALID_FILE_ATTRIBUTES;
265 }
266
267 bool
268 Path::readable() const {
269   // FIXME: take security attributes into account.
270   DWORD attr = GetFileAttributes(path.c_str());
271   return attr != INVALID_FILE_ATTRIBUTES;
272 }
273
274 bool
275 Path::writable() const {
276   // FIXME: take security attributes into account.
277   DWORD attr = GetFileAttributes(path.c_str());
278   return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
279 }
280
281 bool
282 Path::executable() const {
283   // FIXME: take security attributes into account.
284   DWORD attr = GetFileAttributes(path.c_str());
285   return attr != INVALID_FILE_ATTRIBUTES;
286 }
287
288 std::string
289 Path::getLast() const {
290   // Find the last slash
291   size_t pos = path.rfind('/');
292
293   // Handle the corner cases
294   if (pos == std::string::npos)
295     return path;
296
297   // If the last character is a slash
298   if (pos == path.length()-1) {
299     // Find the second to last slash
300     size_t pos2 = path.rfind('/', pos-1);
301     if (pos2 == std::string::npos)
302       return path.substr(0,pos);
303     else
304       return path.substr(pos2+1,pos-pos2-1);
305   }
306   // Return everything after the last slash
307   return path.substr(pos+1);
308 }
309
310 bool
311 Path::setDirectory(const std::string& a_path) {
312   if (a_path.size() == 0)
313     return false;
314   Path save(*this);
315   path = a_path;
316   FlipBackSlashes(path);
317   size_t last = a_path.size() -1;
318   if (last != 0 && a_path[last] != '/')
319     path += '/';
320   if (!isValid()) {
321     path = save.path;
322     return false;
323   }
324   return true;
325 }
326
327 bool
328 Path::setFile(const std::string& a_path) {
329   if (a_path.size() == 0)
330     return false;
331   Path save(*this);
332   path = a_path;
333   FlipBackSlashes(path);
334   size_t last = a_path.size() - 1;
335   while (last > 0 && a_path[last] == '/')
336     last--;
337   path.erase(last+1);
338   if (!isValid()) {
339     path = save.path;
340     return false;
341   }
342   return true;
343 }
344
345 bool
346 Path::appendDirectory(const std::string& dir) {
347   if (isFile())
348     return false;
349   Path save(*this);
350   path += dir;
351   path += "/";
352   if (!isValid()) {
353     path = save.path;
354     return false;
355   }
356   return true;
357 }
358
359 bool
360 Path::elideDirectory() {
361   if (isFile())
362     return false;
363   size_t slashpos = path.rfind('/',path.size());
364   if (slashpos == 0 || slashpos == std::string::npos)
365     return false;
366   if (slashpos == path.size() - 1)
367     slashpos = path.rfind('/',slashpos-1);
368   if (slashpos == std::string::npos)
369     return false;
370   path.erase(slashpos);
371   return true;
372 }
373
374 bool
375 Path::appendFile(const std::string& file) {
376   if (!isDirectory())
377     return false;
378   Path save(*this);
379   path += file;
380   if (!isValid()) {
381     path = save.path;
382     return false;
383   }
384   return true;
385 }
386
387 bool
388 Path::elideFile() {
389   if (isDirectory())
390     return false;
391   size_t slashpos = path.rfind('/',path.size());
392   if (slashpos == std::string::npos)
393     return false;
394   path.erase(slashpos+1);
395   return true;
396 }
397
398 bool
399 Path::appendSuffix(const std::string& suffix) {
400   if (isDirectory())
401     return false;
402   Path save(*this);
403   path.append(".");
404   path.append(suffix);
405   if (!isValid()) {
406     path = save.path;
407     return false;
408   }
409   return true;
410 }
411
412 bool
413 Path::elideSuffix() {
414   if (isDirectory()) return false;
415   size_t dotpos = path.rfind('.',path.size());
416   size_t slashpos = path.rfind('/',path.size());
417   if (slashpos != std::string::npos && dotpos != std::string::npos &&
418       dotpos > slashpos) {
419     path.erase(dotpos, path.size()-dotpos);
420     return true;
421   }
422   return false;
423 }
424
425
426 bool
427 Path::createDirectory( bool create_parents) {
428   // Make sure we're dealing with a directory
429   if (!isDirectory()) return false;
430
431   // Get a writeable copy of the path name
432   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
433   path.copy(pathname,path.length());
434   pathname[path.length()] = 0;
435
436   // Determine starting point for initial / search.
437   char *next = pathname;
438   if (pathname[0] == '/' && pathname[1] == '/') {
439     // Skip host name.
440     next = strchr(pathname+2, '/');
441     if (next == NULL)
442       throw std::string(pathname) + ": badly formed remote directory";
443     // Skip share name.
444     next = strchr(next+1, '/');
445     if (next == NULL)
446       throw std::string(pathname) + ": badly formed remote directory";
447     next++;
448     if (*next == 0)
449       throw std::string(pathname) + ": badly formed remote directory";
450   } else {
451     if (pathname[1] == ':')
452       next += 2;    // skip drive letter
453     if (*next == '/')
454       next++;       // skip root directory
455   }
456
457   // If we're supposed to create intermediate directories
458   if (create_parents) {
459     // Loop through the directory components until we're done
460     while (*next) {
461       next = strchr(next, '/');
462       *next = 0;
463       if (!CreateDirectory(pathname, NULL))
464           ThrowError(std::string(pathname) + ": Can't create directory: ");
465       *next++ = '/';
466     }
467   } else {
468     // Drop trailing slash.
469     pathname[path.size()-1] = 0;
470     if (!CreateDirectory(pathname, NULL)) {
471       ThrowError(std::string(pathname) + ": Can't create directory: ");
472     }
473   }
474   return true;
475 }
476
477 bool
478 Path::createFile() {
479   // Make sure we're dealing with a file
480   if (!isFile()) return false;
481
482   // Create the file
483   HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
484                         FILE_ATTRIBUTE_NORMAL, NULL);
485   if (h == INVALID_HANDLE_VALUE)
486     ThrowError(std::string(path.c_str()) + ": Can't create file: ");
487
488   CloseHandle(h);
489   return true;
490 }
491
492 bool
493 Path::destroyDirectory(bool remove_contents) {
494   // Make sure we're dealing with a directory
495   if (!isDirectory()) return false;
496
497   // If it doesn't exist, we're done.
498   if (!exists()) return true;
499
500   char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
501   path.copy(pathname,path.length()+1);
502   int lastchar = path.length() - 1 ;
503   if (pathname[lastchar] == '/')
504     pathname[lastchar] = 0;
505
506   if (remove_contents) {
507     // Recursively descend the directory to remove its content
508     // FIXME: The correct way of doing this on Windows isn't pretty...
509     // but this may work if unix-like utils are present.
510     std::string cmd("rm -rf ");
511     cmd += path;
512     system(cmd.c_str());
513   } else {
514     // Otherwise, try to just remove the one directory
515     if (!RemoveDirectory(pathname))
516       ThrowError(std::string(pathname) + ": Can't destroy directory: ");
517   }
518   return true;
519 }
520
521 bool
522 Path::destroyFile() {
523   if (!isFile()) return false;
524
525   DWORD attr = GetFileAttributes(path.c_str());
526
527   // If it doesn't exist, we're done.
528   if (attr == INVALID_FILE_ATTRIBUTES)
529     return true;
530
531   // Read-only files cannot be deleted on Windows.  Must remove the read-only
532   // attribute first.
533   if (attr & FILE_ATTRIBUTE_READONLY) {
534     if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
535       ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
536   }
537
538   if (!DeleteFile(path.c_str()))
539     ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
540   return true;
541 }
542
543 }
544 }
545
546 // vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
547