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/Directory.h>
     11 #include <mcld/Support/Path.h>
     12 #include <llvm/Support/ErrorHandling.h>
     13 
     14 #include <cerrno>
     15 #include <dirent.h>
     16 #include <stdio.h>
     17 #include <sys/stat.h>
     18 #include <sys/types.h>
     19 #include <string>
     20 #include <stack>
     21 #include <unistd.h>
     22 
     23 namespace mcld{
     24 namespace sys{
     25 namespace fs{
     26 namespace detail{
     27 
     28 const char          separator = '/';
     29 const char          preferred_separator = '/';
     30 
     31 // return the last charactor being handled.
     32 size_t canonicalize(std::string& pathname)
     33 {
     34   // Variable Index //
     35   // SepTable - stack of result separators
     36   // LR(1) Algorithm //
     37   // traverse pPathName
     38   //   if we meet '//', '///', '////', ...
     39   //     -> ignore it
     40   //     -> push current into stack
     41   //     -> jump to the next not '/'
     42   //   if we meet '/./'
     43   //     -> ignore
     44   //     -> jump to the next not '/'
     45   //   if we meet '/../'
     46   //     -> pop previous position of '/' P
     47   //     -> erase P+1 to now
     48   //   if we meet other else
     49   //     -> go go go
     50   //   if we meet '/.../', '/..../', ... -> illegal
     51   if (pathname.empty())
     52     return 0;
     53 
     54   size_t handler = 0;
     55   std::stack<size_t> slash_stack;
     56   slash_stack.push(-1);
     57   while (handler < pathname.size()) {
     58     if (separator == pathname[handler]) { // handler = 1st '/'
     59       size_t next = handler + 1;
     60       if (next >= pathname.size())
     61         return handler;
     62       switch(pathname[next]) { // next = handler + 1;
     63         case separator: { // '//'
     64           while (next < pathname.size() && separator == pathname[next])
     65             ++next;
     66           // next is the last not '/'
     67           pathname.erase(handler, next - handler - 1);
     68           // handler is the first '/'
     69           slash_stack.push(handler);
     70           break;
     71         }
     72         case '.': { // '/.'
     73           ++next; // next = handler + 2
     74           if (next >= pathname.size()) // '/.'
     75             return handler;
     76           switch (pathname[next]) {
     77             case separator: { // '/./'
     78               pathname.erase(handler, 2);
     79               break;
     80             }
     81             case '.': { // '/..'
     82               ++next; // next = handler + 3;
     83               if (next >= pathname.size()) // '/..?'
     84                 return handler;
     85               switch(pathname[next]) {
     86                 case separator: { // '/../'
     87                   handler = slash_stack.top();
     88                   slash_stack.pop();
     89                   pathname.erase(handler+1, next-handler);
     90                   if (static_cast<size_t>(-1) == handler) {
     91                     slash_stack.push(-1);
     92                     handler = pathname.find_first_of(separator, handler);
     93                   }
     94                   break;
     95                 }
     96                 case '.': { // '/...', illegal
     97                   return handler;
     98                   break;
     99                 }
    100                 default : { // '/..a'
    101                   slash_stack.push(handler);
    102                   handler = pathname.find_first_of(separator, handler+3);
    103                   break;
    104                 }
    105               }
    106               break;
    107             }
    108             default : { // '/.a'
    109               slash_stack.push(handler);
    110               handler = pathname.find_first_of(separator, handler+2);
    111               break;
    112             }
    113           }
    114           break;
    115         }
    116         default : { // '/a
    117           slash_stack.push(handler);
    118           handler = pathname.find_first_of(separator, handler+1);
    119           break;
    120         }
    121       }
    122     }
    123     else {
    124       handler = pathname.find_first_of(separator, handler);
    125     }
    126   }
    127   return handler;
    128 }
    129 
    130 bool not_found_error(int perrno)
    131 {
    132   return perrno == ENOENT || perrno == ENOTDIR;
    133 }
    134 
    135 void status(const Path& p, FileStatus& pFileStatus)
    136 {
    137   struct stat path_stat;
    138   if(stat(p.c_str(), &path_stat)!= 0)
    139   {
    140     if(not_found_error(errno))
    141     {
    142       pFileStatus.setType(FileNotFound);
    143     }
    144     else
    145       pFileStatus.setType(StatusError);
    146   }
    147   else if(S_ISDIR(path_stat.st_mode))
    148     pFileStatus.setType(DirectoryFile);
    149   else if(S_ISREG(path_stat.st_mode))
    150     pFileStatus.setType(RegularFile);
    151   else if(S_ISBLK(path_stat.st_mode))
    152     pFileStatus.setType(BlockFile);
    153   else if(S_ISCHR(path_stat.st_mode))
    154     pFileStatus.setType(CharacterFile);
    155   else if(S_ISFIFO(path_stat.st_mode))
    156     pFileStatus.setType(FifoFile);
    157   else if(S_ISSOCK(path_stat.st_mode))
    158     pFileStatus.setType(SocketFile);
    159   else
    160     pFileStatus.setType(TypeUnknown);
    161 }
    162 
    163 void symlink_status(const Path& p, FileStatus& pFileStatus)
    164 {
    165   struct stat path_stat;
    166   if(lstat(p.c_str(), &path_stat)!= 0)
    167   {
    168     if(errno == ENOENT || errno == ENOTDIR) // these are not errors
    169     {
    170       pFileStatus.setType(FileNotFound);
    171     }
    172     else
    173       pFileStatus.setType(StatusError);
    174   }
    175   if(S_ISREG(path_stat.st_mode))
    176     pFileStatus.setType(RegularFile);
    177   if(S_ISDIR(path_stat.st_mode))
    178     pFileStatus.setType(DirectoryFile);
    179   if(S_ISLNK(path_stat.st_mode))
    180     pFileStatus.setType(SymlinkFile);
    181   if(S_ISBLK(path_stat.st_mode))
    182     pFileStatus.setType(BlockFile);
    183   if(S_ISCHR(path_stat.st_mode))
    184     pFileStatus.setType(CharacterFile);
    185   if(S_ISFIFO(path_stat.st_mode))
    186     pFileStatus.setType(FifoFile);
    187   if(S_ISSOCK(path_stat.st_mode))
    188     pFileStatus.setType(SocketFile);
    189   else
    190     pFileStatus.setType(TypeUnknown);
    191 }
    192 
    193 /// read_dir - return true if we read one entry
    194 //  @return value -1: read error
    195 //                 0: read the end
    196 //                 1: success
    197 static int read_dir(intptr_t& pDir, std::string& pOutFilename)
    198 {
    199   errno = 0;
    200   dirent *cur_dir = ::readdir(reinterpret_cast<DIR*>(pDir));
    201   if (0 == cur_dir && 0 != errno)
    202     return -1;
    203 
    204   // idx does not stay at the end, but all elements had beed put into cache.
    205   if (NULL == cur_dir) {
    206     return 0;
    207   }
    208 
    209   llvm::StringRef name(cur_dir->d_name, strlen(cur_dir->d_name));
    210   if ((name.size() == 1 && name[0] == '.') ||
    211       (name.size() == 2 && name[0] == '.' && name[1] == '.'))
    212     return read_dir(pDir, pOutFilename);
    213 
    214   // find a new directory
    215   pOutFilename.append(name.data(), name.size());
    216   return 1;
    217 }
    218 
    219 /// directory_iterator_increment - increment function implementation
    220 //
    221 //  iterator will call this function in two situations:
    222 //  1. All elements have been put into cache, and iterator stays at the end
    223 //     of cache. (a real end)
    224 //  2. Some but not all elements had beed put into cache, and we stoped.
    225 //     An iterator now is staying at the end of cache. (a temporal end)
    226 mcld::sys::fs::PathCache::entry_type* bring_one_into_cache(DirIterator& pIter)
    227 {
    228   mcld::sys::fs::PathCache::entry_type* entry = 0;
    229   std::string path(pIter.m_pParent->m_Path.native());
    230   switch (read_dir(pIter.m_pParent->m_Handler, path)) {
    231   case 1: {
    232     // read one
    233     bool exist = false;
    234     entry = pIter.m_pParent->m_Cache.insert(path, exist);
    235     if (!exist)
    236       entry->setValue(path);
    237     break;
    238   }
    239   case 0:// meet real end
    240     pIter.m_pParent->m_CacheFull = true;
    241     break;
    242   default:
    243   case -1:
    244     llvm::report_fatal_error(std::string("Can't read directory: ")+
    245                              pIter.m_pParent->path().native());
    246     break;
    247   }
    248   return entry;
    249 }
    250 
    251 void open_dir(Directory& pDir)
    252 {
    253   pDir.m_Handler = reinterpret_cast<intptr_t>(opendir(pDir.path().c_str()));
    254   if (pDir.m_Handler == 0) {
    255     errno = 0; // opendir() will set errno if it failed to open directory.
    256     pDir.m_CacheFull = true;
    257     return;
    258   }
    259   // read one entry for advance the end element of the cache.
    260   std::string path(pDir.path().native());
    261   switch (read_dir(pDir.m_Handler, path)) {
    262   case 1: {
    263     // find a new directory
    264     bool exist = false;
    265     mcld::sys::fs::PathCache::entry_type* entry = pDir.m_Cache.insert(path, exist);
    266     if (!exist)
    267       entry->setValue(path);
    268     return;
    269   }
    270   case 0:
    271     // FIXME: a warning function
    272     pDir.m_CacheFull = true;
    273     return;
    274   default:
    275   case -1:
    276     llvm::report_fatal_error(std::string("Can't read directory: ")+
    277                              pDir.path().native());
    278   }
    279 }
    280 
    281 void close_dir(Directory& pDir)
    282 {
    283   if (pDir.m_Handler)
    284     closedir(reinterpret_cast<DIR *>(pDir.m_Handler));
    285   pDir.m_Handler = 0;
    286 }
    287 
    288 void get_pwd(std::string& pPWD)
    289 {
    290   char* pwd = (char*)malloc(PATH_MAX);
    291   pPWD.assign(getcwd(pwd, PATH_MAX));
    292   free(pwd);
    293 }
    294 
    295 } // namespace of detail
    296 } // namespace of fs
    297 } // namespace of sys
    298 } // namespace of mcld
    299 
    300