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