blob: 70cc4f01032f2253e502ecc98a7120718f87c7c9 [file] [log] [blame]
Reid Spencerb89a2232004-08-25 06:20:07 +00001//===- 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
Reid Spencer8e665952004-08-29 05:24:01 +000016//=== is guaranteed to work on *all* UNIX variants.
Reid Spencerb89a2232004-08-25 06:20:07 +000017//===----------------------------------------------------------------------===//
18
Reid Spencer551ccae2004-09-01 22:55:40 +000019#include <llvm/Config/config.h>
Reid Spencerbe31d2a2004-11-16 17:14:08 +000020#include <llvm/Config/alloca.h>
Reid Spencerb89a2232004-08-25 06:20:07 +000021#include "Unix.h"
22#include <sys/stat.h>
23#include <fcntl.h>
Reid Spencer1b554b42004-09-11 04:55:08 +000024#include <fstream>
Reid Spencereaf18152004-11-14 22:08:36 +000025#include <utime.h>
26#include <dirent.h>
Reid Spencerb89a2232004-08-25 06:20:07 +000027
Reid Spencer8e665952004-08-29 05:24:01 +000028namespace llvm {
29using namespace sys;
Reid Spencerb89a2232004-08-25 06:20:07 +000030
Reid Spencer8e665952004-08-29 05:24:01 +000031Path::Path(std::string unverified_path)
32 : path(unverified_path)
Reid Spencerb89a2232004-08-25 06:20:07 +000033{
Reid Spencer8e665952004-08-29 05:24:01 +000034 if (unverified_path.empty())
35 return;
Reid Spencer07adb282004-11-05 22:15:36 +000036 if (this->isValid())
Reid Spencer8e665952004-08-29 05:24:01 +000037 return;
38 // oops, not valid.
39 path.clear();
40 ThrowErrno(unverified_path + ": path is not valid");
Reid Spencerb89a2232004-08-25 06:20:07 +000041}
42
Reid Spencer8e665952004-08-29 05:24:01 +000043Path
44Path::GetRootDirectory() {
45 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +000046 result.setDirectory("/");
Reid Spencer8e665952004-08-29 05:24:01 +000047 return result;
48}
49
Reid Spencer74e72612004-09-14 00:16:39 +000050static inline bool IsLibrary(Path& path, const std::string& basename) {
Reid Spencer07adb282004-11-05 22:15:36 +000051 if (path.appendFile(std::string("lib") + basename)) {
52 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000053 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000054 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000055 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000056 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000057 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000058 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000059 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000060 } else if (path.elideFile() && path.appendFile(basename)) {
61 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000062 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000063 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000064 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000065 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000066 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000067 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000068 return true;
Reid Spencer74e72612004-09-14 00:16:39 +000069 }
70 path.clear();
71 return false;
72}
73
74Path
75Path::GetLibraryPath(const std::string& basename,
76 const std::vector<std::string>& LibPaths) {
77 Path result;
78
79 // Try the paths provided
80 for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
81 E = LibPaths.end(); I != E; ++I ) {
Reid Spencer07adb282004-11-05 22:15:36 +000082 if (result.setDirectory(*I) && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000083 return result;
84 }
85
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000086 // Try the LLVM lib directory in the LLVM install area
Reid Spencer07adb282004-11-05 22:15:36 +000087 if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000088 return result;
89
Reid Spencer74e72612004-09-14 00:16:39 +000090 // Try /usr/lib
Reid Spencer07adb282004-11-05 22:15:36 +000091 if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000092 return result;
93
94 // Try /lib
Reid Spencer07adb282004-11-05 22:15:36 +000095 if (result.setDirectory("/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000096 return result;
97
98 // Can't find it, give up and return invalid path.
99 result.clear();
100 return result;
101}
102
Reid Spencer8e665952004-08-29 05:24:01 +0000103Path
104Path::GetSystemLibraryPath1() {
105 return Path("/lib/");
106}
107
108Path
109Path::GetSystemLibraryPath2() {
110 return Path("/usr/lib/");
111}
112
113Path
114Path::GetLLVMDefaultConfigDir() {
115 return Path("/etc/llvm/");
116}
117
118Path
119Path::GetLLVMConfigDir() {
120 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000121 if (result.setDirectory(LLVM_ETCDIR))
Reid Spencer8e665952004-08-29 05:24:01 +0000122 return result;
123 return GetLLVMDefaultConfigDir();
124}
125
126Path
127Path::GetUserHomeDirectory() {
128 const char* home = getenv("HOME");
129 if (home) {
130 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000131 if (result.setDirectory(home))
Reid Spencer8e665952004-08-29 05:24:01 +0000132 return result;
133 }
134 return GetRootDirectory();
135}
136
137bool
Reid Spencer07adb282004-11-05 22:15:36 +0000138Path::isFile() const {
139 return (isValid() && path[path.length()-1] != '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000140}
141
142bool
Reid Spencer07adb282004-11-05 22:15:36 +0000143Path::isDirectory() const {
144 return (isValid() && path[path.length()-1] == '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000145}
146
147std::string
Reid Spencer07adb282004-11-05 22:15:36 +0000148Path::getBasename() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000149 // Find the last slash
150 size_t slash = path.rfind('/');
151 if (slash == std::string::npos)
152 slash = 0;
153 else
154 slash++;
155
156 return path.substr(slash, path.rfind('.'));
157}
158
Reid Spencer07adb282004-11-05 22:15:36 +0000159bool Path::hasMagicNumber(const std::string &Magic) const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000160 size_t len = Magic.size();
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000161 assert(len < 1024 && "Request for magic string too long");
162 char* buf = (char*) alloca(1 + len);
163 int fd = ::open(path.c_str(),O_RDONLY);
164 if (fd < 0)
165 return false;
166 if (0 != ::read(fd, buf, len))
167 return false;
168 close(fd);
Reid Spencer1b554b42004-09-11 04:55:08 +0000169 buf[len] = '\0';
170 return Magic == buf;
171}
172
Reid Spencereaf18152004-11-14 22:08:36 +0000173bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
174 if (!isFile())
175 return false;
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000176 assert(len < 1024 && "Request for magic string too long");
177 char* buf = (char*) alloca(1 + len);
178 int fd = ::open(path.c_str(),O_RDONLY);
179 if (fd < 0)
180 return false;
181 if (0 != ::read(fd, buf, len))
182 return false;
183 close(fd);
Reid Spencereaf18152004-11-14 22:08:36 +0000184 buf[len] = '\0';
185 Magic = buf;
186 return true;
187}
188
Reid Spencer1b554b42004-09-11 04:55:08 +0000189bool
Reid Spencer07adb282004-11-05 22:15:36 +0000190Path::isBytecodeFile() const {
Reid Spencer9195f372004-11-09 20:26:31 +0000191 char buffer[ 4];
192 buffer[0] = 0;
193 std::ifstream f(path.c_str());
194 f.read(buffer, 4);
195 if (f.bad())
196 ThrowErrno("can't read file signature");
Reid Spencereaf18152004-11-14 22:08:36 +0000197
198 return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
199 (buffer[3] == 'c' || buffer[3] == 'm'));
Reid Spencer1b554b42004-09-11 04:55:08 +0000200}
201
202bool
Reid Spencer07adb282004-11-05 22:15:36 +0000203Path::isArchive() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000204 if (readable()) {
Reid Spencer07adb282004-11-05 22:15:36 +0000205 return hasMagicNumber("!<arch>\012");
Reid Spencer1b554b42004-09-11 04:55:08 +0000206 }
207 return false;
208}
209
210bool
Reid Spencer8e665952004-08-29 05:24:01 +0000211Path::exists() const {
212 return 0 == access(path.c_str(), F_OK );
213}
214
215bool
216Path::readable() const {
217 return 0 == access(path.c_str(), F_OK | R_OK );
218}
219
220bool
221Path::writable() const {
222 return 0 == access(path.c_str(), F_OK | W_OK );
223}
224
225bool
226Path::executable() const {
227 return 0 == access(path.c_str(), R_OK | X_OK );
228}
229
230std::string
231Path::getLast() const {
232 // Find the last slash
233 size_t pos = path.rfind('/');
234
235 // Handle the corner cases
236 if (pos == std::string::npos)
237 return path;
238
239 // If the last character is a slash
240 if (pos == path.length()-1) {
241 // Find the second to last slash
242 size_t pos2 = path.rfind('/', pos-1);
243 if (pos2 == std::string::npos)
244 return path.substr(0,pos);
245 else
246 return path.substr(pos2+1,pos-pos2-1);
247 }
248 // Return everything after the last slash
249 return path.substr(pos+1);
250}
251
Reid Spencerb608a812004-11-16 06:15:19 +0000252void
253Path::getStatusInfo(StatusInfo& info) const {
Reid Spencer9195f372004-11-09 20:26:31 +0000254 struct stat buf;
255 if (0 != stat(path.c_str(), &buf)) {
256 ThrowErrno(std::string("Can't get status: ")+path);
257 }
258 info.fileSize = buf.st_size;
Reid Spencereaf18152004-11-14 22:08:36 +0000259 info.modTime.fromEpochTime(buf.st_mtime);
Reid Spencer9195f372004-11-09 20:26:31 +0000260 info.mode = buf.st_mode;
261 info.user = buf.st_uid;
262 info.group = buf.st_gid;
Reid Spencereaf18152004-11-14 22:08:36 +0000263 info.isDir = S_ISDIR(buf.st_mode);
264 if (info.isDir && path[path.length()-1] != '/')
265 path += '/';
266}
267
268bool
Reid Spencerb608a812004-11-16 06:15:19 +0000269Path::getDirectoryContents(std::set<Path>& result) const {
Reid Spencereaf18152004-11-14 22:08:36 +0000270 if (!isDirectory())
271 return false;
272 DIR* direntries = ::opendir(path.c_str());
273 if (direntries == 0)
274 ThrowErrno(path + ": can't open directory");
275
276 result.clear();
277 struct dirent* de = ::readdir(direntries);
278 while (de != 0) {
279 if (de->d_name[0] != '.') {
280 Path aPath(path + (const char*)de->d_name);
281 struct stat buf;
282 if (0 != stat(aPath.path.c_str(), &buf))
283 ThrowErrno(aPath.path + ": can't get status");
284 if (S_ISDIR(buf.st_mode))
285 aPath.path += "/";
Reid Spencerb608a812004-11-16 06:15:19 +0000286 result.insert(aPath);
Reid Spencereaf18152004-11-14 22:08:36 +0000287 }
288 de = ::readdir(direntries);
289 }
290
291 closedir(direntries);
292 return true;
Reid Spencer9195f372004-11-09 20:26:31 +0000293}
294
Reid Spencer8e665952004-08-29 05:24:01 +0000295bool
Reid Spencer07adb282004-11-05 22:15:36 +0000296Path::setDirectory(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000297 if (a_path.size() == 0)
298 return false;
299 Path save(*this);
300 path = a_path;
301 size_t last = a_path.size() -1;
302 if (last != 0 && a_path[last] != '/')
303 path += '/';
Reid Spencer07adb282004-11-05 22:15:36 +0000304 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000305 path = save.path;
306 return false;
307 }
308 return true;
309}
310
311bool
Reid Spencer07adb282004-11-05 22:15:36 +0000312Path::setFile(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000313 if (a_path.size() == 0)
314 return false;
315 Path save(*this);
316 path = a_path;
317 size_t last = a_path.size() - 1;
318 while (last > 0 && a_path[last] == '/')
319 last--;
320 path.erase(last+1);
Reid Spencer07adb282004-11-05 22:15:36 +0000321 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000322 path = save.path;
323 return false;
324 }
325 return true;
326}
327
328bool
Reid Spencer07adb282004-11-05 22:15:36 +0000329Path::appendDirectory(const std::string& dir) {
330 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000331 return false;
332 Path save(*this);
333 path += dir;
334 path += "/";
Reid Spencer07adb282004-11-05 22:15:36 +0000335 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000336 path = save.path;
337 return false;
338 }
339 return true;
340}
341
342bool
Reid Spencer07adb282004-11-05 22:15:36 +0000343Path::elideDirectory() {
344 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000345 return false;
346 size_t slashpos = path.rfind('/',path.size());
347 if (slashpos == 0 || slashpos == std::string::npos)
348 return false;
349 if (slashpos == path.size() - 1)
350 slashpos = path.rfind('/',slashpos-1);
351 if (slashpos == std::string::npos)
352 return false;
353 path.erase(slashpos);
354 return true;
355}
356
357bool
Reid Spencer07adb282004-11-05 22:15:36 +0000358Path::appendFile(const std::string& file) {
359 if (!isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000360 return false;
361 Path save(*this);
362 path += file;
Reid Spencer07adb282004-11-05 22:15:36 +0000363 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000364 path = save.path;
365 return false;
366 }
367 return true;
368}
369
370bool
Reid Spencer07adb282004-11-05 22:15:36 +0000371Path::elideFile() {
372 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000373 return false;
374 size_t slashpos = path.rfind('/',path.size());
375 if (slashpos == std::string::npos)
376 return false;
377 path.erase(slashpos+1);
378 return true;
379}
380
381bool
Reid Spencer07adb282004-11-05 22:15:36 +0000382Path::appendSuffix(const std::string& suffix) {
383 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000384 return false;
385 Path save(*this);
386 path.append(".");
387 path.append(suffix);
Reid Spencer07adb282004-11-05 22:15:36 +0000388 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000389 path = save.path;
390 return false;
391 }
392 return true;
393}
394
395bool
Reid Spencer07adb282004-11-05 22:15:36 +0000396Path::elideSuffix() {
397 if (isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000398 size_t dotpos = path.rfind('.',path.size());
399 size_t slashpos = path.rfind('/',path.size());
400 if (slashpos != std::string::npos && dotpos != std::string::npos &&
401 dotpos > slashpos) {
402 path.erase(dotpos, path.size()-dotpos);
403 return true;
404 }
405 return false;
406}
407
408
409bool
Reid Spencer07adb282004-11-05 22:15:36 +0000410Path::createDirectory( bool create_parents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000411 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000412 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000413
414 // Get a writeable copy of the path name
415 char pathname[MAXPATHLEN];
416 path.copy(pathname,MAXPATHLEN);
417
418 // Null-terminate the last component
419 int lastchar = path.length() - 1 ;
420 if (pathname[lastchar] == '/')
421 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000422 else
423 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000424
425 // If we're supposed to create intermediate directories
426 if ( create_parents ) {
427 // Find the end of the initial name component
428 char * next = strchr(pathname,'/');
429 if ( pathname[0] == '/')
430 next = strchr(&pathname[1],'/');
431
432 // Loop through the directory components until we're done
433 while ( next != 0 ) {
434 *next = 0;
435 if (0 != access(pathname, F_OK | R_OK | W_OK))
436 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
437 ThrowErrno(std::string(pathname) + ": Can't create directory");
438 char* save = next;
Reid Spencereaf18152004-11-14 22:08:36 +0000439 next = strchr(next+1,'/');
Reid Spencer8e665952004-08-29 05:24:01 +0000440 *save = '/';
441 }
Reid Spencer8e665952004-08-29 05:24:01 +0000442 }
Reid Spencereaf18152004-11-14 22:08:36 +0000443
444 if (0 != access(pathname, F_OK | R_OK))
445 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
446 ThrowErrno(std::string(pathname) + ": Can't create directory");
Reid Spencer8e665952004-08-29 05:24:01 +0000447 return true;
448}
449
450bool
Reid Spencer07adb282004-11-05 22:15:36 +0000451Path::createFile() {
Reid Spencer8e665952004-08-29 05:24:01 +0000452 // Make sure we're dealing with a file
Reid Spencer07adb282004-11-05 22:15:36 +0000453 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000454
455 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000456 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
457 if (fd < 0)
Reid Spencer9195f372004-11-09 20:26:31 +0000458 ThrowErrno(path + ": Can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000459 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000460
461 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000462}
463
Reid Spencer8e665952004-08-29 05:24:01 +0000464bool
Reid Spencer9195f372004-11-09 20:26:31 +0000465Path::createTemporaryFile() {
466 // Make sure we're dealing with a file
467 if (!isFile()) return false;
468
469 // Append the filename filler
470 char pathname[MAXPATHLEN];
471 path.copy(pathname,MAXPATHLEN);
Reid Spencereaf18152004-11-14 22:08:36 +0000472 pathname[path.length()] = 0;
Reid Spencer9195f372004-11-09 20:26:31 +0000473 strcat(pathname,"XXXXXX");
474 int fd = ::mkstemp(pathname);
475 if (fd < 0) {
476 ThrowErrno(path + ": Can't create temporary file");
477 }
478 path = pathname;
479 ::close(fd);
480 return true;
481}
482
483bool
Reid Spencer07adb282004-11-05 22:15:36 +0000484Path::destroyDirectory(bool remove_contents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000485 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000486 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000487
488 // If it doesn't exist, we're done.
489 if (!exists()) return true;
490
491 if (remove_contents) {
492 // Recursively descend the directory to remove its content
493 std::string cmd("/bin/rm -rf ");
494 cmd += path;
495 system(cmd.c_str());
496 } else {
497 // Otherwise, try to just remove the one directory
498 char pathname[MAXPATHLEN];
499 path.copy(pathname,MAXPATHLEN);
500 int lastchar = path.length() - 1 ;
501 if (pathname[lastchar] == '/')
502 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000503 else
504 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000505 if ( 0 != rmdir(pathname))
506 ThrowErrno(std::string(pathname) + ": Can't destroy directory");
507 }
508 return true;
509}
510
511bool
Reid Spencer07adb282004-11-05 22:15:36 +0000512Path::destroyFile() {
513 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000514 if (0 != unlink(path.c_str()))
Reid Spencereaf18152004-11-14 22:08:36 +0000515 ThrowErrno(path + ": Can't destroy file");
516 return true;
517}
518
519bool
520Path::renameFile(const Path& newName) {
521 if (!isFile()) return false;
522 if (0 != rename(path.c_str(), newName.c_str()))
523 ThrowErrno(std::string("can't rename ") + path + " as " + newName.get());
524 return true;
525}
526
527bool
528Path::setStatusInfo(const StatusInfo& si) const {
529 if (!isFile()) return false;
530 struct utimbuf utb;
531 utb.actime = si.modTime.toPosixTime();
532 utb.modtime = utb.actime;
533 if (0 != ::utime(path.c_str(),&utb))
534 ThrowErrno(path + ": can't set file modification time");
535 if (0 != ::chmod(path.c_str(),si.mode))
536 ThrowErrno(path + ": can't set mode");
Reid Spencer8e665952004-08-29 05:24:01 +0000537 return true;
538}
539
Reid Spencerb89a2232004-08-25 06:20:07 +0000540}
541
542// vim: sw=2