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