blob: 55ef6669ee35075b47766bc375433d3624e12ea2 [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 Spencerb89a2232004-08-25 06:20:07 +000020#include "Unix.h"
21#include <sys/stat.h>
22#include <fcntl.h>
Reid Spencer1b554b42004-09-11 04:55:08 +000023#include <fstream>
Reid Spencereaf18152004-11-14 22:08:36 +000024#include <utime.h>
25#include <dirent.h>
Reid Spencerb89a2232004-08-25 06:20:07 +000026
Reid Spencer8e665952004-08-29 05:24:01 +000027namespace llvm {
28using namespace sys;
Reid Spencerb89a2232004-08-25 06:20:07 +000029
Reid Spencer8e665952004-08-29 05:24:01 +000030Path::Path(std::string unverified_path)
31 : path(unverified_path)
Reid Spencerb89a2232004-08-25 06:20:07 +000032{
Reid Spencer8e665952004-08-29 05:24:01 +000033 if (unverified_path.empty())
34 return;
Reid Spencer07adb282004-11-05 22:15:36 +000035 if (this->isValid())
Reid Spencer8e665952004-08-29 05:24:01 +000036 return;
37 // oops, not valid.
38 path.clear();
39 ThrowErrno(unverified_path + ": path is not valid");
Reid Spencerb89a2232004-08-25 06:20:07 +000040}
41
Reid Spencer8e665952004-08-29 05:24:01 +000042Path
43Path::GetRootDirectory() {
44 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +000045 result.setDirectory("/");
Reid Spencer8e665952004-08-29 05:24:01 +000046 return result;
47}
48
Reid Spencer74e72612004-09-14 00:16:39 +000049static inline bool IsLibrary(Path& path, const std::string& basename) {
Reid Spencer07adb282004-11-05 22:15:36 +000050 if (path.appendFile(std::string("lib") + basename)) {
51 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000052 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000053 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000054 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000055 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000056 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000057 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000058 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000059 } else if (path.elideFile() && path.appendFile(basename)) {
60 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000061 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000062 else if (path.elideSuffix() && path.appendSuffix("a") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000063 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000064 else if (path.elideSuffix() && path.appendSuffix("o") && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000065 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000066 else if (path.elideSuffix() && path.appendSuffix("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000067 return true;
Reid Spencer74e72612004-09-14 00:16:39 +000068 }
69 path.clear();
70 return false;
71}
72
73Path
74Path::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 ) {
Reid Spencer07adb282004-11-05 22:15:36 +000081 if (result.setDirectory(*I) && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000082 return result;
83 }
84
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000085 // Try the LLVM lib directory in the LLVM install area
Reid Spencer07adb282004-11-05 22:15:36 +000086 if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000087 return result;
88
Reid Spencer74e72612004-09-14 00:16:39 +000089 // Try /usr/lib
Reid Spencer07adb282004-11-05 22:15:36 +000090 if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000091 return result;
92
93 // Try /lib
Reid Spencer07adb282004-11-05 22:15:36 +000094 if (result.setDirectory("/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000095 return result;
96
97 // Can't find it, give up and return invalid path.
98 result.clear();
99 return result;
100}
101
Reid Spencer8e665952004-08-29 05:24:01 +0000102Path
103Path::GetSystemLibraryPath1() {
104 return Path("/lib/");
105}
106
107Path
108Path::GetSystemLibraryPath2() {
109 return Path("/usr/lib/");
110}
111
112Path
113Path::GetLLVMDefaultConfigDir() {
114 return Path("/etc/llvm/");
115}
116
117Path
118Path::GetLLVMConfigDir() {
119 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000120 if (result.setDirectory(LLVM_ETCDIR))
Reid Spencer8e665952004-08-29 05:24:01 +0000121 return result;
122 return GetLLVMDefaultConfigDir();
123}
124
125Path
126Path::GetUserHomeDirectory() {
127 const char* home = getenv("HOME");
128 if (home) {
129 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000130 if (result.setDirectory(home))
Reid Spencer8e665952004-08-29 05:24:01 +0000131 return result;
132 }
133 return GetRootDirectory();
134}
135
136bool
Reid Spencer07adb282004-11-05 22:15:36 +0000137Path::isFile() const {
138 return (isValid() && path[path.length()-1] != '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000139}
140
141bool
Reid Spencer07adb282004-11-05 22:15:36 +0000142Path::isDirectory() const {
143 return (isValid() && path[path.length()-1] == '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000144}
145
146std::string
Reid Spencer07adb282004-11-05 22:15:36 +0000147Path::getBasename() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000148 // 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
Reid Spencer07adb282004-11-05 22:15:36 +0000158bool Path::hasMagicNumber(const std::string &Magic) const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000159 size_t len = Magic.size();
160 char buf[ 1 + len];
161 std::ifstream f(path.c_str());
162 f.read(buf, len);
163 buf[len] = '\0';
Reid Spencereaf18152004-11-14 22:08:36 +0000164 f.close();
Reid Spencer1b554b42004-09-11 04:55:08 +0000165 return Magic == buf;
166}
167
Reid Spencereaf18152004-11-14 22:08:36 +0000168bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
169 if (!isFile())
170 return false;
171 char buf[1 + len];
172 std::ifstream f(path.c_str());
173 f.read(buf,len);
174 buf[len] = '\0';
175 Magic = buf;
176 return true;
177}
178
Reid Spencer1b554b42004-09-11 04:55:08 +0000179bool
Reid Spencer07adb282004-11-05 22:15:36 +0000180Path::isBytecodeFile() const {
Reid Spencer9195f372004-11-09 20:26:31 +0000181 char buffer[ 4];
182 buffer[0] = 0;
183 std::ifstream f(path.c_str());
184 f.read(buffer, 4);
185 if (f.bad())
186 ThrowErrno("can't read file signature");
Reid Spencereaf18152004-11-14 22:08:36 +0000187
188 return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
189 (buffer[3] == 'c' || buffer[3] == 'm'));
Reid Spencer1b554b42004-09-11 04:55:08 +0000190}
191
192bool
Reid Spencer07adb282004-11-05 22:15:36 +0000193Path::isArchive() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000194 if (readable()) {
Reid Spencer07adb282004-11-05 22:15:36 +0000195 return hasMagicNumber("!<arch>\012");
Reid Spencer1b554b42004-09-11 04:55:08 +0000196 }
197 return false;
198}
199
200bool
Reid Spencer8e665952004-08-29 05:24:01 +0000201Path::exists() const {
202 return 0 == access(path.c_str(), F_OK );
203}
204
205bool
206Path::readable() const {
207 return 0 == access(path.c_str(), F_OK | R_OK );
208}
209
210bool
211Path::writable() const {
212 return 0 == access(path.c_str(), F_OK | W_OK );
213}
214
215bool
216Path::executable() const {
217 return 0 == access(path.c_str(), R_OK | X_OK );
218}
219
220std::string
221Path::getLast() const {
222 // Find the last slash
223 size_t pos = path.rfind('/');
224
225 // Handle the corner cases
226 if (pos == std::string::npos)
227 return path;
228
229 // If the last character is a slash
230 if (pos == path.length()-1) {
231 // Find the second to last slash
232 size_t pos2 = path.rfind('/', pos-1);
233 if (pos2 == std::string::npos)
234 return path.substr(0,pos);
235 else
236 return path.substr(pos2+1,pos-pos2-1);
237 }
238 // Return everything after the last slash
239 return path.substr(pos+1);
240}
241
Reid Spencerb608a812004-11-16 06:15:19 +0000242void
243Path::getStatusInfo(StatusInfo& info) const {
Reid Spencer9195f372004-11-09 20:26:31 +0000244 struct stat buf;
245 if (0 != stat(path.c_str(), &buf)) {
246 ThrowErrno(std::string("Can't get status: ")+path);
247 }
248 info.fileSize = buf.st_size;
Reid Spencereaf18152004-11-14 22:08:36 +0000249 info.modTime.fromEpochTime(buf.st_mtime);
Reid Spencer9195f372004-11-09 20:26:31 +0000250 info.mode = buf.st_mode;
251 info.user = buf.st_uid;
252 info.group = buf.st_gid;
Reid Spencereaf18152004-11-14 22:08:36 +0000253 info.isDir = S_ISDIR(buf.st_mode);
254 if (info.isDir && path[path.length()-1] != '/')
255 path += '/';
256}
257
258bool
Reid Spencerb608a812004-11-16 06:15:19 +0000259Path::getDirectoryContents(std::set<Path>& result) const {
Reid Spencereaf18152004-11-14 22:08:36 +0000260 if (!isDirectory())
261 return false;
262 DIR* direntries = ::opendir(path.c_str());
263 if (direntries == 0)
264 ThrowErrno(path + ": can't open directory");
265
266 result.clear();
267 struct dirent* de = ::readdir(direntries);
268 while (de != 0) {
269 if (de->d_name[0] != '.') {
270 Path aPath(path + (const char*)de->d_name);
271 struct stat buf;
272 if (0 != stat(aPath.path.c_str(), &buf))
273 ThrowErrno(aPath.path + ": can't get status");
274 if (S_ISDIR(buf.st_mode))
275 aPath.path += "/";
Reid Spencerb608a812004-11-16 06:15:19 +0000276 result.insert(aPath);
Reid Spencereaf18152004-11-14 22:08:36 +0000277 }
278 de = ::readdir(direntries);
279 }
280
281 closedir(direntries);
282 return true;
Reid Spencer9195f372004-11-09 20:26:31 +0000283}
284
Reid Spencer8e665952004-08-29 05:24:01 +0000285bool
Reid Spencer07adb282004-11-05 22:15:36 +0000286Path::setDirectory(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000287 if (a_path.size() == 0)
288 return false;
289 Path save(*this);
290 path = a_path;
291 size_t last = a_path.size() -1;
292 if (last != 0 && a_path[last] != '/')
293 path += '/';
Reid Spencer07adb282004-11-05 22:15:36 +0000294 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000295 path = save.path;
296 return false;
297 }
298 return true;
299}
300
301bool
Reid Spencer07adb282004-11-05 22:15:36 +0000302Path::setFile(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000303 if (a_path.size() == 0)
304 return false;
305 Path save(*this);
306 path = a_path;
307 size_t last = a_path.size() - 1;
308 while (last > 0 && a_path[last] == '/')
309 last--;
310 path.erase(last+1);
Reid Spencer07adb282004-11-05 22:15:36 +0000311 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000312 path = save.path;
313 return false;
314 }
315 return true;
316}
317
318bool
Reid Spencer07adb282004-11-05 22:15:36 +0000319Path::appendDirectory(const std::string& dir) {
320 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000321 return false;
322 Path save(*this);
323 path += dir;
324 path += "/";
Reid Spencer07adb282004-11-05 22:15:36 +0000325 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000326 path = save.path;
327 return false;
328 }
329 return true;
330}
331
332bool
Reid Spencer07adb282004-11-05 22:15:36 +0000333Path::elideDirectory() {
334 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000335 return false;
336 size_t slashpos = path.rfind('/',path.size());
337 if (slashpos == 0 || slashpos == std::string::npos)
338 return false;
339 if (slashpos == path.size() - 1)
340 slashpos = path.rfind('/',slashpos-1);
341 if (slashpos == std::string::npos)
342 return false;
343 path.erase(slashpos);
344 return true;
345}
346
347bool
Reid Spencer07adb282004-11-05 22:15:36 +0000348Path::appendFile(const std::string& file) {
349 if (!isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000350 return false;
351 Path save(*this);
352 path += file;
Reid Spencer07adb282004-11-05 22:15:36 +0000353 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000354 path = save.path;
355 return false;
356 }
357 return true;
358}
359
360bool
Reid Spencer07adb282004-11-05 22:15:36 +0000361Path::elideFile() {
362 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000363 return false;
364 size_t slashpos = path.rfind('/',path.size());
365 if (slashpos == std::string::npos)
366 return false;
367 path.erase(slashpos+1);
368 return true;
369}
370
371bool
Reid Spencer07adb282004-11-05 22:15:36 +0000372Path::appendSuffix(const std::string& suffix) {
373 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000374 return false;
375 Path save(*this);
376 path.append(".");
377 path.append(suffix);
Reid Spencer07adb282004-11-05 22:15:36 +0000378 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000379 path = save.path;
380 return false;
381 }
382 return true;
383}
384
385bool
Reid Spencer07adb282004-11-05 22:15:36 +0000386Path::elideSuffix() {
387 if (isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000388 size_t dotpos = path.rfind('.',path.size());
389 size_t slashpos = path.rfind('/',path.size());
390 if (slashpos != std::string::npos && dotpos != std::string::npos &&
391 dotpos > slashpos) {
392 path.erase(dotpos, path.size()-dotpos);
393 return true;
394 }
395 return false;
396}
397
398
399bool
Reid Spencer07adb282004-11-05 22:15:36 +0000400Path::createDirectory( bool create_parents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000401 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000402 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000403
404 // Get a writeable copy of the path name
405 char pathname[MAXPATHLEN];
406 path.copy(pathname,MAXPATHLEN);
407
408 // Null-terminate the last component
409 int lastchar = path.length() - 1 ;
410 if (pathname[lastchar] == '/')
411 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000412 else
413 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000414
415 // If we're supposed to create intermediate directories
416 if ( create_parents ) {
417 // Find the end of the initial name component
418 char * next = strchr(pathname,'/');
419 if ( pathname[0] == '/')
420 next = strchr(&pathname[1],'/');
421
422 // Loop through the directory components until we're done
423 while ( next != 0 ) {
424 *next = 0;
425 if (0 != access(pathname, F_OK | R_OK | W_OK))
426 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
427 ThrowErrno(std::string(pathname) + ": Can't create directory");
428 char* save = next;
Reid Spencereaf18152004-11-14 22:08:36 +0000429 next = strchr(next+1,'/');
Reid Spencer8e665952004-08-29 05:24:01 +0000430 *save = '/';
431 }
Reid Spencer8e665952004-08-29 05:24:01 +0000432 }
Reid Spencereaf18152004-11-14 22:08:36 +0000433
434 if (0 != access(pathname, F_OK | R_OK))
435 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
436 ThrowErrno(std::string(pathname) + ": Can't create directory");
Reid Spencer8e665952004-08-29 05:24:01 +0000437 return true;
438}
439
440bool
Reid Spencer07adb282004-11-05 22:15:36 +0000441Path::createFile() {
Reid Spencer8e665952004-08-29 05:24:01 +0000442 // Make sure we're dealing with a file
Reid Spencer07adb282004-11-05 22:15:36 +0000443 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000444
445 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000446 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
447 if (fd < 0)
Reid Spencer9195f372004-11-09 20:26:31 +0000448 ThrowErrno(path + ": Can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000449 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000450
451 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000452}
453
Reid Spencer8e665952004-08-29 05:24:01 +0000454bool
Reid Spencer9195f372004-11-09 20:26:31 +0000455Path::createTemporaryFile() {
456 // Make sure we're dealing with a file
457 if (!isFile()) return false;
458
459 // Append the filename filler
460 char pathname[MAXPATHLEN];
461 path.copy(pathname,MAXPATHLEN);
Reid Spencereaf18152004-11-14 22:08:36 +0000462 pathname[path.length()] = 0;
Reid Spencer9195f372004-11-09 20:26:31 +0000463 strcat(pathname,"XXXXXX");
464 int fd = ::mkstemp(pathname);
465 if (fd < 0) {
466 ThrowErrno(path + ": Can't create temporary file");
467 }
468 path = pathname;
469 ::close(fd);
470 return true;
471}
472
473bool
Reid Spencer07adb282004-11-05 22:15:36 +0000474Path::destroyDirectory(bool remove_contents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000475 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000476 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000477
478 // If it doesn't exist, we're done.
479 if (!exists()) return true;
480
481 if (remove_contents) {
482 // Recursively descend the directory to remove its content
483 std::string cmd("/bin/rm -rf ");
484 cmd += path;
485 system(cmd.c_str());
486 } else {
487 // Otherwise, try to just remove the one directory
488 char pathname[MAXPATHLEN];
489 path.copy(pathname,MAXPATHLEN);
490 int lastchar = path.length() - 1 ;
491 if (pathname[lastchar] == '/')
492 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000493 else
494 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000495 if ( 0 != rmdir(pathname))
496 ThrowErrno(std::string(pathname) + ": Can't destroy directory");
497 }
498 return true;
499}
500
501bool
Reid Spencer07adb282004-11-05 22:15:36 +0000502Path::destroyFile() {
503 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000504 if (0 != unlink(path.c_str()))
Reid Spencereaf18152004-11-14 22:08:36 +0000505 ThrowErrno(path + ": Can't destroy file");
506 return true;
507}
508
509bool
510Path::renameFile(const Path& newName) {
511 if (!isFile()) return false;
512 if (0 != rename(path.c_str(), newName.c_str()))
513 ThrowErrno(std::string("can't rename ") + path + " as " + newName.get());
514 return true;
515}
516
517bool
518Path::setStatusInfo(const StatusInfo& si) const {
519 if (!isFile()) return false;
520 struct utimbuf utb;
521 utb.actime = si.modTime.toPosixTime();
522 utb.modtime = utb.actime;
523 if (0 != ::utime(path.c_str(),&utb))
524 ThrowErrno(path + ": can't set file modification time");
525 if (0 != ::chmod(path.c_str(),si.mode))
526 ThrowErrno(path + ": can't set mode");
Reid Spencer8e665952004-08-29 05:24:01 +0000527 return true;
528}
529
Reid Spencerb89a2232004-08-25 06:20:07 +0000530}
531
532// vim: sw=2