| //===- 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 |