blob: b34987e4802256fa55726d43e7760c967da2117f [file] [log] [blame]
Reid Spencerb016a372004-09-15 05:49:50 +00001//===- llvm/System/Linux/Path.cpp - Linux Path Implementation ---*- C++ -*-===//
2//
Reid Spencercbad7012004-09-11 04:59:30 +00003// The LLVM Compiler Infrastructure
4//
Reid Spencerb016a372004-09-15 05:49:50 +00005// This file was developed by Reid Spencer and is distributed under the
Reid Spencercbad7012004-09-11 04:59:30 +00006// University of Illinois Open Source License. See LICENSE.TXT for details.
Reid Spencerb016a372004-09-15 05:49:50 +00007//
8// Modified by Henrik Bach to comply with at least MinGW.
Reid Spencerd0c9e0e2004-09-18 19:29:16 +00009// Ported to Win32 by Jeff Cohen.
Reid Spencerb016a372004-09-15 05:49:50 +000010//
Reid Spencercbad7012004-09-11 04:59:30 +000011//===----------------------------------------------------------------------===//
12//
13// This file provides the Win32 specific implementation of the Path class.
14//
15//===----------------------------------------------------------------------===//
16
17//===----------------------------------------------------------------------===//
Reid Spencerb016a372004-09-15 05:49:50 +000018//=== WARNING: Implementation here must contain only generic Win32 code that
19//=== is guaranteed to work on *all* Win32 variants.
Reid Spencercbad7012004-09-11 04:59:30 +000020//===----------------------------------------------------------------------===//
21
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000022#include "Win32.h"
Reid Spencerb016a372004-09-15 05:49:50 +000023#include <llvm/System/Path.h>
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000024#include <fstream>
25#include <malloc.h>
Reid Spencercbad7012004-09-11 04:59:30 +000026
27namespace llvm {
Reid Spencerb016a372004-09-15 05:49:50 +000028namespace sys {
Reid Spencercbad7012004-09-11 04:59:30 +000029
Reid Spencerb016a372004-09-15 05:49:50 +000030bool
31Path::is_valid() const {
32 if (path.empty())
33 return false;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000034
35 // On Unix, the realpath function is used, which only requires that the
36 // directories leading up the to final file name are valid. The file itself
37 // need not exist. To get this behavior on Windows, we must elide the file
38 // name manually.
39 Path dir(*this);
40 dir.elide_file();
41 if (dir.path.empty())
42 return true;
43
44 DWORD attr = GetFileAttributes(dir.path.c_str());
45 return (attr != INVALID_FILE_ATTRIBUTES) && (attr & FILE_ATTRIBUTE_DIRECTORY);
Reid Spencercbad7012004-09-11 04:59:30 +000046}
47
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000048static Path *TempDirectory = NULL;
49
Reid Spencerb016a372004-09-15 05:49:50 +000050Path
51Path::GetTemporaryDirectory() {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000052 if (TempDirectory)
53 return *TempDirectory;
54
55 char pathname[MAX_PATH];
56 if (!GetTempPath(MAX_PATH, pathname))
57 ThrowError("Can't determine temporary directory");
58
Reid Spencerb016a372004-09-15 05:49:50 +000059 Path result;
60 result.set_directory(pathname);
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000061
62 // Append a subdirectory passed on our process id so multiple LLVMs don't
63 // step on each other's toes.
64 sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
65 result.append_directory(pathname);
66
67 // If there's a directory left over from a previous LLVM execution that
68 // happened to have the same process id, get rid of it.
69 result.destroy_directory(true);
70
71 // And finally (re-)create the empty directory.
72 result.create_directory(false);
73 TempDirectory = new Path(result);
74 return *TempDirectory;
Reid Spencerb016a372004-09-15 05:49:50 +000075}
76
77Path::Path(std::string unverified_path)
78 : path(unverified_path)
79{
80 if (unverified_path.empty())
81 return;
82 if (this->is_valid())
83 return;
84 // oops, not valid.
85 path.clear();
86 ThrowError(unverified_path + ": path is not valid");
87}
88
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000089// FIXME: the following set of functions don't map to Windows very well.
Reid Spencerb016a372004-09-15 05:49:50 +000090Path
91Path::GetRootDirectory() {
92 Path result;
93 result.set_directory("/");
94 return result;
95}
96
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000097static inline bool IsLibrary(Path& path, const std::string& basename) {
98 if (path.append_file(std::string("lib") + basename)) {
99 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
100 return true;
101 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
102 return true;
103 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
104 return true;
105 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
106 return true;
107 } else if (path.elide_file() && path.append_file(basename)) {
108 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
109 return true;
110 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
111 return true;
112 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
113 return true;
114 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
115 return true;
116 }
117 path.clear();
118 return false;
119}
120
121Path
122Path::GetLibraryPath(const std::string& basename,
123 const std::vector<std::string>& LibPaths) {
124 Path result;
125
126 // Try the paths provided
127 for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
128 E = LibPaths.end(); I != E; ++I ) {
129 if (result.set_directory(*I) && IsLibrary(result,basename))
130 return result;
131 }
132
133 // Try the LLVM lib directory in the LLVM install area
134 //if (result.set_directory(LLVM_LIBDIR) && IsLibrary(result,basename))
135 // return result;
136
137 // Try /usr/lib
138 if (result.set_directory("/usr/lib/") && IsLibrary(result,basename))
139 return result;
140
141 // Try /lib
142 if (result.set_directory("/lib/") && IsLibrary(result,basename))
143 return result;
144
145 // Can't find it, give up and return invalid path.
146 result.clear();
147 return result;
148}
149
Reid Spencerb016a372004-09-15 05:49:50 +0000150Path
151Path::GetSystemLibraryPath1() {
152 return Path("/lib/");
153}
154
155Path
156Path::GetSystemLibraryPath2() {
157 return Path("/usr/lib/");
158}
159
160Path
161Path::GetLLVMDefaultConfigDir() {
162 return Path("/etc/llvm/");
163}
164
165Path
166Path::GetLLVMConfigDir() {
Reid Spencerb016a372004-09-15 05:49:50 +0000167 return GetLLVMDefaultConfigDir();
168}
169
170Path
171Path::GetUserHomeDirectory() {
172 const char* home = getenv("HOME");
173 if (home) {
174 Path result;
175 if (result.set_directory(home))
176 return result;
177 }
178 return GetRootDirectory();
179}
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000180// FIXME: the above set of functions don't map to Windows very well.
181
182bool
183Path::is_file() const {
184 return (is_valid() && path[path.length()-1] != '/');
185}
186
187bool
188Path::is_directory() const {
189 return (is_valid() && path[path.length()-1] == '/');
190}
191
192std::string
193Path::get_basename() const {
194 // Find the last slash
195 size_t slash = path.rfind('/');
196 if (slash == std::string::npos)
197 slash = 0;
198 else
199 slash++;
200
201 return path.substr(slash, path.rfind('.'));
202}
203
204bool Path::has_magic_number(const std::string &Magic) const {
205 size_t len = Magic.size();
206 char *buf = reinterpret_cast<char *>(_alloca(len+1));
207 std::ifstream f(path.c_str());
208 f.read(buf, len);
209 buf[len] = '\0';
210 return Magic == buf;
211}
212
213bool
214Path::is_bytecode_file() const {
215 if (readable()) {
216 return has_magic_number("llvm");
217 }
218 return false;
219}
220
221bool
222Path::is_archive() const {
223 if (readable()) {
224 return has_magic_number("!<arch>\012");
225 }
226 return false;
227}
Reid Spencerb016a372004-09-15 05:49:50 +0000228
229bool
230Path::exists() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000231 DWORD attr = GetFileAttributes(path.c_str());
232 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000233}
234
235bool
236Path::readable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000237 // FIXME: take security attributes into account.
238 DWORD attr = GetFileAttributes(path.c_str());
239 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000240}
241
242bool
243Path::writable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000244 // FIXME: take security attributes into account.
245 DWORD attr = GetFileAttributes(path.c_str());
246 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
Reid Spencerb016a372004-09-15 05:49:50 +0000247}
248
249bool
250Path::executable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000251 // FIXME: take security attributes into account.
252 DWORD attr = GetFileAttributes(path.c_str());
253 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000254}
255
256std::string
257Path::getLast() const {
258 // Find the last slash
259 size_t pos = path.rfind('/');
260
261 // Handle the corner cases
262 if (pos == std::string::npos)
263 return path;
264
265 // If the last character is a slash
266 if (pos == path.length()-1) {
267 // Find the second to last slash
268 size_t pos2 = path.rfind('/', pos-1);
269 if (pos2 == std::string::npos)
270 return path.substr(0,pos);
271 else
272 return path.substr(pos2+1,pos-pos2-1);
273 }
274 // Return everything after the last slash
275 return path.substr(pos+1);
276}
277
278bool
279Path::set_directory(const std::string& a_path) {
280 if (a_path.size() == 0)
281 return false;
282 Path save(*this);
283 path = a_path;
284 size_t last = a_path.size() -1;
285 if (last != 0 && a_path[last] != '/')
286 path += '/';
287 if (!is_valid()) {
288 path = save.path;
289 return false;
290 }
291 return true;
292}
293
294bool
295Path::set_file(const std::string& a_path) {
296 if (a_path.size() == 0)
297 return false;
298 Path save(*this);
299 path = a_path;
300 size_t last = a_path.size() - 1;
301 while (last > 0 && a_path[last] == '/')
302 last--;
303 path.erase(last+1);
304 if (!is_valid()) {
305 path = save.path;
306 return false;
307 }
308 return true;
309}
310
311bool
312Path::append_directory(const std::string& dir) {
313 if (is_file())
314 return false;
315 Path save(*this);
316 path += dir;
317 path += "/";
318 if (!is_valid()) {
319 path = save.path;
320 return false;
321 }
322 return true;
323}
324
325bool
326Path::elide_directory() {
327 if (is_file())
328 return false;
329 size_t slashpos = path.rfind('/',path.size());
330 if (slashpos == 0 || slashpos == std::string::npos)
331 return false;
332 if (slashpos == path.size() - 1)
333 slashpos = path.rfind('/',slashpos-1);
334 if (slashpos == std::string::npos)
335 return false;
336 path.erase(slashpos);
337 return true;
338}
339
340bool
341Path::append_file(const std::string& file) {
342 if (!is_directory())
343 return false;
344 Path save(*this);
345 path += file;
346 if (!is_valid()) {
347 path = save.path;
348 return false;
349 }
350 return true;
351}
352
353bool
354Path::elide_file() {
355 if (is_directory())
356 return false;
357 size_t slashpos = path.rfind('/',path.size());
358 if (slashpos == std::string::npos)
359 return false;
360 path.erase(slashpos+1);
361 return true;
362}
363
364bool
365Path::append_suffix(const std::string& suffix) {
366 if (is_directory())
367 return false;
368 Path save(*this);
369 path.append(".");
370 path.append(suffix);
371 if (!is_valid()) {
372 path = save.path;
373 return false;
374 }
375 return true;
376}
377
378bool
379Path::elide_suffix() {
380 if (is_directory()) return false;
381 size_t dotpos = path.rfind('.',path.size());
382 size_t slashpos = path.rfind('/',path.size());
383 if (slashpos != std::string::npos && dotpos != std::string::npos &&
384 dotpos > slashpos) {
385 path.erase(dotpos, path.size()-dotpos);
386 return true;
387 }
388 return false;
389}
390
391
392bool
393Path::create_directory( bool create_parents) {
394 // Make sure we're dealing with a directory
395 if (!is_directory()) return false;
396
397 // Get a writeable copy of the path name
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000398 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
399 path.copy(pathname,path.length()+1);
Reid Spencerb016a372004-09-15 05:49:50 +0000400
401 // Null-terminate the last component
402 int lastchar = path.length() - 1 ;
403 if (pathname[lastchar] == '/')
404 pathname[lastchar] = 0;
405
406 // If we're supposed to create intermediate directories
407 if ( create_parents ) {
408 // Find the end of the initial name component
409 char * next = strchr(pathname,'/');
410 if ( pathname[0] == '/')
411 next = strchr(&pathname[1],'/');
412
413 // Loop through the directory components until we're done
414 while ( next != 0 ) {
415 *next = 0;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000416 if (!CreateDirectory(pathname, NULL))
417 ThrowError(std::string(pathname) + ": Can't create directory: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000418 char* save = next;
419 next = strchr(pathname,'/');
420 *save = '/';
421 }
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000422 } else if (!CreateDirectory(pathname, NULL)) {
423 ThrowError(std::string(pathname) + ": Can't create directory: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000424 }
425 return true;
426}
427
428bool
429Path::create_file() {
430 // Make sure we're dealing with a file
431 if (!is_file()) return false;
432
433 // Create the file
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000434 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
435 FILE_ATTRIBUTE_NORMAL, NULL);
436 if (h == INVALID_HANDLE_VALUE)
437 ThrowError(std::string(path.c_str()) + ": Can't create file: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000438
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000439 CloseHandle(h);
Reid Spencerb016a372004-09-15 05:49:50 +0000440 return true;
441}
442
443bool
444Path::destroy_directory(bool remove_contents) {
445 // Make sure we're dealing with a directory
446 if (!is_directory()) return false;
447
448 // If it doesn't exist, we're done.
449 if (!exists()) return true;
450
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000451 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
452 path.copy(pathname,path.length()+1);
453 int lastchar = path.length() - 1 ;
454 if (pathname[lastchar] == '/')
455 pathname[lastchar] = 0;
456
Reid Spencerb016a372004-09-15 05:49:50 +0000457 if (remove_contents) {
458 // Recursively descend the directory to remove its content
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000459 // FIXME: The correct way of doing this on Windows isn't pretty...
460 // but this may work if unix-like utils are present.
461 std::string cmd("rm -rf ");
Reid Spencerb016a372004-09-15 05:49:50 +0000462 cmd += path;
463 system(cmd.c_str());
464 } else {
465 // Otherwise, try to just remove the one directory
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000466 if (!RemoveDirectory(pathname))
467 ThrowError(std::string(pathname) + ": Can't destroy directory: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000468 }
469 return true;
470}
471
472bool
473Path::destroy_file() {
474 if (!is_file()) return false;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000475
476 DWORD attr = GetFileAttributes(path.c_str());
477
478 // If it doesn't exist, we're done.
479 if (attr == INVALID_FILE_ATTRIBUTES)
480 return true;
481
482 // Read-only files cannot be deleted on Windows. Must remove the read-only
483 // attribute first.
484 if (attr & FILE_ATTRIBUTE_READONLY) {
485 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
486 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
487 }
488
489 if (!DeleteFile(path.c_str()))
490 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000491 return true;
492}
493
494}
Reid Spencercbad7012004-09-11 04:59:30 +0000495}
496
497// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
Reid Spencerb016a372004-09-15 05:49:50 +0000498