d3e4d96a83ef636d165308f594468b8d1743a47b
[oota-llvm.git] / lib / System / Unix / Path.inc
1 //===- llvm/System/Unix/Path.cpp - Unix 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 //===----------------------------------------------------------------------===//
9 //
10 // This file implements the Unix specific portion of the Path class.
11 //
12 //===----------------------------------------------------------------------===//
13
14 //===----------------------------------------------------------------------===//
15 //=== WARNING: Implementation here must contain only generic UNIX code that
16 //===          is guaranteed to work on *all* UNIX variants.
17 //===----------------------------------------------------------------------===//
18
19 #include <llvm/Config/config.h>
20 #include <llvm/Config/alloca.h>
21 #include "Unix.h"
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <utime.h>
25 #include <dirent.h>
26
27 namespace llvm {
28 using namespace sys;
29
30 Path::Path(std::string unverified_path) 
31   : path(unverified_path)
32 {
33   if (unverified_path.empty())
34     return;
35   if (this->isValid()) 
36     return;
37   // oops, not valid.
38   path.clear();
39   ThrowErrno(unverified_path + ": path is not valid");
40 }
41
42 Path
43 Path::GetRootDirectory() {
44   Path result;
45   result.setDirectory("/");
46   return result;
47 }
48
49 static inline bool IsLibrary(Path& path, const std::string& basename) {
50   if (path.appendFile(std::string("lib") + basename)) {
51     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
52       return true;
53     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
54       return true;
55     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
56       return true;
57     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
58       return true;
59   } else if (path.elideFile() && path.appendFile(basename)) {
60     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
61       return true;
62     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
63       return true;
64     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
65       return true;
66     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
67       return true;
68   }
69   path.clear();
70   return false;
71 }
72
73 Path 
74 Path::GetLibraryPath(const std::string& basename, 
75                      const std::vector<std::string>& LibPaths) {
76   Path result;
77
78   // Try the paths provided
79   for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
80        E = LibPaths.end(); I != E; ++I ) {
81     if (result.setDirectory(*I) && IsLibrary(result,basename))
82       return result;
83   }
84
85   // Try the LLVM lib directory in the LLVM install area
86   if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
87     return result;
88
89   // Try /usr/lib
90   if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
91     return result;
92
93   // Try /lib
94   if (result.setDirectory("/lib/") && IsLibrary(result,basename))
95     return result;
96
97   // Can't find it, give up and return invalid path.
98   result.clear();
99   return result;
100 }
101
102 Path 
103 Path::GetSystemLibraryPath1() {
104   return Path("/lib/");
105 }
106
107 Path 
108 Path::GetSystemLibraryPath2() {
109   return Path("/usr/lib/");
110 }
111
112 Path 
113 Path::GetLLVMDefaultConfigDir() {
114   return Path("/etc/llvm/");
115 }
116
117 Path 
118 Path::GetLLVMConfigDir() {
119   Path result;
120   if (result.setDirectory(LLVM_ETCDIR))
121     return result;
122   return GetLLVMDefaultConfigDir();
123 }
124
125 Path
126 Path::GetUserHomeDirectory() {
127   const char* home = getenv("HOME");
128   if (home) {
129     Path result;
130     if (result.setDirectory(home))
131       return result;
132   }
133   return GetRootDirectory();
134 }
135
136 bool
137 Path::isFile() const {
138   return (isValid() && path[path.length()-1] != '/');
139 }
140
141 bool
142 Path::isDirectory() const {
143   return (isValid() && path[path.length()-1] == '/');
144 }
145
146 std::string
147 Path::getBasename() const {
148   // Find the last slash
149   size_t slash = path.rfind('/');
150   if (slash == std::string::npos)
151     slash = 0;
152   else
153     slash++;
154
155   return path.substr(slash, path.rfind('.'));
156 }
157
158 bool Path::hasMagicNumber(const std::string &Magic) const {
159   size_t len = Magic.size();
160   assert(len < 1024 && "Request for magic string too long");
161   char* buf = (char*) alloca(1 + len);
162   int fd = ::open(path.c_str(),O_RDONLY);
163   if (fd < 0)
164     return false;
165   if (0 != ::read(fd, buf, len))
166     return false;
167   close(fd);
168   buf[len] = '\0';
169   return Magic == buf;
170 }
171
172 bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
173   if (!isFile())
174     return false;
175   assert(len < 1024 && "Request for magic string too long");
176   char* buf = (char*) alloca(1 + len);
177   int fd = ::open(path.c_str(),O_RDONLY);
178   if (fd < 0)
179     return false;
180   ssize_t bytes_read = ::read(fd, buf, len);
181   ::close(fd);
182   if (ssize_t(len) != bytes_read) {
183     Magic.clear();
184     return false;
185   }
186   Magic.assign(buf,len);
187   return true;
188 }
189
190 bool 
191 Path::isBytecodeFile() const {
192   char buffer[ 4];
193   buffer[0] = 0;
194   int fd = ::open(path.c_str(),O_RDONLY);
195   if (fd < 0)
196     return false;
197   ssize_t bytes_read = ::read(fd, buffer, 4);
198   ::close(fd);
199   if (4 != bytes_read) 
200     return false;
201
202   return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
203       (buffer[3] == 'c' || buffer[3] == 'm'));
204 }
205
206 bool
207 Path::isArchive() const {
208   if (readable()) {
209     return hasMagicNumber("!<arch>\012");
210   }
211   return false;
212 }
213
214 bool
215 Path::exists() const {
216   return 0 == access(path.c_str(), F_OK );
217 }
218
219 bool
220 Path::readable() const {
221   return 0 == access(path.c_str(), F_OK | R_OK );
222 }
223
224 bool
225 Path::writable() const {
226   return 0 == access(path.c_str(), F_OK | W_OK );
227 }
228
229 bool
230 Path::executable() const {
231   return 0 == access(path.c_str(), R_OK | X_OK );
232 }
233
234 std::string 
235 Path::getLast() const {
236   // Find the last slash
237   size_t pos = path.rfind('/');
238
239   // Handle the corner cases
240   if (pos == std::string::npos)
241     return path;
242
243   // If the last character is a slash
244   if (pos == path.length()-1) {
245     // Find the second to last slash
246     size_t pos2 = path.rfind('/', pos-1);
247     if (pos2 == std::string::npos)
248       return path.substr(0,pos);
249     else
250       return path.substr(pos2+1,pos-pos2-1);
251   }
252   // Return everything after the last slash
253   return path.substr(pos+1);
254 }
255
256 void
257 Path::getStatusInfo(StatusInfo& info) const {
258   struct stat buf;
259   if (0 != stat(path.c_str(), &buf)) {
260     ThrowErrno(std::string("Can't get status: ")+path);
261   }
262   info.fileSize = buf.st_size;
263   info.modTime.fromEpochTime(buf.st_mtime);
264   info.mode = buf.st_mode;
265   info.user = buf.st_uid;
266   info.group = buf.st_gid;
267   info.isDir = S_ISDIR(buf.st_mode);
268   if (info.isDir && path[path.length()-1] != '/')
269     path += '/';
270 }
271
272 bool
273 Path::getDirectoryContents(std::set<Path>& result) const {
274   if (!isDirectory())
275     return false;
276   DIR* direntries = ::opendir(path.c_str());
277   if (direntries == 0)
278     ThrowErrno(path + ": can't open directory");
279
280   result.clear();
281   struct dirent* de = ::readdir(direntries);
282   while (de != 0) {
283     if (de->d_name[0] != '.') {
284       Path aPath(path + (const char*)de->d_name);
285       struct stat buf;
286       if (0 != stat(aPath.path.c_str(), &buf))
287         ThrowErrno(aPath.path + ": can't get status");
288       if (S_ISDIR(buf.st_mode))
289         aPath.path += "/";
290       result.insert(aPath);
291     }
292     de = ::readdir(direntries);
293   }
294   
295   closedir(direntries);
296   return true;
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   size_t last = a_path.size() -1;
306   if (last != 0 && a_path[last] != '/')
307     path += '/';
308   if (!isValid()) {
309     path = save.path;
310     return false;
311   }
312   return true;
313 }
314
315 bool
316 Path::setFile(const std::string& a_path) {
317   if (a_path.size() == 0)
318     return false;
319   Path save(*this);
320   path = a_path;
321   size_t last = a_path.size() - 1;
322   while (last > 0 && a_path[last] == '/')
323     last--;
324   path.erase(last+1);
325   if (!isValid()) {
326     path = save.path;
327     return false;
328   }
329   return true;
330 }
331
332 bool
333 Path::appendDirectory(const std::string& dir) {
334   if (isFile()) 
335     return false;
336   Path save(*this);
337   path += dir;
338   path += "/";
339   if (!isValid()) {
340     path = save.path;
341     return false;
342   }
343   return true;
344 }
345
346 bool
347 Path::elideDirectory() {
348   if (isFile()) 
349     return false;
350   size_t slashpos = path.rfind('/',path.size());
351   if (slashpos == 0 || slashpos == std::string::npos)
352     return false;
353   if (slashpos == path.size() - 1)
354     slashpos = path.rfind('/',slashpos-1);
355   if (slashpos == std::string::npos)
356     return false;
357   path.erase(slashpos);
358   return true;
359 }
360
361 bool
362 Path::appendFile(const std::string& file) {
363   if (!isDirectory()) 
364     return false;
365   Path save(*this);
366   path += file;
367   if (!isValid()) {
368     path = save.path;
369     return false;
370   }
371   return true;
372 }
373
374 bool
375 Path::elideFile() {
376   if (isDirectory()) 
377     return false;
378   size_t slashpos = path.rfind('/',path.size());
379   if (slashpos == std::string::npos)
380     return false;
381   path.erase(slashpos+1);
382   return true;
383 }
384
385 bool
386 Path::appendSuffix(const std::string& suffix) {
387   if (isDirectory()) 
388     return false;
389   Path save(*this);
390   path.append(".");
391   path.append(suffix);
392   if (!isValid()) {
393     path = save.path;
394     return false;
395   }
396   return true;
397 }
398
399 bool 
400 Path::elideSuffix() {
401   if (isDirectory()) return false;
402   size_t dotpos = path.rfind('.',path.size());
403   size_t slashpos = path.rfind('/',path.size());
404   if (slashpos != std::string::npos && dotpos != std::string::npos &&
405       dotpos > slashpos) {
406     path.erase(dotpos, path.size()-dotpos);
407     return true;
408   }
409   return false;
410 }
411
412
413 bool
414 Path::createDirectory( bool create_parents) {
415   // Make sure we're dealing with a directory
416   if (!isDirectory()) return false;
417
418   // Get a writeable copy of the path name
419   char pathname[MAXPATHLEN];
420   path.copy(pathname,MAXPATHLEN);
421
422   // Null-terminate the last component
423   int lastchar = path.length() - 1 ; 
424   if (pathname[lastchar] == '/') 
425     pathname[lastchar] = 0;
426   else 
427     pathname[lastchar+1] = 0;
428
429   // If we're supposed to create intermediate directories
430   if ( create_parents ) {
431     // Find the end of the initial name component
432     char * next = strchr(pathname,'/');
433     if ( pathname[0] == '/') 
434       next = strchr(&pathname[1],'/');
435
436     // Loop through the directory components until we're done 
437     while ( next != 0 ) {
438       *next = 0;
439       if (0 != access(pathname, F_OK | R_OK | W_OK))
440         if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
441           ThrowErrno(std::string(pathname) + ": Can't create directory");
442       char* save = next;
443       next = strchr(next+1,'/');
444       *save = '/';
445     }
446   } 
447
448   if (0 != access(pathname, F_OK | R_OK))
449     if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
450       ThrowErrno(std::string(pathname) + ": Can't create directory");
451   return true;
452 }
453
454 bool
455 Path::createFile() {
456   // Make sure we're dealing with a file
457   if (!isFile()) return false; 
458
459   // Create the file
460   int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
461   if (fd < 0)
462     ThrowErrno(path + ": Can't create file");
463   ::close(fd);
464
465   return true;
466 }
467
468 bool
469 Path::createTemporaryFile() {
470   // Make sure we're dealing with a file
471   if (!isFile()) return false;
472
473   // Append the filename filler
474   char pathname[MAXPATHLEN];
475   path.copy(pathname,MAXPATHLEN);
476   pathname[path.length()] = 0;
477   strcat(pathname,"XXXXXX");
478   int fd = ::mkstemp(pathname);
479   if (fd < 0) {
480     ThrowErrno(path + ": Can't create temporary file");
481   }
482   path = pathname;
483   ::close(fd);
484   return true;
485 }
486
487 bool
488 Path::destroyDirectory(bool remove_contents) {
489   // Make sure we're dealing with a directory
490   if (!isDirectory()) return false;
491
492   // If it doesn't exist, we're done.
493   if (!exists()) return true;
494
495   if (remove_contents) {
496     // Recursively descend the directory to remove its content
497     std::string cmd("/bin/rm -rf ");
498     cmd += path;
499     system(cmd.c_str());
500   } else {
501     // Otherwise, try to just remove the one directory
502     char pathname[MAXPATHLEN];
503     path.copy(pathname,MAXPATHLEN);
504     int lastchar = path.length() - 1 ; 
505     if (pathname[lastchar] == '/') 
506       pathname[lastchar] = 0;
507     else
508       pathname[lastchar+1] = 0;
509     if ( 0 != rmdir(pathname))
510       ThrowErrno(std::string(pathname) + ": Can't destroy directory");
511   }
512   return true;
513 }
514
515 bool
516 Path::destroyFile() {
517   if (!isFile()) return false;
518   if (0 != unlink(path.c_str()))
519     ThrowErrno(path + ": Can't destroy file");
520   return true;
521 }
522
523 bool
524 Path::renameFile(const Path& newName) {
525   if (!isFile()) return false;
526   if (0 != rename(path.c_str(), newName.c_str()))
527     ThrowErrno(std::string("can't rename ") + path + " as " + 
528                newName.toString());
529   return true;
530 }
531
532 bool
533 Path::setStatusInfo(const StatusInfo& si) const {
534   if (!isFile()) return false;
535   struct utimbuf utb;
536   utb.actime = si.modTime.toPosixTime();
537   utb.modtime = utb.actime;
538   if (0 != ::utime(path.c_str(),&utb))
539     ThrowErrno(path + ": can't set file modification time");
540   if (0 != ::chmod(path.c_str(),si.mode))
541     ThrowErrno(path + ": can't set mode");
542   return true;
543 }
544
545 }
546
547 // vim: sw=2