blob: 79786358ac70cabd0bc759473a5fe54d69a5c143 [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
Misha Brukman210b32b2004-12-20 00:16:38 +000019#include "llvm/Config/alloca.h"
Reid Spencerb89a2232004-08-25 06:20:07 +000020#include "Unix.h"
Reid Spenceraf2f2082004-12-27 06:17:15 +000021#if HAVE_SYS_STAT_H
Reid Spencerb89a2232004-08-25 06:20:07 +000022#include <sys/stat.h>
Reid Spenceraf2f2082004-12-27 06:17:15 +000023#endif
24#if HAVE_FCNTL_H
Reid Spencerb89a2232004-08-25 06:20:07 +000025#include <fcntl.h>
Reid Spenceraf2f2082004-12-27 06:17:15 +000026#endif
27#if HAVE_UTIME_H
Reid Spencereaf18152004-11-14 22:08:36 +000028#include <utime.h>
Reid Spenceraf2f2082004-12-27 06:17:15 +000029#endif
30#if HAVE_TIME_H
Reid Spencer69a16162004-12-24 06:29:42 +000031#include <time.h>
Reid Spenceraf2f2082004-12-27 06:17:15 +000032#endif
33#if HAVE_DIRENT_H
34# include <dirent.h>
35# define NAMLEN(dirent) strlen((dirent)->d_name)
36#else
37# define dirent direct
38# define NAMLEN(dirent) (dirent)->d_namlen
39# if HAVE_SYS_NDIR_H
40# include <sys/ndir.h>
41# endif
42# if HAVE_SYS_DIR_H
43# include <sys/dir.h>
44# endif
45# if HAVE_NDIR_H
46# include <ndir.h>
47# endif
48#endif
49
Reid Spencer932e2e32005-06-02 05:38:20 +000050// Put in a hack for Cygwin which falsely reports that the mkdtemp function
51// is available when it is not.
52#ifdef __CYGWIN__
53# undef HAVE_MKDTEMP
54#endif
Reid Spencerb89a2232004-08-25 06:20:07 +000055
Reid Spencer8e665952004-08-29 05:24:01 +000056namespace llvm {
57using namespace sys;
Reid Spencerb89a2232004-08-25 06:20:07 +000058
Misha Brukman210b32b2004-12-20 00:16:38 +000059Path::Path(const std::string& unverified_path) : path(unverified_path) {
Reid Spencer8e665952004-08-29 05:24:01 +000060 if (unverified_path.empty())
61 return;
Reid Spencer07adb282004-11-05 22:15:36 +000062 if (this->isValid())
Reid Spencer8e665952004-08-29 05:24:01 +000063 return;
64 // oops, not valid.
65 path.clear();
66 ThrowErrno(unverified_path + ": path is not valid");
Reid Spencerb89a2232004-08-25 06:20:07 +000067}
68
Reid Spencer69a16162004-12-24 06:29:42 +000069bool
70Path::isValid() const {
71 if (path.empty())
72 return false;
73 else if (path.length() >= MAXPATHLEN)
74 return false;
75#if defined(HAVE_REALPATH)
76 char pathname[MAXPATHLEN];
77 if (0 == realpath(path.c_str(), pathname))
78 if (errno != EACCES && errno != EIO && errno != ENOENT && errno != ENOTDIR)
79 return false;
80#endif
81 return true;
82}
83
Reid Spencer8e665952004-08-29 05:24:01 +000084Path
85Path::GetRootDirectory() {
86 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +000087 result.set("/");
Reid Spencer8e665952004-08-29 05:24:01 +000088 return result;
89}
90
Reid Spencer69a16162004-12-24 06:29:42 +000091Path
92Path::GetTemporaryDirectory() {
93#if defined(HAVE_MKDTEMP)
94 // The best way is with mkdtemp but that's not available on many systems,
95 // Linux and FreeBSD have it. Others probably won't.
96 char pathname[MAXPATHLEN];
97 strcpy(pathname,"/tmp/llvm_XXXXXX");
98 if (0 == mkdtemp(pathname))
Reid Spencer2aafadc2005-04-21 02:50:10 +000099 ThrowErrno(std::string(pathname) + ": can't create temporary directory");
Reid Spencer69a16162004-12-24 06:29:42 +0000100 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +0000101 result.set(pathname);
Reid Spencer69a16162004-12-24 06:29:42 +0000102 assert(result.isValid() && "mkdtemp didn't create a valid pathname!");
103 return result;
104#elif defined(HAVE_MKSTEMP)
105 // If no mkdtemp is available, mkstemp can be used to create a temporary file
106 // which is then removed and created as a directory. We prefer this over
107 // mktemp because of mktemp's inherent security and threading risks. We still
108 // have a slight race condition from the time the temporary file is created to
109 // the time it is re-created as a directoy.
110 char pathname[MAXPATHLEN];
111 strcpy(pathname, "/tmp/llvm_XXXXXX");
112 int fd = 0;
113 if (-1 == (fd = mkstemp(pathname)))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000114 ThrowErrno(std::string(pathname) + ": can't create temporary directory");
Reid Spencer69a16162004-12-24 06:29:42 +0000115 ::close(fd);
116 ::unlink(pathname); // start race condition, ignore errors
117 if (-1 == ::mkdir(pathname, S_IRWXU)) // end race condition
Reid Spencer2aafadc2005-04-21 02:50:10 +0000118 ThrowErrno(std::string(pathname) + ": can't create temporary directory");
Reid Spencer69a16162004-12-24 06:29:42 +0000119 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +0000120 result.set(pathname);
Reid Spencer69a16162004-12-24 06:29:42 +0000121 assert(result.isValid() && "mkstemp didn't create a valid pathname!");
122 return result;
123#elif defined(HAVE_MKTEMP)
124 // If a system doesn't have mkdtemp(3) or mkstemp(3) but it does have
125 // mktemp(3) then we'll assume that system (e.g. AIX) has a reasonable
126 // implementation of mktemp(3) and doesn't follow BSD 4.3's lead of replacing
127 // the XXXXXX with the pid of the process and a letter. That leads to only
128 // twenty six temporary files that can be generated.
129 char pathname[MAXPATHLEN];
130 strcpy(pathname, "/tmp/llvm_XXXXXX");
131 char *TmpName = ::mktemp(pathname);
132 if (TmpName == 0)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000133 ThrowErrno(std::string(TmpName) + ": can't create unique directory name");
Reid Spencer69a16162004-12-24 06:29:42 +0000134 if (-1 == ::mkdir(TmpName, S_IRWXU))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000135 ThrowErrno(std::string(TmpName) + ": can't create temporary directory");
Reid Spencer69a16162004-12-24 06:29:42 +0000136 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +0000137 result.set(TmpName);
Reid Spencer69a16162004-12-24 06:29:42 +0000138 assert(result.isValid() && "mktemp didn't create a valid pathname!");
139 return result;
140#else
141 // This is the worst case implementation. tempnam(3) leaks memory unless its
142 // on an SVID2 (or later) system. On BSD 4.3 it leaks. tmpnam(3) has thread
143 // issues. The mktemp(3) function doesn't have enough variability in the
144 // temporary name generated. So, we provide our own implementation that
145 // increments an integer from a random number seeded by the current time. This
146 // should be sufficiently unique that we don't have many collisions between
147 // processes. Generally LLVM processes don't run very long and don't use very
148 // many temporary files so this shouldn't be a big issue for LLVM.
149 static time_t num = ::time(0);
150 char pathname[MAXPATHLEN];
151 do {
152 num++;
153 sprintf(pathname, "/tmp/llvm_%010u", unsigned(num));
154 } while ( 0 == access(pathname, F_OK ) );
155 if (-1 == ::mkdir(pathname, S_IRWXU))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000156 ThrowErrno(std::string(pathname) + ": can't create temporary directory");
Reid Spencer69a16162004-12-24 06:29:42 +0000157 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +0000158 result.set(pathname);
Reid Spencer69a16162004-12-24 06:29:42 +0000159 assert(result.isValid() && "mkstemp didn't create a valid pathname!");
160 return result;
161#endif
162}
163
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000164static void getPathList(const char*path, std::vector<sys::Path>& Paths) {
165 const char* at = path;
166 const char* delim = strchr(at, ':');
167 Path tmpPath;
168 while( delim != 0 ) {
169 std::string tmp(at, size_t(delim-at));
Reid Spencerdd04df02005-07-07 23:21:43 +0000170 if (tmpPath.set(tmp))
Reid Spencerc7f08322005-07-07 18:21:42 +0000171 if (tmpPath.canRead())
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000172 Paths.push_back(tmpPath);
173 at = delim + 1;
174 delim = strchr(at, ':');
Reid Spencer74e72612004-09-14 00:16:39 +0000175 }
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000176 if (*at != 0)
Reid Spencerdd04df02005-07-07 23:21:43 +0000177 if (tmpPath.set(std::string(at)))
Reid Spencerc7f08322005-07-07 18:21:42 +0000178 if (tmpPath.canRead())
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000179 Paths.push_back(tmpPath);
180
Reid Spencer74e72612004-09-14 00:16:39 +0000181}
Misha Brukman210b32b2004-12-20 00:16:38 +0000182
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000183void
184Path::GetSystemLibraryPaths(std::vector<sys::Path>& Paths) {
185#ifdef LTDL_SHLIBPATH_VAR
186 char* env_var = getenv(LTDL_SHLIBPATH_VAR);
187 if (env_var != 0) {
188 getPathList(env_var,Paths);
Reid Spencer74e72612004-09-14 00:16:39 +0000189 }
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000190#endif
191 // FIXME: Should this look at LD_LIBRARY_PATH too?
192 Paths.push_back(sys::Path("/usr/local/lib/"));
193 Paths.push_back(sys::Path("/usr/X11R6/lib/"));
194 Paths.push_back(sys::Path("/usr/lib/"));
195 Paths.push_back(sys::Path("/lib/"));
Reid Spencer74e72612004-09-14 00:16:39 +0000196}
197
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000198void
199Path::GetBytecodeLibraryPaths(std::vector<sys::Path>& Paths) {
200 char * env_var = getenv("LLVM_LIB_SEARCH_PATH");
201 if (env_var != 0) {
202 getPathList(env_var,Paths);
203 }
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000204#ifdef LLVM_LIBDIR
205 {
206 Path tmpPath;
Reid Spencerdd04df02005-07-07 23:21:43 +0000207 if (tmpPath.set(LLVM_LIBDIR))
Reid Spencerc7f08322005-07-07 18:21:42 +0000208 if (tmpPath.canRead())
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000209 Paths.push_back(tmpPath);
210 }
211#endif
212 GetSystemLibraryPaths(Paths);
Reid Spencer8e665952004-08-29 05:24:01 +0000213}
214
215Path
216Path::GetLLVMDefaultConfigDir() {
217 return Path("/etc/llvm/");
218}
219
Reid Spencer8e665952004-08-29 05:24:01 +0000220Path
221Path::GetUserHomeDirectory() {
222 const char* home = getenv("HOME");
223 if (home) {
224 Path result;
Reid Spencerdd04df02005-07-07 23:21:43 +0000225 if (result.set(home))
Reid Spencer8e665952004-08-29 05:24:01 +0000226 return result;
227 }
228 return GetRootDirectory();
229}
230
231bool
Reid Spencer07adb282004-11-05 22:15:36 +0000232Path::isFile() const {
Reid Spencerdd04df02005-07-07 23:21:43 +0000233 struct stat buf;
234 if (0 != stat(path.c_str(), &buf)) {
235 ThrowErrno(path + ": can't determine type of path object: ");
236 }
237 return S_ISREG(buf.st_mode);
Reid Spencer1b554b42004-09-11 04:55:08 +0000238}
239
240bool
Reid Spencer07adb282004-11-05 22:15:36 +0000241Path::isDirectory() const {
Reid Spencerdd04df02005-07-07 23:21:43 +0000242 struct stat buf;
243 if (0 != stat(path.c_str(), &buf)) {
244 ThrowErrno(path + ": can't determine type of path object: ");
245 }
246 return S_ISDIR(buf.st_mode);
Reid Spencer1b554b42004-09-11 04:55:08 +0000247}
248
Reid Spencera229c5c2005-07-08 03:08:58 +0000249bool
250Path::isHidden() const {
251 size_t slash = path.rfind('/');
252 return (slash != std::string::npos &&
253 slash < path.length()-1 &&
254 path[slash+1] == '.') ||
255 (!path.empty() && slash == std::string::npos && path[0] == '.');
256}
257
Reid Spencer1b554b42004-09-11 04:55:08 +0000258std::string
Reid Spencer07adb282004-11-05 22:15:36 +0000259Path::getBasename() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000260 // Find the last slash
261 size_t slash = path.rfind('/');
262 if (slash == std::string::npos)
263 slash = 0;
264 else
265 slash++;
266
267 return path.substr(slash, path.rfind('.'));
268}
269
Reid Spencer07adb282004-11-05 22:15:36 +0000270bool Path::hasMagicNumber(const std::string &Magic) const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000271 size_t len = Magic.size();
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000272 assert(len < 1024 && "Request for magic string too long");
273 char* buf = (char*) alloca(1 + len);
274 int fd = ::open(path.c_str(),O_RDONLY);
275 if (fd < 0)
276 return false;
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000277 size_t read_len = ::read(fd, buf, len);
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000278 close(fd);
Reid Spencer1b6b99b2004-12-13 03:00:51 +0000279 if (len != read_len)
280 return false;
Reid Spencer1b554b42004-09-11 04:55:08 +0000281 buf[len] = '\0';
282 return Magic == buf;
283}
284
Reid Spencereaf18152004-11-14 22:08:36 +0000285bool Path::getMagicNumber(std::string& Magic, unsigned len) const {
286 if (!isFile())
287 return false;
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000288 assert(len < 1024 && "Request for magic string too long");
289 char* buf = (char*) alloca(1 + len);
290 int fd = ::open(path.c_str(),O_RDONLY);
291 if (fd < 0)
292 return false;
Reid Spencer86ac2dc2004-12-02 09:09:48 +0000293 ssize_t bytes_read = ::read(fd, buf, len);
294 ::close(fd);
295 if (ssize_t(len) != bytes_read) {
296 Magic.clear();
Reid Spencerbe31d2a2004-11-16 17:14:08 +0000297 return false;
Reid Spencer86ac2dc2004-12-02 09:09:48 +0000298 }
299 Magic.assign(buf,len);
Reid Spencereaf18152004-11-14 22:08:36 +0000300 return true;
301}
302
Reid Spencer1b554b42004-09-11 04:55:08 +0000303bool
Reid Spencer07adb282004-11-05 22:15:36 +0000304Path::isBytecodeFile() const {
Reid Spencer9195f372004-11-09 20:26:31 +0000305 char buffer[ 4];
306 buffer[0] = 0;
Reid Spencer1fce0912004-12-11 00:14:15 +0000307 int fd = ::open(path.c_str(),O_RDONLY);
308 if (fd < 0)
309 return false;
310 ssize_t bytes_read = ::read(fd, buffer, 4);
311 ::close(fd);
312 if (4 != bytes_read)
313 return false;
Reid Spencereaf18152004-11-14 22:08:36 +0000314
315 return (buffer[0] == 'l' && buffer[1] == 'l' && buffer[2] == 'v' &&
316 (buffer[3] == 'c' || buffer[3] == 'm'));
Reid Spencer1b554b42004-09-11 04:55:08 +0000317}
318
319bool
Reid Spencer8e665952004-08-29 05:24:01 +0000320Path::exists() const {
321 return 0 == access(path.c_str(), F_OK );
322}
323
324bool
Reid Spencerc7f08322005-07-07 18:21:42 +0000325Path::canRead() const {
Reid Spencer8e665952004-08-29 05:24:01 +0000326 return 0 == access(path.c_str(), F_OK | R_OK );
327}
328
329bool
Reid Spencerc7f08322005-07-07 18:21:42 +0000330Path::canWrite() const {
Reid Spencer8e665952004-08-29 05:24:01 +0000331 return 0 == access(path.c_str(), F_OK | W_OK );
332}
333
334bool
Reid Spencerc7f08322005-07-07 18:21:42 +0000335Path::canExecute() const {
Misha Brukman8177bf82005-04-20 15:33:22 +0000336 struct stat st;
337 int r = stat(path.c_str(), &st);
338 if (r != 0 || !S_ISREG(st.st_mode))
339 return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000340 return 0 == access(path.c_str(), R_OK | X_OK );
341}
342
343std::string
344Path::getLast() const {
345 // Find the last slash
346 size_t pos = path.rfind('/');
347
348 // Handle the corner cases
349 if (pos == std::string::npos)
350 return path;
351
352 // If the last character is a slash
353 if (pos == path.length()-1) {
354 // Find the second to last slash
355 size_t pos2 = path.rfind('/', pos-1);
356 if (pos2 == std::string::npos)
357 return path.substr(0,pos);
358 else
359 return path.substr(pos2+1,pos-pos2-1);
360 }
361 // Return everything after the last slash
362 return path.substr(pos+1);
363}
364
Reid Spencerb608a812004-11-16 06:15:19 +0000365void
366Path::getStatusInfo(StatusInfo& info) const {
Reid Spencer9195f372004-11-09 20:26:31 +0000367 struct stat buf;
368 if (0 != stat(path.c_str(), &buf)) {
Reid Spencer2aafadc2005-04-21 02:50:10 +0000369 ThrowErrno(path + ": can't determine type of path object: ");
Reid Spencer9195f372004-11-09 20:26:31 +0000370 }
371 info.fileSize = buf.st_size;
Reid Spencereaf18152004-11-14 22:08:36 +0000372 info.modTime.fromEpochTime(buf.st_mtime);
Reid Spencer9195f372004-11-09 20:26:31 +0000373 info.mode = buf.st_mode;
374 info.user = buf.st_uid;
375 info.group = buf.st_gid;
Reid Spencereaf18152004-11-14 22:08:36 +0000376 info.isDir = S_ISDIR(buf.st_mode);
Reid Spencereaf18152004-11-14 22:08:36 +0000377}
378
Reid Spencer77cc91d2004-12-13 19:59:50 +0000379static bool AddPermissionBits(const std::string& Filename, int bits) {
380 // Get the umask value from the operating system. We want to use it
381 // when changing the file's permissions. Since calling umask() sets
382 // the umask and returns its old value, we must call it a second
383 // time to reset it to the user's preference.
384 int mask = umask(0777); // The arg. to umask is arbitrary.
385 umask(mask); // Restore the umask.
386
387 // Get the file's current mode.
388 struct stat st;
389 if ((stat(Filename.c_str(), &st)) == -1)
390 return false;
391
392 // Change the file to have whichever permissions bits from 'bits'
393 // that the umask would not disable.
394 if ((chmod(Filename.c_str(), (st.st_mode | (bits & ~mask)))) == -1)
395 return false;
396
397 return true;
398}
399
Reid Spencera229c5c2005-07-08 03:08:58 +0000400void Path::makeReadableOnDisk() {
Reid Spencer77cc91d2004-12-13 19:59:50 +0000401 if (!AddPermissionBits(path,0444))
402 ThrowErrno(path + ": can't make file readable");
403}
404
Reid Spencera229c5c2005-07-08 03:08:58 +0000405void Path::makeWriteableOnDisk() {
Reid Spencer77cc91d2004-12-13 19:59:50 +0000406 if (!AddPermissionBits(path,0222))
407 ThrowErrno(path + ": can't make file writable");
408}
409
Reid Spencera229c5c2005-07-08 03:08:58 +0000410void Path::makeExecutableOnDisk() {
Reid Spencer77cc91d2004-12-13 19:59:50 +0000411 if (!AddPermissionBits(path,0111))
412 ThrowErrno(path + ": can't make file executable");
413}
414
Reid Spencereaf18152004-11-14 22:08:36 +0000415bool
Reid Spencerb608a812004-11-16 06:15:19 +0000416Path::getDirectoryContents(std::set<Path>& result) const {
Reid Spencereaf18152004-11-14 22:08:36 +0000417 if (!isDirectory())
418 return false;
419 DIR* direntries = ::opendir(path.c_str());
420 if (direntries == 0)
421 ThrowErrno(path + ": can't open directory");
422
423 result.clear();
424 struct dirent* de = ::readdir(direntries);
Misha Brukman4619c752005-04-20 04:04:07 +0000425 for ( ; de != 0; de = ::readdir(direntries)) {
Reid Spencereaf18152004-11-14 22:08:36 +0000426 if (de->d_name[0] != '.') {
427 Path aPath(path + (const char*)de->d_name);
428 struct stat buf;
Misha Brukman4619c752005-04-20 04:04:07 +0000429 if (0 != stat(aPath.path.c_str(), &buf)) {
Reid Spencer2aafadc2005-04-21 02:50:10 +0000430 int stat_errno = errno;
Misha Brukman4619c752005-04-20 04:04:07 +0000431 struct stat st;
432 if (0 == lstat(aPath.path.c_str(), &st) && S_ISLNK(st.st_mode))
433 continue; // dangling symlink -- ignore
Reid Spencer2aafadc2005-04-21 02:50:10 +0000434 ThrowErrno(aPath.path +
435 ": can't determine file object type", stat_errno);
Misha Brukman4619c752005-04-20 04:04:07 +0000436 }
Reid Spencerb608a812004-11-16 06:15:19 +0000437 result.insert(aPath);
Reid Spencereaf18152004-11-14 22:08:36 +0000438 }
Reid Spencereaf18152004-11-14 22:08:36 +0000439 }
440
441 closedir(direntries);
442 return true;
Reid Spencer9195f372004-11-09 20:26:31 +0000443}
444
Reid Spencer8e665952004-08-29 05:24:01 +0000445bool
Reid Spencerdd04df02005-07-07 23:21:43 +0000446Path::set(const std::string& a_path) {
447 if (a_path.empty())
Reid Spencer8e665952004-08-29 05:24:01 +0000448 return false;
Reid Spencerdd04df02005-07-07 23:21:43 +0000449 std::string save(path);
Reid Spencer8e665952004-08-29 05:24:01 +0000450 path = a_path;
Reid Spencer07adb282004-11-05 22:15:36 +0000451 if (!isValid()) {
Reid Spencerdd04df02005-07-07 23:21:43 +0000452 path = save;
Reid Spencer8e665952004-08-29 05:24:01 +0000453 return false;
454 }
455 return true;
456}
457
458bool
Reid Spencerdd04df02005-07-07 23:21:43 +0000459Path::appendComponent(const std::string& name) {
460 if (name.empty())
Reid Spencer8e665952004-08-29 05:24:01 +0000461 return false;
Reid Spencerdd04df02005-07-07 23:21:43 +0000462 std::string save(path);
463 if (!path.empty()) {
464 size_t last = path.size() - 1;
465 if (path[last] != '/')
466 path += '/';
467 }
468 path += name;
Reid Spencer07adb282004-11-05 22:15:36 +0000469 if (!isValid()) {
Reid Spencerdd04df02005-07-07 23:21:43 +0000470 path = save;
Reid Spencer8e665952004-08-29 05:24:01 +0000471 return false;
472 }
473 return true;
474}
475
476bool
Reid Spencerdd04df02005-07-07 23:21:43 +0000477Path::eraseComponent() {
Reid Spencer8e665952004-08-29 05:24:01 +0000478 size_t slashpos = path.rfind('/',path.size());
Reid Spencerdd04df02005-07-07 23:21:43 +0000479 if (slashpos == 0 || slashpos == std::string::npos) {
480 path.erase();
481 return true;
482 }
Reid Spencer8e665952004-08-29 05:24:01 +0000483 if (slashpos == path.size() - 1)
484 slashpos = path.rfind('/',slashpos-1);
Reid Spencerdd04df02005-07-07 23:21:43 +0000485 if (slashpos == std::string::npos) {
486 path.erase();
487 return true;
488 }
Reid Spencer8e665952004-08-29 05:24:01 +0000489 path.erase(slashpos);
490 return true;
491}
492
493bool
Reid Spencer07adb282004-11-05 22:15:36 +0000494Path::appendSuffix(const std::string& suffix) {
Reid Spencerdd04df02005-07-07 23:21:43 +0000495 std::string save(path);
Reid Spencer8e665952004-08-29 05:24:01 +0000496 path.append(".");
497 path.append(suffix);
Reid Spencer07adb282004-11-05 22:15:36 +0000498 if (!isValid()) {
Reid Spencerdd04df02005-07-07 23:21:43 +0000499 path = save;
Reid Spencer8e665952004-08-29 05:24:01 +0000500 return false;
501 }
502 return true;
503}
504
Reid Spencerdd04df02005-07-07 23:21:43 +0000505bool
506Path::eraseSuffix() {
Reid Spencer8e665952004-08-29 05:24:01 +0000507 size_t dotpos = path.rfind('.',path.size());
508 size_t slashpos = path.rfind('/',path.size());
Jeff Cohen563a17f2005-07-08 04:49:16 +0000509 if (dotpos != std::string::npos) {
510 if (slashpos == std::string::npos || dotpos > slashpos) {
511 path.erase(dotpos, path.size()-dotpos);
512 return true;
513 }
Reid Spencer8e665952004-08-29 05:24:01 +0000514 }
Jeff Cohen563a17f2005-07-08 04:49:16 +0000515 return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000516}
517
Reid Spencer8e665952004-08-29 05:24:01 +0000518bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000519Path::createDirectoryOnDisk( bool create_parents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000520 // Get a writeable copy of the path name
521 char pathname[MAXPATHLEN];
522 path.copy(pathname,MAXPATHLEN);
523
524 // Null-terminate the last component
525 int lastchar = path.length() - 1 ;
526 if (pathname[lastchar] == '/')
527 pathname[lastchar] = 0;
Reid Spencereaf18152004-11-14 22:08:36 +0000528 else
529 pathname[lastchar+1] = 0;
Reid Spencer8e665952004-08-29 05:24:01 +0000530
531 // If we're supposed to create intermediate directories
532 if ( create_parents ) {
533 // Find the end of the initial name component
534 char * next = strchr(pathname,'/');
535 if ( pathname[0] == '/')
536 next = strchr(&pathname[1],'/');
537
538 // Loop through the directory components until we're done
539 while ( next != 0 ) {
540 *next = 0;
541 if (0 != access(pathname, F_OK | R_OK | W_OK))
542 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000543 ThrowErrno(std::string(pathname) + ": can't create directory");
Reid Spencer8e665952004-08-29 05:24:01 +0000544 char* save = next;
Reid Spencereaf18152004-11-14 22:08:36 +0000545 next = strchr(next+1,'/');
Reid Spencer8e665952004-08-29 05:24:01 +0000546 *save = '/';
547 }
Reid Spencer8e665952004-08-29 05:24:01 +0000548 }
Reid Spencereaf18152004-11-14 22:08:36 +0000549
550 if (0 != access(pathname, F_OK | R_OK))
551 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000552 ThrowErrno(std::string(pathname) + ": can't create directory");
Reid Spencer8e665952004-08-29 05:24:01 +0000553 return true;
554}
555
556bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000557Path::createFileOnDisk() {
Reid Spencer8e665952004-08-29 05:24:01 +0000558 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000559 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
560 if (fd < 0)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000561 ThrowErrno(path + ": can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000562 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000563
564 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000565}
566
Reid Spencer8e665952004-08-29 05:24:01 +0000567bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000568Path::createTemporaryFileOnDisk(bool reuse_current) {
Reid Spencerc29befb2004-12-15 01:50:13 +0000569 // Make this into a unique file name
Reid Spencer07f9f4e2004-12-15 08:32:45 +0000570 makeUnique( reuse_current );
Reid Spencerc29befb2004-12-15 01:50:13 +0000571
572 // create the file
573 int outFile = ::open(path.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
574 if (outFile != -1) {
575 ::close(outFile);
576 return true;
Reid Spencer9195f372004-11-09 20:26:31 +0000577 }
Reid Spencerc29befb2004-12-15 01:50:13 +0000578 return false;
Reid Spencer9195f372004-11-09 20:26:31 +0000579}
580
581bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000582Path::eraseFromDisk(bool remove_contents) const {
Reid Spencer8e665952004-08-29 05:24:01 +0000583 // Make sure we're dealing with a directory
Reid Spencerdd04df02005-07-07 23:21:43 +0000584 if (isFile()) {
585 if (0 != unlink(path.c_str()))
586 ThrowErrno(path + ": can't destroy file");
587 } else if (isDirectory()) {
588 if (remove_contents) {
589 // Recursively descend the directory to remove its content
590 std::string cmd("/bin/rm -rf ");
591 cmd += path;
592 system(cmd.c_str());
593 } else {
594 // Otherwise, try to just remove the one directory
595 char pathname[MAXPATHLEN];
596 path.copy(pathname,MAXPATHLEN);
597 int lastchar = path.length() - 1 ;
598 if (pathname[lastchar] == '/')
599 pathname[lastchar] = 0;
600 else
601 pathname[lastchar+1] = 0;
602 if ( 0 != rmdir(pathname))
603 ThrowErrno(std::string(pathname) + ": can't destroy directory");
604 }
Reid Spencer8e665952004-08-29 05:24:01 +0000605 }
Reid Spencerdd04df02005-07-07 23:21:43 +0000606 else
607 return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000608 return true;
609}
610
611bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000612Path::renamePathOnDisk(const Path& newName) {
Reid Spencerdd04df02005-07-07 23:21:43 +0000613 if (0 != ::rename(path.c_str(), newName.c_str()))
Reid Spencer2aafadc2005-04-21 02:50:10 +0000614 ThrowErrno(std::string("can't rename '") + path + "' as '" +
615 newName.toString() + "' ");
Reid Spencereaf18152004-11-14 22:08:36 +0000616 return true;
617}
618
619bool
Reid Spencera229c5c2005-07-08 03:08:58 +0000620Path::setStatusInfoOnDisk(const StatusInfo& si) const {
Reid Spencereaf18152004-11-14 22:08:36 +0000621 struct utimbuf utb;
622 utb.actime = si.modTime.toPosixTime();
623 utb.modtime = utb.actime;
624 if (0 != ::utime(path.c_str(),&utb))
625 ThrowErrno(path + ": can't set file modification time");
626 if (0 != ::chmod(path.c_str(),si.mode))
627 ThrowErrno(path + ": can't set mode");
Reid Spencer8e665952004-08-29 05:24:01 +0000628 return true;
629}
630
Reid Spencerc29befb2004-12-15 01:50:13 +0000631void
Reid Spencer78076f62004-12-21 03:27:08 +0000632sys::CopyFile(const sys::Path &Dest, const sys::Path &Src) {
Reid Spencerc29befb2004-12-15 01:50:13 +0000633 int inFile = -1;
634 int outFile = -1;
635 try {
636 inFile = ::open(Src.c_str(), O_RDONLY);
637 if (inFile == -1)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000638 ThrowErrno(Src.toString() + ": can't open source file to copy: ");
Reid Spencerc29befb2004-12-15 01:50:13 +0000639
640 outFile = ::open(Dest.c_str(), O_WRONLY|O_CREAT, 0666);
641 if (outFile == -1)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000642 ThrowErrno(Dest.toString() +": can't create destination file for copy: ");
Reid Spencerc29befb2004-12-15 01:50:13 +0000643
644 char Buffer[16*1024];
645 while (ssize_t Amt = ::read(inFile, Buffer, 16*1024)) {
646 if (Amt == -1) {
647 if (errno != EINTR && errno != EAGAIN)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000648 ThrowErrno(Src.toString()+": can't read source file: ");
Reid Spencerc29befb2004-12-15 01:50:13 +0000649 } else {
650 char *BufPtr = Buffer;
651 while (Amt) {
652 ssize_t AmtWritten = ::write(outFile, BufPtr, Amt);
653 if (AmtWritten == -1) {
654 if (errno != EINTR && errno != EAGAIN)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000655 ThrowErrno(Dest.toString() + ": can't write destination file: ");
Reid Spencerc29befb2004-12-15 01:50:13 +0000656 } else {
657 Amt -= AmtWritten;
658 BufPtr += AmtWritten;
659 }
660 }
661 }
662 }
663 ::close(inFile);
664 ::close(outFile);
665 } catch (...) {
666 if (inFile != -1)
667 ::close(inFile);
668 if (outFile != -1)
669 ::close(outFile);
670 throw;
671 }
672}
673
674void
Reid Spencer07f9f4e2004-12-15 08:32:45 +0000675Path::makeUnique(bool reuse_current) {
676 if (reuse_current && !exists())
Reid Spencerc29befb2004-12-15 01:50:13 +0000677 return; // File doesn't exist already, just use it!
678
679 // Append an XXXXXX pattern to the end of the file for use with mkstemp,
680 // mktemp or our own implementation.
681 char *FNBuffer = (char*) alloca(path.size()+8);
682 path.copy(FNBuffer,path.size());
683 strcpy(FNBuffer+path.size(), "-XXXXXX");
684
685#if defined(HAVE_MKSTEMP)
686 int TempFD;
687 if ((TempFD = mkstemp(FNBuffer)) == -1) {
Reid Spencer2aafadc2005-04-21 02:50:10 +0000688 ThrowErrno(path + ": can't make unique filename");
Reid Spencerc29befb2004-12-15 01:50:13 +0000689 }
690
691 // We don't need to hold the temp file descriptor... we will trust that no one
692 // will overwrite/delete the file before we can open it again.
693 close(TempFD);
694
695 // Save the name
696 path = FNBuffer;
697#elif defined(HAVE_MKTEMP)
698 // If we don't have mkstemp, use the old and obsolete mktemp function.
699 if (mktemp(FNBuffer) == 0) {
Reid Spencer2aafadc2005-04-21 02:50:10 +0000700 ThrowErrno(path + ": can't make unique filename");
Reid Spencerc29befb2004-12-15 01:50:13 +0000701 }
702
703 // Save the name
704 path = FNBuffer;
705#else
706 // Okay, looks like we have to do it all by our lonesome.
707 static unsigned FCounter = 0;
708 unsigned offset = path.size() + 1;
709 while ( FCounter < 999999 && exists()) {
710 sprintf(FNBuffer+offset,"%06u",++FCounter);
711 path = FNBuffer;
712 }
713 if (FCounter > 999999)
Reid Spencer2aafadc2005-04-21 02:50:10 +0000714 throw std::string(path + ": can't make unique filename: too many files");
Reid Spencerc29befb2004-12-15 01:50:13 +0000715#endif
716
717}
Reid Spencerb89a2232004-08-25 06:20:07 +0000718}
719