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