/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "util/Files.h" #include "util/Util.h" #include <algorithm> #include <android-base/file.h> #include <cerrno> #include <cstdio> #include <dirent.h> #include <string> #include <sys/stat.h> #ifdef _WIN32 // Windows includes. #include <direct.h> #endif namespace aapt { namespace file { FileType getFileType(const StringPiece& path) { struct stat sb; if (stat(path.data(), &sb) < 0) { if (errno == ENOENT || errno == ENOTDIR) { return FileType::kNonexistant; } return FileType::kUnknown; } if (S_ISREG(sb.st_mode)) { return FileType::kRegular; } else if (S_ISDIR(sb.st_mode)) { return FileType::kDirectory; } else if (S_ISCHR(sb.st_mode)) { return FileType::kCharDev; } else if (S_ISBLK(sb.st_mode)) { return FileType::kBlockDev; } else if (S_ISFIFO(sb.st_mode)) { return FileType::kFifo; #if defined(S_ISLNK) } else if (S_ISLNK(sb.st_mode)) { return FileType::kSymlink; #endif #if defined(S_ISSOCK) } else if (S_ISSOCK(sb.st_mode)) { return FileType::kSocket; #endif } else { return FileType::kUnknown; } } std::vector<std::string> listFiles(const StringPiece& root, std::string* outError) { DIR* dir = opendir(root.data()); if (dir == nullptr) { if (outError) { std::stringstream errorStr; errorStr << "unable to open file: " << strerror(errno); *outError = errorStr.str(); return {}; } } std::vector<std::string> files; dirent* entry; while ((entry = readdir(dir))) { files.emplace_back(entry->d_name); } closedir(dir); return files; } inline static int mkdirImpl(const StringPiece& path) { #ifdef _WIN32 return _mkdir(path.toString().c_str()); #else return mkdir(path.toString().c_str(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP); #endif } bool mkdirs(const StringPiece& path) { const char* start = path.begin(); const char* end = path.end(); for (const char* current = start; current != end; ++current) { if (*current == sDirSep && current != start) { StringPiece parentPath(start, current - start); int result = mkdirImpl(parentPath); if (result < 0 && errno != EEXIST) { return false; } } } return mkdirImpl(path) == 0 || errno == EEXIST; } StringPiece getStem(const StringPiece& path) { const char* start = path.begin(); const char* end = path.end(); for (const char* current = end - 1; current != start - 1; --current) { if (*current == sDirSep) { return StringPiece(start, current - start); } } return {}; } StringPiece getFilename(const StringPiece& path) { const char* end = path.end(); const char* lastDirSep = path.begin(); for (const char* c = path.begin(); c != end; ++c) { if (*c == sDirSep) { lastDirSep = c + 1; } } return StringPiece(lastDirSep, end - lastDirSep); } StringPiece getExtension(const StringPiece& path) { StringPiece filename = getFilename(path); const char* const end = filename.end(); const char* c = std::find(filename.begin(), end, '.'); if (c != end) { return StringPiece(c, end - c); } return {}; } void appendPath(std::string* base, StringPiece part) { assert(base); const bool baseHasTrailingSep = (!base->empty() && *(base->end() - 1) == sDirSep); const bool partHasLeadingSep = (!part.empty() && *(part.begin()) == sDirSep); if (baseHasTrailingSep && partHasLeadingSep) { // Remove the part's leading sep part = part.substr(1, part.size() - 1); } else if (!baseHasTrailingSep && !partHasLeadingSep) { // None of the pieces has a separator. *base += sDirSep; } base->append(part.data(), part.size()); } std::string packageToPath(const StringPiece& package) { std::string outPath; for (StringPiece part : util::tokenize<char>(package, '.')) { appendPath(&outPath, part); } return outPath; } Maybe<android::FileMap> mmapPath(const StringPiece& path, std::string* outError) { std::unique_ptr<FILE, decltype(fclose)*> f = { fopen(path.data(), "rb"), fclose }; if (!f) { if (outError) *outError = strerror(errno); return {}; } int fd = fileno(f.get()); struct stat fileStats = {}; if (fstat(fd, &fileStats) != 0) { if (outError) *outError = strerror(errno); return {}; } android::FileMap fileMap; if (fileStats.st_size == 0) { // mmap doesn't like a length of 0. Instead we return an empty FileMap. return std::move(fileMap); } if (!fileMap.create(path.data(), fd, 0, fileStats.st_size, true)) { if (outError) *outError = strerror(errno); return {}; } return std::move(fileMap); } bool appendArgsFromFile(const StringPiece& path, std::vector<std::string>* outArgList, std::string* outError) { std::string contents; if (!android::base::ReadFileToString(path.toString(), &contents)) { if (outError) *outError = "failed to read argument-list file"; return false; } for (StringPiece line : util::tokenize<char>(contents, ' ')) { line = util::trimWhitespace(line); if (!line.empty()) { outArgList->push_back(line.toString()); } } return true; } bool FileFilter::setPattern(const StringPiece& pattern) { mPatternTokens = util::splitAndLowercase(pattern, ':'); return true; } bool FileFilter::operator()(const std::string& filename, FileType type) const { if (filename == "." || filename == "..") { return false; } const char kDir[] = "dir"; const char kFile[] = "file"; const size_t filenameLen = filename.length(); bool chatty = true; for (const std::string& token : mPatternTokens) { const char* tokenStr = token.c_str(); if (*tokenStr == '!') { chatty = false; tokenStr++; } if (strncasecmp(tokenStr, kDir, sizeof(kDir)) == 0) { if (type != FileType::kDirectory) { continue; } tokenStr += sizeof(kDir); } if (strncasecmp(tokenStr, kFile, sizeof(kFile)) == 0) { if (type != FileType::kRegular) { continue; } tokenStr += sizeof(kFile); } bool ignore = false; size_t n = strlen(tokenStr); if (*tokenStr == '*') { // Math suffix. tokenStr++; n--; if (n <= filenameLen) { ignore = strncasecmp(tokenStr, filename.c_str() + filenameLen - n, n) == 0; } } else if (n > 1 && tokenStr[n - 1] == '*') { // Match prefix. ignore = strncasecmp(tokenStr, filename.c_str(), n - 1) == 0; } else { ignore = strcasecmp(tokenStr, filename.c_str()) == 0; } if (ignore) { if (chatty) { mDiag->warn(DiagMessage() << "skipping " << (type == FileType::kDirectory ? "dir '" : "file '") << filename << "' due to ignore pattern '" << token << "'"); } return false; } } return true; } } // namespace file } // namespace aapt