217 lines
6.8 KiB
C++
217 lines
6.8 KiB
C++
//===- PathV3.inc ---------------------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "mcld/Support/FileSystem.h"
|
|
#include "mcld/Support/Path.h"
|
|
|
|
#include <llvm/Support/ErrorHandling.h>
|
|
|
|
#include <cerrno>
|
|
#include <stack>
|
|
#include <stdio.h>
|
|
#include <string>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
namespace mcld {
|
|
namespace sys {
|
|
namespace fs {
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// mcld::sys::fs::detail
|
|
//===----------------------------------------------------------------------===//
|
|
namespace detail {
|
|
|
|
// return the last charactor being handled.
|
|
size_t canonicalize(std::string& pathname) {
|
|
// Variable Index //
|
|
// SepTable - stack of result separators
|
|
// LR(1) Algorithm //
|
|
// traverse pPathName
|
|
// if we meet '//', '///', '////', ...
|
|
// -> ignore it
|
|
// -> push current into stack
|
|
// -> jump to the next not '/'
|
|
// if we meet '/./'
|
|
// -> ignore
|
|
// -> jump to the next not '/'
|
|
// if we meet '/../'
|
|
// -> pop previous position of '/' P
|
|
// -> erase P+1 to now
|
|
// if we meet other else
|
|
// -> go go go
|
|
// if we meet '/.../', '/..../', ... -> illegal
|
|
if (pathname.empty())
|
|
return 0;
|
|
|
|
size_t handler = 0;
|
|
std::stack<size_t> slash_stack;
|
|
slash_stack.push(-1);
|
|
while (handler < pathname.size()) {
|
|
if (separator == pathname[handler]) { // handler = 1st '/'
|
|
size_t next = handler + 1;
|
|
if (next >= pathname.size())
|
|
return handler;
|
|
switch (pathname[next]) { // next = handler + 1;
|
|
case separator: { // '//'
|
|
while (next < pathname.size() && separator == pathname[next])
|
|
++next;
|
|
// next is the last not '/'
|
|
pathname.erase(handler, next - handler - 1);
|
|
// handler is the first '/'
|
|
slash_stack.push(handler);
|
|
break;
|
|
}
|
|
case '.': { // '/.'
|
|
++next; // next = handler + 2
|
|
if (next >= pathname.size()) // '/.'
|
|
return handler;
|
|
switch (pathname[next]) {
|
|
case separator: { // '/./'
|
|
pathname.erase(handler, 2);
|
|
break;
|
|
}
|
|
case '.': { // '/..'
|
|
++next; // next = handler + 3;
|
|
if (next >= pathname.size()) // '/..?'
|
|
return handler;
|
|
switch (pathname[next]) {
|
|
case separator: { // '/../'
|
|
handler = slash_stack.top();
|
|
slash_stack.pop();
|
|
pathname.erase(handler + 1, next - handler);
|
|
if (static_cast<size_t>(-1) == handler) {
|
|
slash_stack.push(-1);
|
|
handler = pathname.find_first_of(separator, handler);
|
|
}
|
|
break;
|
|
}
|
|
case '.': { // '/...', illegal
|
|
return handler;
|
|
break;
|
|
}
|
|
default: { // '/..a'
|
|
slash_stack.push(handler);
|
|
handler = pathname.find_first_of(separator, handler + 3);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: { // '/.a'
|
|
slash_stack.push(handler);
|
|
handler = pathname.find_first_of(separator, handler + 2);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: { // '/a
|
|
slash_stack.push(handler);
|
|
handler = pathname.find_first_of(separator, handler + 1);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
handler = pathname.find_first_of(separator, handler);
|
|
}
|
|
}
|
|
return handler;
|
|
}
|
|
|
|
bool not_found_error(int perrno) {
|
|
return perrno == ENOENT || perrno == ENOTDIR;
|
|
}
|
|
|
|
void status(const Path& p, FileStatus& pFileStatus) {
|
|
struct stat path_stat;
|
|
if (stat(p.c_str(), &path_stat) != 0) {
|
|
if (not_found_error(errno)) {
|
|
pFileStatus.setType(FileNotFound);
|
|
} else
|
|
pFileStatus.setType(StatusError);
|
|
} else if (S_ISDIR(path_stat.st_mode))
|
|
pFileStatus.setType(DirectoryFile);
|
|
else if (S_ISREG(path_stat.st_mode))
|
|
pFileStatus.setType(RegularFile);
|
|
else if (S_ISBLK(path_stat.st_mode))
|
|
pFileStatus.setType(BlockFile);
|
|
else if (S_ISCHR(path_stat.st_mode))
|
|
pFileStatus.setType(CharacterFile);
|
|
else if (S_ISFIFO(path_stat.st_mode))
|
|
pFileStatus.setType(FifoFile);
|
|
else if (S_ISSOCK(path_stat.st_mode))
|
|
pFileStatus.setType(SocketFile);
|
|
else
|
|
pFileStatus.setType(TypeUnknown);
|
|
}
|
|
|
|
void symlink_status(const Path& p, FileStatus& pFileStatus) {
|
|
struct stat path_stat;
|
|
if (lstat(p.c_str(), &path_stat) != 0) {
|
|
if (errno == ENOENT || errno == ENOTDIR) // these are not errors
|
|
{
|
|
pFileStatus.setType(FileNotFound);
|
|
} else
|
|
pFileStatus.setType(StatusError);
|
|
}
|
|
if (S_ISREG(path_stat.st_mode))
|
|
pFileStatus.setType(RegularFile);
|
|
if (S_ISDIR(path_stat.st_mode))
|
|
pFileStatus.setType(DirectoryFile);
|
|
if (S_ISLNK(path_stat.st_mode))
|
|
pFileStatus.setType(SymlinkFile);
|
|
if (S_ISBLK(path_stat.st_mode))
|
|
pFileStatus.setType(BlockFile);
|
|
if (S_ISCHR(path_stat.st_mode))
|
|
pFileStatus.setType(CharacterFile);
|
|
if (S_ISFIFO(path_stat.st_mode))
|
|
pFileStatus.setType(FifoFile);
|
|
if (S_ISSOCK(path_stat.st_mode))
|
|
pFileStatus.setType(SocketFile);
|
|
else
|
|
pFileStatus.setType(TypeUnknown);
|
|
}
|
|
|
|
/// directory_iterator_increment - increment function implementation
|
|
//
|
|
// iterator will call this function in two situations:
|
|
// 1. All elements have been put into cache, and iterator stays at the end
|
|
// of cache. (a real end)
|
|
// 2. Some but not all elements had been put into cache, and we stoped.
|
|
// An iterator now is staying at the end of cache. (a temporal end)
|
|
mcld::sys::fs::PathCache::entry_type* bring_one_into_cache(DirIterator& pIter) {
|
|
mcld::sys::fs::PathCache::entry_type* entry = 0;
|
|
std::string path(pIter.m_pParent->m_Path.native());
|
|
switch (read_dir(pIter.m_pParent->m_Handler, path)) {
|
|
case 1: {
|
|
// read one
|
|
bool exist = false;
|
|
entry = pIter.m_pParent->m_Cache.insert(path, exist);
|
|
if (!exist)
|
|
entry->setValue(sys::fs::Path(path));
|
|
break;
|
|
}
|
|
case 0: // meet real end
|
|
pIter.m_pParent->m_CacheFull = true;
|
|
break;
|
|
default:
|
|
case -1:
|
|
llvm::report_fatal_error(std::string("Can't read directory: ") +
|
|
pIter.m_pParent->path().native());
|
|
break;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
} // namespace detail
|
|
} // namespace fs
|
|
} // namespace sys
|
|
} // namespace mcld
|