Files.cpp revision 96917c25f09bb2f33e22f20cdfa4ece8f310b084
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "util/Files.h" 18#include "util/Util.h" 19 20#include <cerrno> 21#include <cstdio> 22#include <dirent.h> 23#include <string> 24#include <sys/stat.h> 25 26#ifdef _WIN32 27// Windows includes. 28#include <direct.h> 29#endif 30 31namespace aapt { 32namespace file { 33 34FileType getFileType(const StringPiece& path) { 35 struct stat sb; 36 if (stat(path.data(), &sb) < 0) { 37 if (errno == ENOENT || errno == ENOTDIR) { 38 return FileType::kNonexistant; 39 } 40 return FileType::kUnknown; 41 } 42 43 if (S_ISREG(sb.st_mode)) { 44 return FileType::kRegular; 45 } else if (S_ISDIR(sb.st_mode)) { 46 return FileType::kDirectory; 47 } else if (S_ISCHR(sb.st_mode)) { 48 return FileType::kCharDev; 49 } else if (S_ISBLK(sb.st_mode)) { 50 return FileType::kBlockDev; 51 } else if (S_ISFIFO(sb.st_mode)) { 52 return FileType::kFifo; 53#if defined(S_ISLNK) 54 } else if (S_ISLNK(sb.st_mode)) { 55 return FileType::kSymlink; 56#endif 57#if defined(S_ISSOCK) 58 } else if (S_ISSOCK(sb.st_mode)) { 59 return FileType::kSocket; 60#endif 61 } else { 62 return FileType::kUnknown; 63 } 64} 65 66std::vector<std::string> listFiles(const StringPiece& root, std::string* outError) { 67 DIR* dir = opendir(root.data()); 68 if (dir == nullptr) { 69 if (outError) { 70 std::stringstream errorStr; 71 errorStr << "unable to open file: " << strerror(errno); 72 *outError = errorStr.str(); 73 return {}; 74 } 75 } 76 77 std::vector<std::string> files; 78 dirent* entry; 79 while ((entry = readdir(dir))) { 80 files.emplace_back(entry->d_name); 81 } 82 83 closedir(dir); 84 return files; 85} 86 87inline static int mkdirImpl(const StringPiece& path) { 88#ifdef _WIN32 89 return _mkdir(path.toString().c_str()); 90#else 91 return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); 92#endif 93} 94 95bool mkdirs(const StringPiece& path) { 96 const char* start = path.begin(); 97 const char* end = path.end(); 98 for (const char* current = start; current != end; ++current) { 99 if (*current == sDirSep && current != start) { 100 StringPiece parentPath(start, current - start); 101 int result = mkdirImpl(parentPath); 102 if (result < 0 && errno != EEXIST) { 103 return false; 104 } 105 } 106 } 107 return mkdirImpl(path) == 0 || errno == EEXIST; 108} 109 110StringPiece getStem(const StringPiece& path) { 111 const char* start = path.begin(); 112 const char* end = path.end(); 113 for (const char* current = end - 1; current != start - 1; --current) { 114 if (*current == sDirSep) { 115 return StringPiece(start, current - start); 116 } 117 } 118 return {}; 119} 120 121StringPiece getFilename(const StringPiece& path) { 122 const char* end = path.end(); 123 const char* lastDirSep = path.begin(); 124 for (const char* c = path.begin(); c != end; ++c) { 125 if (*c == sDirSep) { 126 lastDirSep = c + 1; 127 } 128 } 129 return StringPiece(lastDirSep, end - lastDirSep); 130} 131 132StringPiece getExtension(const StringPiece& path) { 133 StringPiece filename = getFilename(path); 134 const char* const end = filename.end(); 135 const char* c = std::find(filename.begin(), end, '.'); 136 if (c != end) { 137 return StringPiece(c, end - c); 138 } 139 return {}; 140} 141 142void appendPath(std::string* base, StringPiece part) { 143 assert(base); 144 const bool baseHasTrailingSep = (!base->empty() && *(base->end() - 1) == sDirSep); 145 const bool partHasLeadingSep = (!part.empty() && *(part.begin()) == sDirSep); 146 if (baseHasTrailingSep && partHasLeadingSep) { 147 // Remove the part's leading sep 148 part = part.substr(1, part.size() - 1); 149 } else if (!baseHasTrailingSep && !partHasLeadingSep) { 150 // None of the pieces has a separator. 151 *base += sDirSep; 152 } 153 base->append(part.data(), part.size()); 154} 155 156std::string packageToPath(const StringPiece& package) { 157 std::string outPath; 158 for (StringPiece part : util::tokenize<char>(package, '.')) { 159 appendPath(&outPath, part); 160 } 161 return outPath; 162} 163 164Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError) { 165 std::unique_ptr<FILE, decltype(fclose)*> f = { fopen(path.data(), "rb"), fclose }; 166 if (!f) { 167 if (outError) *outError = strerror(errno); 168 return {}; 169 } 170 171 int fd = fileno(f.get()); 172 173 struct stat fileStats = {}; 174 if (fstat(fd, &fileStats) != 0) { 175 if (outError) *outError = strerror(errno); 176 return {}; 177 } 178 179 android::FileMap fileMap; 180 if (fileStats.st_size == 0) { 181 // mmap doesn't like a length of 0. Instead we return an empty FileMap. 182 return std::move(fileMap); 183 } 184 185 if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) { 186 if (outError) *outError = strerror(errno); 187 return {}; 188 } 189 return std::move(fileMap); 190} 191 192bool FileFilter::setPattern(const StringPiece& pattern) { 193 mPatternTokens = util::splitAndLowercase(pattern, ':'); 194 return true; 195} 196 197bool FileFilter::operator()(const std::string& filename, FileType type) const { 198 if (filename == "." || filename == "..") { 199 return false; 200 } 201 202 const char kDir[] = "dir"; 203 const char kFile[] = "file"; 204 const size_t filenameLen = filename.length(); 205 bool chatty = true; 206 for (const std::string& token : mPatternTokens) { 207 const char* tokenStr = token.c_str(); 208 if (*tokenStr == '!') { 209 chatty = false; 210 tokenStr++; 211 } 212 213 if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) { 214 if (type != FileType::kDirectory) { 215 continue; 216 } 217 tokenStr += sizeof(kDir); 218 } 219 220 if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) { 221 if (type != FileType::kRegular) { 222 continue; 223 } 224 tokenStr += sizeof(kFile); 225 } 226 227 bool ignore = false; 228 size_t n = strlen(tokenStr); 229 if (*tokenStr == '*') { 230 // Math suffix. 231 tokenStr++; 232 n--; 233 if (n <= filenameLen) { 234 ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0; 235 } 236 } else if (n > 1 && tokenStr[n - 1] == '*') { 237 // Match prefix. 238 ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0; 239 } else { 240 ignore = strcasecmp(tokenStr, filename.c_str()) == 0; 241 } 242 243 if (ignore) { 244 if (chatty) { 245 mDiag->warn(DiagMessage() << "skipping " 246 << (type == FileType::kDirectory ? "dir '" : "file '") 247 << filename << "' due to ignore pattern '" 248 << token << "'"); 249 } 250 return false; 251 } 252 } 253 return true; 254} 255 256} // namespace file 257} // namespace aapt 258