| //===- 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/Directory.h> |
| #include <mcld/Support/Path.h> |
| #include <llvm/Support/ErrorHandling.h> |
| |
| #include <cerrno> |
| #include <dirent.h> |
| #include <stdio.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <string> |
| #include <stack> |
| #include <unistd.h> |
| |
| namespace mcld{ |
| namespace sys{ |
| namespace fs{ |
| namespace detail{ |
| |
| const char separator = '/'; |
| const char preferred_separator = '/'; |
| |
| // 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); |
| } |
| |
| /// read_dir - return true if we read one entry |
| // @return value -1: read error |
| // 0: read the end |
| // 1: success |
| static int read_dir(intptr_t& pDir, std::string& pOutFilename) |
| { |
| errno = 0; |
| dirent *cur_dir = ::readdir(reinterpret_cast<DIR*>(pDir)); |
| if (0 == cur_dir && 0 != errno) |
| return -1; |
| |
| // idx does not stay at the end, but all elements had beed put into cache. |
| if (NULL == cur_dir) { |
| return 0; |
| } |
| |
| llvm::StringRef name(cur_dir->d_name, strlen(cur_dir->d_name)); |
| if ((name.size() == 1 && name[0] == '.') || |
| (name.size() == 2 && name[0] == '.' && name[1] == '.')) |
| return read_dir(pDir, pOutFilename); |
| |
| // find a new directory |
| pOutFilename.append(name.data(), name.size()); |
| return 1; |
| } |
| |
| /// 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 beed 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(new 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; |
| } |
| |
| void open_dir(Directory& pDir) |
| { |
| pDir.m_Handler = reinterpret_cast<intptr_t>(opendir(pDir.path().c_str())); |
| if (pDir.m_Handler == 0) { |
| errno = 0; // opendir() will set errno if it failed to open directory. |
| pDir.m_CacheFull = true; |
| return; |
| } |
| // read one entry for advance the end element of the cache. |
| std::string path(pDir.path().native()); |
| switch (read_dir(pDir.m_Handler, path)) { |
| case 1: { |
| // find a new directory |
| bool exist = false; |
| mcld::sys::fs::PathCache::entry_type* entry = pDir.m_Cache.insert(path, exist); |
| if (!exist) |
| entry->setValue(new Path(path)); |
| return; |
| } |
| case 0: |
| // FIXME: a warning function |
| pDir.m_CacheFull = true; |
| return; |
| default: |
| case -1: |
| llvm::report_fatal_error(std::string("Can't read directory: ")+ |
| pDir.path().native()); |
| } |
| } |
| |
| void close_dir(Directory& pDir) |
| { |
| if (pDir.m_Handler) |
| closedir(reinterpret_cast<DIR *>(pDir.m_Handler)); |
| pDir.m_Handler = 0; |
| } |
| |
| void get_pwd(std::string& pPWD) |
| { |
| char* pwd = (char*)malloc(PATH_MAX); |
| pPWD.assign(getcwd(pwd, PATH_MAX)); |
| free(pwd); |
| } |
| |
| } // namespace of detail |
| } // namespace of fs |
| } // namespace of sys |
| } // namespace of mcld |
| |