Home | History | Annotate | Download | only in Unix
      1 //===- PathV3.inc ---------------------------------------------------------===//
      2 //
      3 //                     The MCLinker Project
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 #include "mcld/Support/FileSystem.h"
     10 #include "mcld/Support/Path.h"
     11 
     12 #include <llvm/Support/ErrorHandling.h>
     13 
     14 #include <cerrno>
     15 #include <stack>
     16 #include <stdio.h>
     17 #include <string>
     18 #include <sys/stat.h>
     19 #include <sys/types.h>
     20 #include <unistd.h>
     21 
     22 namespace mcld {
     23 namespace sys {
     24 namespace fs {
     25 
     26 //===----------------------------------------------------------------------===//
     27 // mcld::sys::fs::detail
     28 //===----------------------------------------------------------------------===//
     29 namespace detail {
     30 
     31 // return the last charactor being handled.
     32 size_t canonicalize(std::string& pathname) {
     33   // Variable Index //
     34   // SepTable - stack of result separators
     35   // LR(1) Algorithm //
     36   // traverse pPathName
     37   //   if we meet '//', '///', '////', ...
     38   //     -> ignore it
     39   //     -> push current into stack
     40   //     -> jump to the next not '/'
     41   //   if we meet '/./'
     42   //     -> ignore
     43   //     -> jump to the next not '/'
     44   //   if we meet '/../'
     45   //     -> pop previous position of '/' P
     46   //     -> erase P+1 to now
     47   //   if we meet other else
     48   //     -> go go go
     49   //   if we meet '/.../', '/..../', ... -> illegal
     50   if (pathname.empty())
     51     return 0;
     52 
     53   size_t handler = 0;
     54   std::stack<size_t> slash_stack;
     55   slash_stack.push(-1);
     56   while (handler < pathname.size()) {
     57     if (separator == pathname[handler]) {  // handler = 1st '/'
     58       size_t next = handler + 1;
     59       if (next >= pathname.size())
     60         return handler;
     61       switch (pathname[next]) {  // next = handler + 1;
     62         case separator: {        // '//'
     63           while (next < pathname.size() && separator == pathname[next])
     64             ++next;
     65           // next is the last not '/'
     66           pathname.erase(handler, next - handler - 1);
     67           // handler is the first '/'
     68           slash_stack.push(handler);
     69           break;
     70         }
     71         case '.': {                     // '/.'
     72           ++next;                       // next = handler + 2
     73           if (next >= pathname.size())  // '/.'
     74             return handler;
     75           switch (pathname[next]) {
     76             case separator: {  // '/./'
     77               pathname.erase(handler, 2);
     78               break;
     79             }
     80             case '.': {                     // '/..'
     81               ++next;                       // next = handler + 3;
     82               if (next >= pathname.size())  // '/..?'
     83                 return handler;
     84               switch (pathname[next]) {
     85                 case separator: {  // '/../'
     86                   handler = slash_stack.top();
     87                   slash_stack.pop();
     88                   pathname.erase(handler + 1, next - handler);
     89                   if (static_cast<size_t>(-1) == handler) {
     90                     slash_stack.push(-1);
     91                     handler = pathname.find_first_of(separator, handler);
     92                   }
     93                   break;
     94                 }
     95                 case '.': {  // '/...', illegal
     96                   return handler;
     97                   break;
     98                 }
     99                 default: {  // '/..a'
    100                   slash_stack.push(handler);
    101                   handler = pathname.find_first_of(separator, handler + 3);
    102                   break;
    103                 }
    104               }
    105               break;
    106             }
    107             default: {  // '/.a'
    108               slash_stack.push(handler);
    109               handler = pathname.find_first_of(separator, handler + 2);
    110               break;
    111             }
    112           }
    113           break;
    114         }
    115         default: {  // '/a
    116           slash_stack.push(handler);
    117           handler = pathname.find_first_of(separator, handler + 1);
    118           break;
    119         }
    120       }
    121     } else {
    122       handler = pathname.find_first_of(separator, handler);
    123     }
    124   }
    125   return handler;
    126 }
    127 
    128 bool not_found_error(int perrno) {
    129   return perrno == ENOENT || perrno == ENOTDIR;
    130 }
    131 
    132 void status(const Path& p, FileStatus& pFileStatus) {
    133   struct stat path_stat;
    134   if (stat(p.c_str(), &path_stat) != 0) {
    135     if (not_found_error(errno)) {
    136       pFileStatus.setType(FileNotFound);
    137     } else
    138       pFileStatus.setType(StatusError);
    139   } else if (S_ISDIR(path_stat.st_mode))
    140     pFileStatus.setType(DirectoryFile);
    141   else if (S_ISREG(path_stat.st_mode))
    142     pFileStatus.setType(RegularFile);
    143   else if (S_ISBLK(path_stat.st_mode))
    144     pFileStatus.setType(BlockFile);
    145   else if (S_ISCHR(path_stat.st_mode))
    146     pFileStatus.setType(CharacterFile);
    147   else if (S_ISFIFO(path_stat.st_mode))
    148     pFileStatus.setType(FifoFile);
    149   else if (S_ISSOCK(path_stat.st_mode))
    150     pFileStatus.setType(SocketFile);
    151   else
    152     pFileStatus.setType(TypeUnknown);
    153 }
    154 
    155 void symlink_status(const Path& p, FileStatus& pFileStatus) {
    156   struct stat path_stat;
    157   if (lstat(p.c_str(), &path_stat) != 0) {
    158     if (errno == ENOENT || errno == ENOTDIR)  // these are not errors
    159     {
    160       pFileStatus.setType(FileNotFound);
    161     } else
    162       pFileStatus.setType(StatusError);
    163   }
    164   if (S_ISREG(path_stat.st_mode))
    165     pFileStatus.setType(RegularFile);
    166   if (S_ISDIR(path_stat.st_mode))
    167     pFileStatus.setType(DirectoryFile);
    168   if (S_ISLNK(path_stat.st_mode))
    169     pFileStatus.setType(SymlinkFile);
    170   if (S_ISBLK(path_stat.st_mode))
    171     pFileStatus.setType(BlockFile);
    172   if (S_ISCHR(path_stat.st_mode))
    173     pFileStatus.setType(CharacterFile);
    174   if (S_ISFIFO(path_stat.st_mode))
    175     pFileStatus.setType(FifoFile);
    176   if (S_ISSOCK(path_stat.st_mode))
    177     pFileStatus.setType(SocketFile);
    178   else
    179     pFileStatus.setType(TypeUnknown);
    180 }
    181 
    182 /// directory_iterator_increment - increment function implementation
    183 //
    184 //  iterator will call this function in two situations:
    185 //  1. All elements have been put into cache, and iterator stays at the end
    186 //     of cache. (a real end)
    187 //  2. Some but not all elements had been put into cache, and we stoped.
    188 //     An iterator now is staying at the end of cache. (a temporal end)
    189 mcld::sys::fs::PathCache::entry_type* bring_one_into_cache(DirIterator& pIter) {
    190   mcld::sys::fs::PathCache::entry_type* entry = 0;
    191   std::string path(pIter.m_pParent->m_Path.native());
    192   switch (read_dir(pIter.m_pParent->m_Handler, path)) {
    193     case 1: {
    194       // read one
    195       bool exist = false;
    196       entry = pIter.m_pParent->m_Cache.insert(path, exist);
    197       if (!exist)
    198         entry->setValue(sys::fs::Path(path));
    199       break;
    200     }
    201     case 0:  // meet real end
    202       pIter.m_pParent->m_CacheFull = true;
    203       break;
    204     default:
    205     case -1:
    206       llvm::report_fatal_error(std::string("Can't read directory: ") +
    207                                pIter.m_pParent->path().native());
    208       break;
    209   }
    210   return entry;
    211 }
    212 
    213 }  // namespace detail
    214 }  // namespace fs
    215 }  // namespace sys
    216 }  // namespace mcld
    217