blob: c3c6775968cdef09567be5a4e099709e25e79c70 [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 Spencerb89a2232004-08-25 06:20:07 +000024
Reid Spencer8e665952004-08-29 05:24:01 +000025namespace llvm {
26using namespace sys;
Reid Spencerb89a2232004-08-25 06:20:07 +000027
Reid Spencer8e665952004-08-29 05:24:01 +000028Path::Path(std::string unverified_path)
29 : path(unverified_path)
Reid Spencerb89a2232004-08-25 06:20:07 +000030{
Reid Spencer8e665952004-08-29 05:24:01 +000031 if (unverified_path.empty())
32 return;
Reid Spencer07adb282004-11-05 22:15:36 +000033 if (this->isValid())
Reid Spencer8e665952004-08-29 05:24:01 +000034 return;
35 // oops, not valid.
36 path.clear();
37 ThrowErrno(unverified_path + ": path is not valid");
Reid Spencerb89a2232004-08-25 06:20:07 +000038}
39
Reid Spencer8e665952004-08-29 05:24:01 +000040Path
41Path::GetRootDirectory() {
42 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +000043 result.setDirectory("/");
Reid Spencer8e665952004-08-29 05:24:01 +000044 return result;
45}
46
Reid Spencer74e72612004-09-14 00:16:39 +000047static inline bool IsLibrary(Path& path, const std::string& basename) {
Reid Spencer07adb282004-11-05 22:15:36 +000048 if (path.appendFile(std::string("lib") + basename)) {
49 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000050 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000051 else if (path.elideSuffix() && path.appendSuffix("a") && 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("o") && 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("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000056 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000057 } else if (path.elideFile() && path.appendFile(basename)) {
58 if (path.appendSuffix(Path::GetDLLSuffix()) && path.readable())
Reid Spencer74e72612004-09-14 00:16:39 +000059 return true;
Reid Spencer07adb282004-11-05 22:15:36 +000060 else if (path.elideSuffix() && path.appendSuffix("a") && 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("o") && 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("bc") && path.readable())
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000065 return true;
Reid Spencer74e72612004-09-14 00:16:39 +000066 }
67 path.clear();
68 return false;
69}
70
71Path
72Path::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 ) {
Reid Spencer07adb282004-11-05 22:15:36 +000079 if (result.setDirectory(*I) && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000080 return result;
81 }
82
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000083 // Try the LLVM lib directory in the LLVM install area
Reid Spencer07adb282004-11-05 22:15:36 +000084 if (result.setDirectory(LLVM_LIBDIR) && IsLibrary(result,basename))
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000085 return result;
86
Reid Spencer74e72612004-09-14 00:16:39 +000087 // Try /usr/lib
Reid Spencer07adb282004-11-05 22:15:36 +000088 if (result.setDirectory("/usr/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000089 return result;
90
91 // Try /lib
Reid Spencer07adb282004-11-05 22:15:36 +000092 if (result.setDirectory("/lib/") && IsLibrary(result,basename))
Reid Spencer74e72612004-09-14 00:16:39 +000093 return result;
94
95 // Can't find it, give up and return invalid path.
96 result.clear();
97 return result;
98}
99
Reid Spencer8e665952004-08-29 05:24:01 +0000100Path
101Path::GetSystemLibraryPath1() {
102 return Path("/lib/");
103}
104
105Path
106Path::GetSystemLibraryPath2() {
107 return Path("/usr/lib/");
108}
109
110Path
111Path::GetLLVMDefaultConfigDir() {
112 return Path("/etc/llvm/");
113}
114
115Path
116Path::GetLLVMConfigDir() {
117 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000118 if (result.setDirectory(LLVM_ETCDIR))
Reid Spencer8e665952004-08-29 05:24:01 +0000119 return result;
120 return GetLLVMDefaultConfigDir();
121}
122
123Path
124Path::GetUserHomeDirectory() {
125 const char* home = getenv("HOME");
126 if (home) {
127 Path result;
Reid Spencer07adb282004-11-05 22:15:36 +0000128 if (result.setDirectory(home))
Reid Spencer8e665952004-08-29 05:24:01 +0000129 return result;
130 }
131 return GetRootDirectory();
132}
133
134bool
Reid Spencer07adb282004-11-05 22:15:36 +0000135Path::isFile() const {
136 return (isValid() && path[path.length()-1] != '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000137}
138
139bool
Reid Spencer07adb282004-11-05 22:15:36 +0000140Path::isDirectory() const {
141 return (isValid() && path[path.length()-1] == '/');
Reid Spencer1b554b42004-09-11 04:55:08 +0000142}
143
144std::string
Reid Spencer07adb282004-11-05 22:15:36 +0000145Path::getBasename() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000146 // 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
Reid Spencer07adb282004-11-05 22:15:36 +0000156bool Path::hasMagicNumber(const std::string &Magic) const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000157 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
165bool
Reid Spencer07adb282004-11-05 22:15:36 +0000166Path::isBytecodeFile() const {
Reid Spencer9195f372004-11-09 20:26:31 +0000167 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);
Reid Spencer1b554b42004-09-11 04:55:08 +0000174}
175
176bool
Reid Spencer07adb282004-11-05 22:15:36 +0000177Path::isArchive() const {
Reid Spencer1b554b42004-09-11 04:55:08 +0000178 if (readable()) {
Reid Spencer07adb282004-11-05 22:15:36 +0000179 return hasMagicNumber("!<arch>\012");
Reid Spencer1b554b42004-09-11 04:55:08 +0000180 }
181 return false;
182}
183
184bool
Reid Spencer8e665952004-08-29 05:24:01 +0000185Path::exists() const {
186 return 0 == access(path.c_str(), F_OK );
187}
188
189bool
190Path::readable() const {
191 return 0 == access(path.c_str(), F_OK | R_OK );
192}
193
194bool
195Path::writable() const {
196 return 0 == access(path.c_str(), F_OK | W_OK );
197}
198
199bool
200Path::executable() const {
201 return 0 == access(path.c_str(), R_OK | X_OK );
202}
203
204std::string
205Path::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
Reid Spencer9195f372004-11-09 20:26:31 +0000226void
227Path::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
Reid Spencer8e665952004-08-29 05:24:01 +0000239bool
Reid Spencer07adb282004-11-05 22:15:36 +0000240Path::setDirectory(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000241 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 += '/';
Reid Spencer07adb282004-11-05 22:15:36 +0000248 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000249 path = save.path;
250 return false;
251 }
252 return true;
253}
254
255bool
Reid Spencer07adb282004-11-05 22:15:36 +0000256Path::setFile(const std::string& a_path) {
Reid Spencer8e665952004-08-29 05:24:01 +0000257 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);
Reid Spencer07adb282004-11-05 22:15:36 +0000265 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000266 path = save.path;
267 return false;
268 }
269 return true;
270}
271
272bool
Reid Spencer07adb282004-11-05 22:15:36 +0000273Path::appendDirectory(const std::string& dir) {
274 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000275 return false;
276 Path save(*this);
277 path += dir;
278 path += "/";
Reid Spencer07adb282004-11-05 22:15:36 +0000279 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000280 path = save.path;
281 return false;
282 }
283 return true;
284}
285
286bool
Reid Spencer07adb282004-11-05 22:15:36 +0000287Path::elideDirectory() {
288 if (isFile())
Reid Spencer8e665952004-08-29 05:24:01 +0000289 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
301bool
Reid Spencer07adb282004-11-05 22:15:36 +0000302Path::appendFile(const std::string& file) {
303 if (!isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000304 return false;
305 Path save(*this);
306 path += file;
Reid Spencer07adb282004-11-05 22:15:36 +0000307 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000308 path = save.path;
309 return false;
310 }
311 return true;
312}
313
314bool
Reid Spencer07adb282004-11-05 22:15:36 +0000315Path::elideFile() {
316 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000317 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
325bool
Reid Spencer07adb282004-11-05 22:15:36 +0000326Path::appendSuffix(const std::string& suffix) {
327 if (isDirectory())
Reid Spencer8e665952004-08-29 05:24:01 +0000328 return false;
329 Path save(*this);
330 path.append(".");
331 path.append(suffix);
Reid Spencer07adb282004-11-05 22:15:36 +0000332 if (!isValid()) {
Reid Spencer8e665952004-08-29 05:24:01 +0000333 path = save.path;
334 return false;
335 }
336 return true;
337}
338
339bool
Reid Spencer07adb282004-11-05 22:15:36 +0000340Path::elideSuffix() {
341 if (isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000342 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
353bool
Reid Spencer07adb282004-11-05 22:15:36 +0000354Path::createDirectory( bool create_parents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000355 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000356 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000357
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
390bool
Reid Spencer07adb282004-11-05 22:15:36 +0000391Path::createFile() {
Reid Spencer8e665952004-08-29 05:24:01 +0000392 // Make sure we're dealing with a file
Reid Spencer07adb282004-11-05 22:15:36 +0000393 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000394
395 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000396 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
397 if (fd < 0)
Reid Spencer9195f372004-11-09 20:26:31 +0000398 ThrowErrno(path + ": Can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000399 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000400
401 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000402}
403
Reid Spencer8e665952004-08-29 05:24:01 +0000404bool
Reid Spencer9195f372004-11-09 20:26:31 +0000405Path::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
422bool
Reid Spencer07adb282004-11-05 22:15:36 +0000423Path::destroyDirectory(bool remove_contents) {
Reid Spencer8e665952004-08-29 05:24:01 +0000424 // Make sure we're dealing with a directory
Reid Spencer07adb282004-11-05 22:15:36 +0000425 if (!isDirectory()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000426
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
448bool
Reid Spencer07adb282004-11-05 22:15:36 +0000449Path::destroyFile() {
450 if (!isFile()) return false;
Reid Spencer8e665952004-08-29 05:24:01 +0000451 if (0 != unlink(path.c_str()))
452 ThrowErrno(std::string(path.c_str()) + ": Can't destroy file");
453 return true;
454}
455
Reid Spencerb89a2232004-08-25 06:20:07 +0000456}
457
458// vim: sw=2