blob: 17d722beedf481a8105749d896a7b4b4fadafb9e [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
Reid Spencer6a0ec6f2004-09-29 00:01:17 +000027static void FlipBackSlashes(std::string& s) {
28 for (size_t i = 0; i < s.size(); i++)
29 if (s[i] == '\\')
30 s[i] = '/';
31}
32
Reid Spencercbad7012004-09-11 04:59:30 +000033namespace llvm {
Reid Spencerb016a372004-09-15 05:49:50 +000034namespace sys {
Reid Spencercbad7012004-09-11 04:59:30 +000035
Reid Spencerb016a372004-09-15 05:49:50 +000036bool
37Path::is_valid() const {
38 if (path.empty())
39 return false;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000040
Reid Spencer6a0ec6f2004-09-29 00:01:17 +000041 // If there is a colon, it must be the second character, preceded by a letter
42 // and followed by something.
43 size_t len = path.size();
44 size_t pos = path.rfind(':',len);
45 if (pos != std::string::npos) {
46 if (pos != 1 || !isalpha(path[0]) || len < 3)
47 return false;
48 }
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000049
Reid Spencer6a0ec6f2004-09-29 00:01:17 +000050 // Check for illegal characters.
51 if (path.find_first_of("\\<>\"|\001\002\003\004\005\006\007\010\011\012"
52 "\013\014\015\016\017\020\021\022\023\024\025\026"
53 "\027\030\031\032\033\034\035\036\037")
54 != std::string::npos)
55 return false;
56
57 // A file or directory name may not end in a period.
58 if (path[len-1] == '.')
59 return false;
60 if (len >= 2 && path[len-2] == '.' && path[len-1] == '/')
61 return false;
62
63 // A file or directory name may not end in a space.
64 if (path[len-1] == ' ')
65 return false;
66 if (len >= 2 && path[len-2] == ' ' && path[len-1] == '/')
67 return false;
68
69 return true;
Reid Spencercbad7012004-09-11 04:59:30 +000070}
71
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000072static Path *TempDirectory = NULL;
73
Reid Spencerb016a372004-09-15 05:49:50 +000074Path
75Path::GetTemporaryDirectory() {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000076 if (TempDirectory)
77 return *TempDirectory;
78
79 char pathname[MAX_PATH];
80 if (!GetTempPath(MAX_PATH, pathname))
Reid Spencer6a0ec6f2004-09-29 00:01:17 +000081 throw std::string("Can't determine temporary directory");
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000082
Reid Spencerb016a372004-09-15 05:49:50 +000083 Path result;
84 result.set_directory(pathname);
Reid Spencerd0c9e0e2004-09-18 19:29:16 +000085
86 // Append a subdirectory passed on our process id so multiple LLVMs don't
87 // step on each other's toes.
88 sprintf(pathname, "LLVM_%u", GetCurrentProcessId());
89 result.append_directory(pathname);
90
91 // If there's a directory left over from a previous LLVM execution that
92 // happened to have the same process id, get rid of it.
93 result.destroy_directory(true);
94
95 // And finally (re-)create the empty directory.
96 result.create_directory(false);
97 TempDirectory = new Path(result);
98 return *TempDirectory;
Reid Spencerb016a372004-09-15 05:49:50 +000099}
100
101Path::Path(std::string unverified_path)
102 : path(unverified_path)
103{
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000104 FlipBackSlashes(path);
Reid Spencerb016a372004-09-15 05:49:50 +0000105 if (unverified_path.empty())
106 return;
107 if (this->is_valid())
108 return;
109 // oops, not valid.
110 path.clear();
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000111 throw std::string(unverified_path + ": path is not valid");
Reid Spencerb016a372004-09-15 05:49:50 +0000112}
113
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000114// FIXME: the following set of functions don't map to Windows very well.
Reid Spencerb016a372004-09-15 05:49:50 +0000115Path
116Path::GetRootDirectory() {
117 Path result;
118 result.set_directory("/");
119 return result;
120}
121
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000122std::string
123Path::GetDLLSuffix() {
124 return "dll";
125}
126
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000127static inline bool IsLibrary(Path& path, const std::string& basename) {
128 if (path.append_file(std::string("lib") + basename)) {
129 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
130 return true;
131 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
132 return true;
133 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
134 return true;
135 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
136 return true;
137 } else if (path.elide_file() && path.append_file(basename)) {
138 if (path.append_suffix(Path::GetDLLSuffix()) && path.readable())
139 return true;
140 else if (path.elide_suffix() && path.append_suffix("a") && path.readable())
141 return true;
142 else if (path.elide_suffix() && path.append_suffix("o") && path.readable())
143 return true;
144 else if (path.elide_suffix() && path.append_suffix("bc") && path.readable())
145 return true;
146 }
147 path.clear();
148 return false;
149}
150
151Path
152Path::GetLibraryPath(const std::string& basename,
153 const std::vector<std::string>& LibPaths) {
154 Path result;
155
156 // Try the paths provided
157 for (std::vector<std::string>::const_iterator I = LibPaths.begin(),
158 E = LibPaths.end(); I != E; ++I ) {
159 if (result.set_directory(*I) && IsLibrary(result,basename))
160 return result;
161 }
162
163 // Try the LLVM lib directory in the LLVM install area
164 //if (result.set_directory(LLVM_LIBDIR) && IsLibrary(result,basename))
165 // return result;
166
167 // Try /usr/lib
168 if (result.set_directory("/usr/lib/") && IsLibrary(result,basename))
169 return result;
170
171 // Try /lib
172 if (result.set_directory("/lib/") && IsLibrary(result,basename))
173 return result;
174
175 // Can't find it, give up and return invalid path.
176 result.clear();
177 return result;
178}
179
Reid Spencerb016a372004-09-15 05:49:50 +0000180Path
181Path::GetSystemLibraryPath1() {
182 return Path("/lib/");
183}
184
185Path
186Path::GetSystemLibraryPath2() {
187 return Path("/usr/lib/");
188}
189
190Path
191Path::GetLLVMDefaultConfigDir() {
192 return Path("/etc/llvm/");
193}
194
195Path
196Path::GetLLVMConfigDir() {
Reid Spencerb016a372004-09-15 05:49:50 +0000197 return GetLLVMDefaultConfigDir();
198}
199
200Path
201Path::GetUserHomeDirectory() {
202 const char* home = getenv("HOME");
203 if (home) {
204 Path result;
205 if (result.set_directory(home))
206 return result;
207 }
208 return GetRootDirectory();
209}
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000210// FIXME: the above set of functions don't map to Windows very well.
211
212bool
213Path::is_file() const {
214 return (is_valid() && path[path.length()-1] != '/');
215}
216
217bool
218Path::is_directory() const {
219 return (is_valid() && path[path.length()-1] == '/');
220}
221
222std::string
223Path::get_basename() const {
224 // Find the last slash
225 size_t slash = path.rfind('/');
226 if (slash == std::string::npos)
227 slash = 0;
228 else
229 slash++;
230
231 return path.substr(slash, path.rfind('.'));
232}
233
234bool Path::has_magic_number(const std::string &Magic) const {
235 size_t len = Magic.size();
236 char *buf = reinterpret_cast<char *>(_alloca(len+1));
237 std::ifstream f(path.c_str());
238 f.read(buf, len);
239 buf[len] = '\0';
240 return Magic == buf;
241}
242
243bool
244Path::is_bytecode_file() const {
245 if (readable()) {
246 return has_magic_number("llvm");
247 }
248 return false;
249}
250
251bool
252Path::is_archive() const {
253 if (readable()) {
254 return has_magic_number("!<arch>\012");
255 }
256 return false;
257}
Reid Spencerb016a372004-09-15 05:49:50 +0000258
259bool
260Path::exists() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000261 DWORD attr = GetFileAttributes(path.c_str());
262 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000263}
264
265bool
266Path::readable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000267 // FIXME: take security attributes into account.
268 DWORD attr = GetFileAttributes(path.c_str());
269 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000270}
271
272bool
273Path::writable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000274 // FIXME: take security attributes into account.
275 DWORD attr = GetFileAttributes(path.c_str());
276 return (attr != INVALID_FILE_ATTRIBUTES) && !(attr & FILE_ATTRIBUTE_READONLY);
Reid Spencerb016a372004-09-15 05:49:50 +0000277}
278
279bool
280Path::executable() const {
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000281 // FIXME: take security attributes into account.
282 DWORD attr = GetFileAttributes(path.c_str());
283 return attr != INVALID_FILE_ATTRIBUTES;
Reid Spencerb016a372004-09-15 05:49:50 +0000284}
285
286std::string
287Path::getLast() const {
288 // Find the last slash
289 size_t pos = path.rfind('/');
290
291 // Handle the corner cases
292 if (pos == std::string::npos)
293 return path;
294
295 // If the last character is a slash
296 if (pos == path.length()-1) {
297 // Find the second to last slash
298 size_t pos2 = path.rfind('/', pos-1);
299 if (pos2 == std::string::npos)
300 return path.substr(0,pos);
301 else
302 return path.substr(pos2+1,pos-pos2-1);
303 }
304 // Return everything after the last slash
305 return path.substr(pos+1);
306}
307
308bool
309Path::set_directory(const std::string& a_path) {
310 if (a_path.size() == 0)
311 return false;
312 Path save(*this);
313 path = a_path;
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000314 FlipBackSlashes(path);
Reid Spencerb016a372004-09-15 05:49:50 +0000315 size_t last = a_path.size() -1;
316 if (last != 0 && a_path[last] != '/')
317 path += '/';
318 if (!is_valid()) {
319 path = save.path;
320 return false;
321 }
322 return true;
323}
324
325bool
326Path::set_file(const std::string& a_path) {
327 if (a_path.size() == 0)
328 return false;
329 Path save(*this);
330 path = a_path;
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000331 FlipBackSlashes(path);
Reid Spencerb016a372004-09-15 05:49:50 +0000332 size_t last = a_path.size() - 1;
333 while (last > 0 && a_path[last] == '/')
334 last--;
335 path.erase(last+1);
336 if (!is_valid()) {
337 path = save.path;
338 return false;
339 }
340 return true;
341}
342
343bool
344Path::append_directory(const std::string& dir) {
345 if (is_file())
346 return false;
347 Path save(*this);
348 path += dir;
349 path += "/";
350 if (!is_valid()) {
351 path = save.path;
352 return false;
353 }
354 return true;
355}
356
357bool
358Path::elide_directory() {
359 if (is_file())
360 return false;
361 size_t slashpos = path.rfind('/',path.size());
362 if (slashpos == 0 || slashpos == std::string::npos)
363 return false;
364 if (slashpos == path.size() - 1)
365 slashpos = path.rfind('/',slashpos-1);
366 if (slashpos == std::string::npos)
367 return false;
368 path.erase(slashpos);
369 return true;
370}
371
372bool
373Path::append_file(const std::string& file) {
374 if (!is_directory())
375 return false;
376 Path save(*this);
377 path += file;
378 if (!is_valid()) {
379 path = save.path;
380 return false;
381 }
382 return true;
383}
384
385bool
386Path::elide_file() {
387 if (is_directory())
388 return false;
389 size_t slashpos = path.rfind('/',path.size());
390 if (slashpos == std::string::npos)
391 return false;
392 path.erase(slashpos+1);
393 return true;
394}
395
396bool
397Path::append_suffix(const std::string& suffix) {
398 if (is_directory())
399 return false;
400 Path save(*this);
401 path.append(".");
402 path.append(suffix);
403 if (!is_valid()) {
404 path = save.path;
405 return false;
406 }
407 return true;
408}
409
410bool
411Path::elide_suffix() {
412 if (is_directory()) return false;
413 size_t dotpos = path.rfind('.',path.size());
414 size_t slashpos = path.rfind('/',path.size());
415 if (slashpos != std::string::npos && dotpos != std::string::npos &&
416 dotpos > slashpos) {
417 path.erase(dotpos, path.size()-dotpos);
418 return true;
419 }
420 return false;
421}
422
423
424bool
425Path::create_directory( bool create_parents) {
426 // Make sure we're dealing with a directory
427 if (!is_directory()) return false;
428
429 // Get a writeable copy of the path name
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000430 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000431 path.copy(pathname,path.length());
432 pathname[path.length()] = 0;
Reid Spencerb016a372004-09-15 05:49:50 +0000433
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000434 // Determine starting point for initial / search.
435 char *next = pathname;
436 if (pathname[0] == '/' && pathname[1] == '/') {
437 // Skip host name.
438 next = strchr(pathname+2, '/');
439 if (next == NULL)
440 throw std::string(pathname) + ": badly formed remote directory";
441 // Skip share name.
442 next = strchr(next+1, '/');
443 if (next == NULL)
444 throw std::string(pathname) + ": badly formed remote directory";
445 next++;
446 if (*next == 0)
447 throw std::string(pathname) + ": badly formed remote directory";
448 } else {
449 if (pathname[1] == ':')
450 next += 2; // skip drive letter
451 if (*next == '/')
452 next++; // skip root directory
453 }
Reid Spencerb016a372004-09-15 05:49:50 +0000454
455 // If we're supposed to create intermediate directories
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000456 if (create_parents) {
Reid Spencerb016a372004-09-15 05:49:50 +0000457 // Loop through the directory components until we're done
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000458 while (*next) {
459 next = strchr(next, '/');
Reid Spencerb016a372004-09-15 05:49:50 +0000460 *next = 0;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000461 if (!CreateDirectory(pathname, NULL))
462 ThrowError(std::string(pathname) + ": Can't create directory: ");
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000463 *next++ = '/';
Reid Spencerb016a372004-09-15 05:49:50 +0000464 }
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000465 } else {
466 // Drop trailing slash.
467 pathname[path.size()-1] = 0;
468 if (!CreateDirectory(pathname, NULL)) {
469 ThrowError(std::string(pathname) + ": Can't create directory: ");
470 }
Reid Spencerb016a372004-09-15 05:49:50 +0000471 }
472 return true;
473}
474
475bool
476Path::create_file() {
477 // Make sure we're dealing with a file
478 if (!is_file()) return false;
479
480 // Create the file
Reid Spencer6a0ec6f2004-09-29 00:01:17 +0000481 HANDLE h = CreateFile(path.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW,
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000482 FILE_ATTRIBUTE_NORMAL, NULL);
483 if (h == INVALID_HANDLE_VALUE)
484 ThrowError(std::string(path.c_str()) + ": Can't create file: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000485
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000486 CloseHandle(h);
Reid Spencerb016a372004-09-15 05:49:50 +0000487 return true;
488}
489
490bool
491Path::destroy_directory(bool remove_contents) {
492 // Make sure we're dealing with a directory
493 if (!is_directory()) return false;
494
495 // If it doesn't exist, we're done.
496 if (!exists()) return true;
497
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000498 char *pathname = reinterpret_cast<char *>(_alloca(path.length()+1));
499 path.copy(pathname,path.length()+1);
500 int lastchar = path.length() - 1 ;
501 if (pathname[lastchar] == '/')
502 pathname[lastchar] = 0;
503
Reid Spencerb016a372004-09-15 05:49:50 +0000504 if (remove_contents) {
505 // Recursively descend the directory to remove its content
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000506 // FIXME: The correct way of doing this on Windows isn't pretty...
507 // but this may work if unix-like utils are present.
508 std::string cmd("rm -rf ");
Reid Spencerb016a372004-09-15 05:49:50 +0000509 cmd += path;
510 system(cmd.c_str());
511 } else {
512 // Otherwise, try to just remove the one directory
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000513 if (!RemoveDirectory(pathname))
514 ThrowError(std::string(pathname) + ": Can't destroy directory: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000515 }
516 return true;
517}
518
519bool
520Path::destroy_file() {
521 if (!is_file()) return false;
Reid Spencerd0c9e0e2004-09-18 19:29:16 +0000522
523 DWORD attr = GetFileAttributes(path.c_str());
524
525 // If it doesn't exist, we're done.
526 if (attr == INVALID_FILE_ATTRIBUTES)
527 return true;
528
529 // Read-only files cannot be deleted on Windows. Must remove the read-only
530 // attribute first.
531 if (attr & FILE_ATTRIBUTE_READONLY) {
532 if (!SetFileAttributes(path.c_str(), attr & ~FILE_ATTRIBUTE_READONLY))
533 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
534 }
535
536 if (!DeleteFile(path.c_str()))
537 ThrowError(std::string(path.c_str()) + ": Can't destroy file: ");
Reid Spencerb016a372004-09-15 05:49:50 +0000538 return true;
539}
540
541}
Reid Spencercbad7012004-09-11 04:59:30 +0000542}
543
544// vim: sw=2 smartindent smarttab tw=80 autoindent expandtab
Reid Spencerb016a372004-09-15 05:49:50 +0000545