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