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