* Implement getStatusInfo for getting stat(2) like information
[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 "Unix.h"
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <fstream>
24
25 namespace llvm {
26 using namespace sys;
27
28 Path::Path(std::string unverified_path) 
29   : path(unverified_path)
30 {
31   if (unverified_path.empty())
32     return;
33   if (this->isValid()) 
34     return;
35   // oops, not valid.
36   path.clear();
37   ThrowErrno(unverified_path + ": path is not valid");
38 }
39
40 Path
41 Path::GetRootDirectory() {
42   Path result;
43   result.setDirectory("/");
44   return result;
45 }
46
47 static inline bool IsLibrary(Path& path, const std::string& basename) {
48   if (path.appendFile(std::string("lib") + basename)) {
49     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
50       return true;
51     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
52       return true;
53     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
54       return true;
55     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
56       return true;
57   } else if (path.elideFile() && path.appendFile(basename)) {
58     if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
59       return true;
60     else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
61       return true;
62     else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
63       return true;
64     else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
65       return true;
66   }
67   path.clear();
68   return false;
69 }
70
71 Path 
72 Path::GetLibraryPath(const std::string& basename, 
73                      const std::vector<std::string>& LibPaths) {
74   Path result;
75
76   // Try the paths provided
77   for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
78        E = LibPaths.end(); I != E; ++I ) {
79     if (result.setDirectory(*I) && IsLibrary(result,basename))
80       return result;
81   }
82
83   // Try the LLVM lib directory in the LLVM install area
84   if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
85     return result;
86
87   // Try /usr/lib
88   if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
89     return result;
90
91   // Try /lib
92   if (result.setDirectory("/lib/") && IsLibrary(result,basename))
93     return result;
94
95   // Can't find it, give up and return invalid path.
96   result.clear();
97   return result;
98 }
99
100 Path 
101 Path::GetSystemLibraryPath1() {
102   return Path("/lib/");
103 }
104
105 Path 
106 Path::GetSystemLibraryPath2() {
107   return Path("/usr/lib/");
108 }
109
110 Path 
111 Path::GetLLVMDefaultConfigDir() {
112   return Path("/etc/llvm/");
113 }
114
115 Path 
116 Path::GetLLVMConfigDir() {
117   Path result;
118   if (result.setDirectory(LLVM_ETCDIR))
119     return result;
120   return GetLLVMDefaultConfigDir();
121 }
122
123 Path
124 Path::GetUserHomeDirectory() {
125   const char* home = getenv("HOME");
126   if (home) {
127     Path result;
128     if (result.setDirectory(home))
129       return result;
130   }
131   return GetRootDirectory();
132 }
133
134 bool
135 Path::isFile() const {
136   return (isValid() && path[path.length()-1] != '/');
137 }
138
139 bool
140 Path::isDirectory() const {
141   return (isValid() && path[path.length()-1] == '/');
142 }
143
144 std::string
145 Path::getBasename() const {
146   // Find the last slash
147   size_t slash = path.rfind('/');
148   if (slash == std::string::npos)
149     slash = 0;
150   else
151     slash++;
152
153   return path.substr(slash, path.rfind('.'));
154 }
155
156 bool Path::hasMagicNumber(const std::string &Magic) const {
157   size_t len = Magic.size();
158   char buf[ 1 + len];
159   std::ifstream f(path.c_str());
160   f.read(buf, len);
161   buf[len] = '\0';
162   return Magic == buf;
163 }
164
165 bool 
166 Path::isBytecodeFile() const {
167   char buffer[ 4];
168   buffer[0] = 0;
169   std::ifstream f(path.c_str());
170   f.read(buffer, 4);
171   if (f.bad())
172     ThrowErrno("can't read file signature");
173   return 0 == memcmp(buffer,"llvc",4) || 0 == memcmp(buffer,"llvm",4);
174 }
175
176 bool
177 Path::isArchive() const {
178   if (readable()) {
179     return hasMagicNumber("!<arch>\012");
180   }
181   return false;
182 }
183
184 bool
185 Path::exists() const {
186   return 0 == access(path.c_str(), F_OK );
187 }
188
189 bool
190 Path::readable() const {
191   return 0 == access(path.c_str(), F_OK | R_OK );
192 }
193
194 bool
195 Path::writable() const {
196   return 0 == access(path.c_str(), F_OK | W_OK );
197 }
198
199 bool
200 Path::executable() const {
201   return 0 == access(path.c_str(), R_OK | X_OK );
202 }
203
204 std::string 
205 Path::getLast() const {
206   // Find the last slash
207   size_t pos = path.rfind('/');
208
209   // Handle the corner cases
210   if (pos == std::string::npos)
211     return path;
212
213   // If the last character is a slash
214   if (pos == path.length()-1) {
215     // Find the second to last slash
216     size_t pos2 = path.rfind('/', pos-1);
217     if (pos2 == std::string::npos)
218       return path.substr(0,pos);
219     else
220       return path.substr(pos2+1,pos-pos2-1);
221   }
222   // Return everything after the last slash
223   return path.substr(pos+1);
224 }
225
226 void
227 Path::getStatusInfo(StatusInfo& info) const {
228   struct stat buf;
229   if (0 != stat(path.c_str(), &buf)) {
230     ThrowErrno(std::string("Can't get status: ")+path);
231   }
232   info.fileSize = buf.st_size;
233   info.modTime.fromPosixTime(buf.st_mtime);
234   info.mode = buf.st_mode;
235   info.user = buf.st_uid;
236   info.group = buf.st_gid;
237 }
238
239 bool
240 Path::setDirectory(const std::string& a_path) {
241   if (a_path.size() == 0)
242     return false;
243   Path save(*this);
244   path = a_path;
245   size_t last = a_path.size() -1;
246   if (last != 0 && a_path[last] != '/')
247     path += '/';
248   if (!isValid()) {
249     path = save.path;
250     return false;
251   }
252   return true;
253 }
254
255 bool
256 Path::setFile(const std::string& a_path) {
257   if (a_path.size() == 0)
258     return false;
259   Path save(*this);
260   path = a_path;
261   size_t last = a_path.size() - 1;
262   while (last > 0 && a_path[last] == '/')
263     last--;
264   path.erase(last+1);
265   if (!isValid()) {
266     path = save.path;
267     return false;
268   }
269   return true;
270 }
271
272 bool
273 Path::appendDirectory(const std::string& dir) {
274   if (isFile()) 
275     return false;
276   Path save(*this);
277   path += dir;
278   path += "/";
279   if (!isValid()) {
280     path = save.path;
281     return false;
282   }
283   return true;
284 }
285
286 bool
287 Path::elideDirectory() {
288   if (isFile()) 
289     return false;
290   size_t slashpos = path.rfind('/',path.size());
291   if (slashpos == 0 || slashpos == std::string::npos)
292     return false;
293   if (slashpos == path.size() - 1)
294     slashpos = path.rfind('/',slashpos-1);
295   if (slashpos == std::string::npos)
296     return false;
297   path.erase(slashpos);
298   return true;
299 }
300
301 bool
302 Path::appendFile(const std::string& file) {
303   if (!isDirectory()) 
304     return false;
305   Path save(*this);
306   path += file;
307   if (!isValid()) {
308     path = save.path;
309     return false;
310   }
311   return true;
312 }
313
314 bool
315 Path::elideFile() {
316   if (isDirectory()) 
317     return false;
318   size_t slashpos = path.rfind('/',path.size());
319   if (slashpos == std::string::npos)
320     return false;
321   path.erase(slashpos+1);
322   return true;
323 }
324
325 bool
326 Path::appendSuffix(const std::string& suffix) {
327   if (isDirectory()) 
328     return false;
329   Path save(*this);
330   path.append(".");
331   path.append(suffix);
332   if (!isValid()) {
333     path = save.path;
334     return false;
335   }
336   return true;
337 }
338
339 bool 
340 Path::elideSuffix() {
341   if (isDirectory()) return false;
342   size_t dotpos = path.rfind('.',path.size());
343   size_t slashpos = path.rfind('/',path.size());
344   if (slashpos != std::string::npos && dotpos != std::string::npos &&
345       dotpos > slashpos) {
346     path.erase(dotpos, path.size()-dotpos);
347     return true;
348   }
349   return false;
350 }
351
352
353 bool
354 Path::createDirectory( bool create_parents) {
355   // Make sure we're dealing with a directory
356   if (!isDirectory()) return false;
357
358   // Get a writeable copy of the path name
359   char pathname[MAXPATHLEN];
360   path.copy(pathname,MAXPATHLEN);
361
362   // Null-terminate the last component
363   int lastchar = path.length() - 1 ; 
364   if (pathname[lastchar] == '/') 
365     pathname[lastchar] = 0;
366
367   // If we're supposed to create intermediate directories
368   if ( create_parents ) {
369     // Find the end of the initial name component
370     char * next = strchr(pathname,'/');
371     if ( pathname[0] == '/') 
372       next = strchr(&pathname[1],'/');
373
374     // Loop through the directory components until we're done 
375     while ( next != 0 ) {
376       *next = 0;
377       if (0 != access(pathname, F_OK | R_OK | W_OK))
378         if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
379           ThrowErrno(std::string(pathname) + ": Can't create directory");
380       char* save = next;
381       next = strchr(pathname,'/');
382       *save = '/';
383     }
384   } else if (0 != mkdir(pathname, S_IRWXU | S_IRWXG)) {
385     ThrowErrno(std::string(pathname) + ": Can't create directory");
386   } 
387   return true;
388 }
389
390 bool
391 Path::createFile() {
392   // Make sure we're dealing with a file
393   if (!isFile()) return false; 
394
395   // Create the file
396   int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
397   if (fd < 0)
398     ThrowErrno(path + ": Can't create file");
399   ::close(fd);
400
401   return true;
402 }
403
404 bool
405 Path::createTemporaryFile() {
406   // Make sure we're dealing with a file
407   if (!isFile()) return false;
408
409   // Append the filename filler
410   char pathname[MAXPATHLEN];
411   path.copy(pathname,MAXPATHLEN);
412   strcat(pathname,"XXXXXX");
413   int fd = ::mkstemp(pathname);
414   if (fd < 0) {
415     ThrowErrno(path + ": Can't create temporary file");
416   }
417   path = pathname;
418   ::close(fd);
419   return true;
420 }
421
422 bool
423 Path::destroyDirectory(bool remove_contents) {
424   // Make sure we're dealing with a directory
425   if (!isDirectory()) return false;
426
427   // If it doesn't exist, we're done.
428   if (!exists()) return true;
429
430   if (remove_contents) {
431     // Recursively descend the directory to remove its content
432     std::string cmd("/bin/rm -rf ");
433     cmd += path;
434     system(cmd.c_str());
435   } else {
436     // Otherwise, try to just remove the one directory
437     char pathname[MAXPATHLEN];
438     path.copy(pathname,MAXPATHLEN);
439     int lastchar = path.length() - 1 ; 
440     if (pathname[lastchar] == '/') 
441       pathname[lastchar] = 0;
442     if ( 0 != rmdir(pathname))
443       ThrowErrno(std::string(pathname) + ": Can't destroy directory");
444   }
445   return true;
446 }
447
448 bool
449 Path::destroyFile() {
450   if (!isFile()) return false;
451   if (0 != unlink(path.c_str()))
452     ThrowErrno(std::string(path.c_str()) + ": Can't destroy file");
453   return true;
454 }
455
456 }
457
458 // vim: sw=2