Revise the design of the Path concept per peer review. Too many changes to
[oota-llvm.git] / lib / System / Unix / Path.inc
1 //===- 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
16 //===          is guaranteed to work on *all* UNIX variants.
17 //===----------------------------------------------------------------------===//
18
19 #include "Unix.h"
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <Config/config.h>
23
24 namespace llvm {
25 using namespace sys;
26
27 Path::Path(std::string unverified_path) 
28   : path(unverified_path)
29 {
30   if (unverified_path.empty())
31     return;
32   if (this->is_valid()) 
33     return;
34   // oops, not valid.
35   path.clear();
36   ThrowErrno(unverified_path + ": path is not valid");
37 }
38
39 Path
40 Path::GetRootDirectory() {
41   Path result;
42   result.set_directory("/");
43   return result;
44 }
45
46 Path
47 Path::GetTemporaryDirectory() {
48   char pathname[MAXPATHLEN];
49   strcpy(pathname,"/tmp/llvm_XXXXXX");
50   if (0 == mkdtemp(pathname))
51     ThrowErrno(std::string(pathname) + ": Can't create temporary directory");
52   Path result;
53   result.set_directory(pathname);
54   assert(result.is_valid() && "mkdtemp didn't create a valid pathname!");
55   return result;
56 }
57
58 Path 
59 Path::GetSystemLibraryPath1() {
60   return Path("/lib/");
61 }
62
63 Path 
64 Path::GetSystemLibraryPath2() {
65   return Path("/usr/lib/");
66 }
67
68 Path 
69 Path::GetLLVMDefaultConfigDir() {
70   return Path("/etc/llvm/");
71 }
72
73 Path 
74 Path::GetLLVMConfigDir() {
75   Path result;
76   if (result.set_directory(LLVM_ETCDIR))
77     return result;
78   return GetLLVMDefaultConfigDir();
79 }
80
81 Path
82 Path::GetUserHomeDirectory() {
83   const char* home = getenv("HOME");
84   if (home) {
85     Path result;
86     if (result.set_directory(home))
87       return result;
88   }
89   return GetRootDirectory();
90 }
91
92 bool
93 Path::exists() const {
94   return 0 == access(path.c_str(), F_OK );
95 }
96
97 bool
98 Path::readable() const {
99   return 0 == access(path.c_str(), F_OK | R_OK );
100 }
101
102 bool
103 Path::writable() const {
104   return 0 == access(path.c_str(), F_OK | W_OK );
105 }
106
107 bool
108 Path::executable() const {
109   return 0 == access(path.c_str(), R_OK | X_OK );
110 }
111
112 std::string 
113 Path::getLast() const {
114   // Find the last slash
115   size_t pos = path.rfind('/');
116
117   // Handle the corner cases
118   if (pos == std::string::npos)
119     return path;
120
121   // If the last character is a slash
122   if (pos == path.length()-1) {
123     // Find the second to last slash
124     size_t pos2 = path.rfind('/', pos-1);
125     if (pos2 == std::string::npos)
126       return path.substr(0,pos);
127     else
128       return path.substr(pos2+1,pos-pos2-1);
129   }
130   // Return everything after the last slash
131   return path.substr(pos+1);
132 }
133
134 bool
135 Path::set_directory(const std::string& a_path) {
136   if (a_path.size() == 0)
137     return false;
138   Path save(*this);
139   path = a_path;
140   size_t last = a_path.size() -1;
141   if (last != 0 && a_path[last] != '/')
142     path += '/';
143   if (!is_valid()) {
144     path = save.path;
145     return false;
146   }
147   return true;
148 }
149
150 bool
151 Path::set_file(const std::string& a_path) {
152   if (a_path.size() == 0)
153     return false;
154   Path save(*this);
155   path = a_path;
156   size_t last = a_path.size() - 1;
157   while (last > 0 && a_path[last] == '/')
158     last--;
159   path.erase(last+1);
160   if (!is_valid()) {
161     path = save.path;
162     return false;
163   }
164   return true;
165 }
166
167 bool
168 Path::append_directory(const std::string& dir) {
169   if (is_file()) 
170     return false;
171   Path save(*this);
172   path += dir;
173   path += "/";
174   if (!is_valid()) {
175     path = save.path;
176     return false;
177   }
178   return true;
179 }
180
181 bool
182 Path::elide_directory() {
183   if (is_file()) 
184     return false;
185   size_t slashpos = path.rfind('/',path.size());
186   if (slashpos == 0 || slashpos == std::string::npos)
187     return false;
188   if (slashpos == path.size() - 1)
189     slashpos = path.rfind('/',slashpos-1);
190   if (slashpos == std::string::npos)
191     return false;
192   path.erase(slashpos);
193   return true;
194 }
195
196 bool
197 Path::append_file(const std::string& file) {
198   if (!is_directory()) 
199     return false;
200   Path save(*this);
201   path += file;
202   if (!is_valid()) {
203     path = save.path;
204     return false;
205   }
206   return true;
207 }
208
209 bool
210 Path::elide_file() {
211   if (is_directory()) 
212     return false;
213   size_t slashpos = path.rfind('/',path.size());
214   if (slashpos == std::string::npos)
215     return false;
216   path.erase(slashpos+1);
217   return true;
218 }
219
220 bool
221 Path::append_suffix(const std::string& suffix) {
222   if (is_directory()) 
223     return false;
224   Path save(*this);
225   path.append(".");
226   path.append(suffix);
227   if (!is_valid()) {
228     path = save.path;
229     return false;
230   }
231   return true;
232 }
233
234 bool 
235 Path::elide_suffix() {
236   if (is_directory()) return false;
237   size_t dotpos = path.rfind('.',path.size());
238   size_t slashpos = path.rfind('/',path.size());
239   if (slashpos != std::string::npos && dotpos != std::string::npos &&
240       dotpos > slashpos) {
241     path.erase(dotpos, path.size()-dotpos);
242     return true;
243   }
244   return false;
245 }
246
247
248 bool
249 Path::create_directory( bool create_parents) {
250   // Make sure we're dealing with a directory
251   if (!is_directory()) return false;
252
253   // Get a writeable copy of the path name
254   char pathname[MAXPATHLEN];
255   path.copy(pathname,MAXPATHLEN);
256
257   // Null-terminate the last component
258   int lastchar = path.length() - 1 ; 
259   if (pathname[lastchar] == '/') 
260     pathname[lastchar] = 0;
261
262   // If we're supposed to create intermediate directories
263   if ( create_parents ) {
264     // Find the end of the initial name component
265     char * next = strchr(pathname,'/');
266     if ( pathname[0] == '/') 
267       next = strchr(&pathname[1],'/');
268
269     // Loop through the directory components until we're done 
270     while ( next != 0 ) {
271       *next = 0;
272       if (0 != access(pathname, F_OK | R_OK | W_OK))
273         if (0 != mkdir(pathname, S_IRWXU | S_IRWXG))
274           ThrowErrno(std::string(pathname) + ": Can't create directory");
275       char* save = next;
276       next = strchr(pathname,'/');
277       *save = '/';
278     }
279   } else if (0 != mkdir(pathname, S_IRWXU | S_IRWXG)) {
280     ThrowErrno(std::string(pathname) + ": Can't create directory");
281   } 
282   return true;
283 }
284
285 bool
286 Path::create_file() {
287   // Make sure we're dealing with a file
288   if (!is_file()) return false; 
289
290   // Create the file
291   if (0 != creat(path.c_str(), S_IRUSR | S_IWUSR))
292     ThrowErrno(std::string(path.c_str()) + ": Can't create file");
293
294   return true;
295 }
296
297 bool
298 Path::destroy_directory(bool remove_contents) {
299   // Make sure we're dealing with a directory
300   if (!is_directory()) return false;
301
302   // If it doesn't exist, we're done.
303   if (!exists()) return true;
304
305   if (remove_contents) {
306     // Recursively descend the directory to remove its content
307     std::string cmd("/bin/rm -rf ");
308     cmd += path;
309     system(cmd.c_str());
310   } else {
311     // Otherwise, try to just remove the one directory
312     char pathname[MAXPATHLEN];
313     path.copy(pathname,MAXPATHLEN);
314     int lastchar = path.length() - 1 ; 
315     if (pathname[lastchar] == '/') 
316       pathname[lastchar] = 0;
317     if ( 0 != rmdir(pathname))
318       ThrowErrno(std::string(pathname) + ": Can't destroy directory");
319   }
320   return true;
321 }
322
323 bool
324 Path::destroy_file() {
325   if (!is_file()) return false;
326   if (0 != unlink(path.c_str()))
327     ThrowErrno(std::string(path.c_str()) + ": Can't destroy file");
328   return true;
329 }
330
331 }
332
333 // vim: sw=2