blob: c9333decbfd64e1c573961b47dc511ffa1822b6b [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;
33 if (this->is_valid())
34 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;
43 result.set_directory("/");
44 return result;
45}
46
Reid Spencer74e72612004-09-14 00:16:39 +000047static inline bool IsLibrary(Path& path, const std::string& basename) {
48 if (path.append_file(std::string("lib") + basename)) {
49 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
50 return true;
51 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
52 return true;
53 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
54 return true;
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000055 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
56 return true;
Reid Spencer74e72612004-09-14 00:16:39 +000057 } else if (path.elide_file() && path.append_file(basename)) {
58 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
59 return true;
60 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
61 return true;
62 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
63 return true;
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000064 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
65 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 ) {
79 if (result.set_directory(*I) && IsLibrary(result,basename))
80 return result;
81 }
82
Reid Spencer0cc2d0a2004-09-16 16:36:10 +000083 // Try the LLVM lib directory in the LLVM install area
84 if (result.set_directory(LLVM_LIBDIR) && IsLibrary(result,basename))
85 return result;
86
Reid Spencer74e72612004-09-14 00:16:39 +000087 // Try /usr/lib
88 if (result.set_directory("/usr/lib/") && IsLibrary(result,basename))
89 return result;
90
91 // Try /lib
92 if (result.set_directory("/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
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;
118 if (result.set_directory(LLVM_ETCDIR))
119 return result;
120 return GetLLVMDefaultConfigDir();
121}
122
123Path
124Path::GetUserHomeDirectory() {
125 const char* home = getenv("HOME");
126 if (home) {
127 Path result;
128 if (result.set_directory(home))
129 return result;
130 }
131 return GetRootDirectory();
132}
133
134bool
Reid Spencer1b554b42004-09-11 04:55:08 +0000135Path::is_file() const {
136 return (is_valid() && path[path.length()-1] != '/');
137}
138
139bool
140Path::is_directory() const {
141 return (is_valid() && path[path.length()-1] == '/');
142}
143
144std::string
145Path::get_basename() 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
156bool Path::has_magic_number(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
165bool
166Path::is_bytecode_file() const {
167 if (readable()) {
168 return has_magic_number("llvm");
169 }
170 return false;
171}
172
173bool
174Path::is_archive() const {
175 if (readable()) {
176 return has_magic_number("!<arch>\012");
177 }
178 return false;
179}
180
181bool
Reid Spencer8e665952004-08-29 05:24:01 +0000182Path::exists() const {
183 return 0 == access(path.c_str(), F_OK );
184}
185
186bool
187Path::readable() const {
188 return 0 == access(path.c_str(), F_OK | R_OK );
189}
190
191bool
192Path::writable() const {
193 return 0 == access(path.c_str(), F_OK | W_OK );
194}
195
196bool
197Path::executable() const {
198 return 0 == access(path.c_str(), R_OK | X_OK );
199}
200
201std::string
202Path::getLast() const {
203 // Find the last slash
204 size_t pos = path.rfind('/');
205
206 // Handle the corner cases
207 if (pos == std::string::npos)
208 return path;
209
210 // If the last character is a slash
211 if (pos == path.length()-1) {
212 // Find the second to last slash
213 size_t pos2 = path.rfind('/', pos-1);
214 if (pos2 == std::string::npos)
215 return path.substr(0,pos);
216 else
217 return path.substr(pos2+1,pos-pos2-1);
218 }
219 // Return everything after the last slash
220 return path.substr(pos+1);
221}
222
223bool
224Path::set_directory(const std::string& a_path) {
225 if (a_path.size() == 0)
226 return false;
227 Path save(*this);
228 path = a_path;
229 size_t last = a_path.size() -1;
230 if (last != 0 && a_path[last] != '/')
231 path += '/';
232 if (!is_valid()) {
233 path = save.path;
234 return false;
235 }
236 return true;
237}
238
239bool
240Path::set_file(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 while (last > 0 && a_path[last] == '/')
247 last--;
248 path.erase(last+1);
249 if (!is_valid()) {
250 path = save.path;
251 return false;
252 }
253 return true;
254}
255
256bool
257Path::append_directory(const std::string& dir) {
258 if (is_file())
259 return false;
260 Path save(*this);
261 path += dir;
262 path += "/";
263 if (!is_valid()) {
264 path = save.path;
265 return false;
266 }
267 return true;
268}
269
270bool
271Path::elide_directory() {
272 if (is_file())
273 return false;
274 size_t slashpos = path.rfind('/',path.size());
275 if (slashpos == 0 || slashpos == std::string::npos)
276 return false;
277 if (slashpos == path.size() - 1)
278 slashpos = path.rfind('/',slashpos-1);
279 if (slashpos == std::string::npos)
280 return false;
281 path.erase(slashpos);
282 return true;
283}
284
285bool
286Path::append_file(const std::string& file) {
287 if (!is_directory())
288 return false;
289 Path save(*this);
290 path += file;
291 if (!is_valid()) {
292 path = save.path;
293 return false;
294 }
295 return true;
296}
297
298bool
299Path::elide_file() {
300 if (is_directory())
301 return false;
302 size_t slashpos = path.rfind('/',path.size());
303 if (slashpos == std::string::npos)
304 return false;
305 path.erase(slashpos+1);
306 return true;
307}
308
309bool
310Path::append_suffix(const std::string& suffix) {
311 if (is_directory())
312 return false;
313 Path save(*this);
314 path.append(".");
315 path.append(suffix);
316 if (!is_valid()) {
317 path = save.path;
318 return false;
319 }
320 return true;
321}
322
323bool
324Path::elide_suffix() {
325 if (is_directory()) return false;
326 size_t dotpos = path.rfind('.',path.size());
327 size_t slashpos = path.rfind('/',path.size());
328 if (slashpos != std::string::npos && dotpos != std::string::npos &&
329 dotpos > slashpos) {
330 path.erase(dotpos, path.size()-dotpos);
331 return true;
332 }
333 return false;
334}
335
336
337bool
338Path::create_directory( bool create_parents) {
339 // Make sure we're dealing with a directory
340 if (!is_directory()) return false;
341
342 // Get a writeable copy of the path name
343 char pathname[MAXPATHLEN];
344 path.copy(pathname,MAXPATHLEN);
345
346 // Null-terminate the last component
347 int lastchar = path.length() - 1 ;
348 if (pathname[lastchar] == '/')
349 pathname[lastchar] = 0;
350
351 // If we're supposed to create intermediate directories
352 if ( create_parents ) {
353 // Find the end of the initial name component
354 char * next = strchr(pathname,'/');
355 if ( pathname[0] == '/')
356 next = strchr(&pathname[1],'/');
357
358 // Loop through the directory components until we're done
359 while ( next != 0 ) {
360 *next = 0;
361 if (0 != access(pathname, F_OK | R_OK | W_OK))
362 if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
363 ThrowErrno(std::string(pathname) + ": Can't create directory");
364 char* save = next;
365 next = strchr(pathname,'/');
366 *save = '/';
367 }
368 } else if (0 != mkdir(pathname, S_IRWXU | S_IRWXG)) {
369 ThrowErrno(std::string(pathname) + ": Can't create directory");
370 }
371 return true;
372}
373
374bool
Reid Spencerb89a2232004-08-25 06:20:07 +0000375Path::create_file() {
Reid Spencer8e665952004-08-29 05:24:01 +0000376 // Make sure we're dealing with a file
377 if (!is_file()) return false;
378
379 // Create the file
Reid Spencer622e2202004-09-18 19:25:11 +0000380 int fd = ::creat(path.c_str(), S_IRUSR | S_IWUSR);
381 if (fd < 0)
Reid Spencer8e665952004-08-29 05:24:01 +0000382 ThrowErrno(std::string(path.c_str()) + ": Can't create file");
Reid Spencer622e2202004-09-18 19:25:11 +0000383 ::close(fd);
Reid Spencer8e665952004-08-29 05:24:01 +0000384
385 return true;
Reid Spencerb89a2232004-08-25 06:20:07 +0000386}
387
Reid Spencer8e665952004-08-29 05:24:01 +0000388bool
389Path::destroy_directory(bool remove_contents) {
390 // Make sure we're dealing with a directory
391 if (!is_directory()) return false;
392
393 // If it doesn't exist, we're done.
394 if (!exists()) return true;
395
396 if (remove_contents) {
397 // Recursively descend the directory to remove its content
398 std::string cmd("/bin/rm -rf ");
399 cmd += path;
400 system(cmd.c_str());
401 } else {
402 // Otherwise, try to just remove the one directory
403 char pathname[MAXPATHLEN];
404 path.copy(pathname,MAXPATHLEN);
405 int lastchar = path.length() - 1 ;
406 if (pathname[lastchar] == '/')
407 pathname[lastchar] = 0;
408 if ( 0 != rmdir(pathname))
409 ThrowErrno(std::string(pathname) + ": Can't destroy directory");
410 }
411 return true;
412}
413
414bool
415Path::destroy_file() {
416 if (!is_file()) return false;
417 if (0 != unlink(path.c_str()))
418 ThrowErrno(std::string(path.c_str()) + ": Can't destroy file");
419 return true;
420}
421
Reid Spencerb89a2232004-08-25 06:20:07 +0000422}
423
424// vim: sw=2