| /* |
| * 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. |
| */ |
| |
| #define _POSIX_THREAD_SAFE_FUNCTIONS // For mingw localtime_r(). |
| |
| #include "io/FileSystem.h" |
| |
| #include <dirent.h> |
| #include <sys/stat.h> |
| |
| #include "android-base/errors.h" |
| #include "androidfw/FileStream.h" |
| #include "androidfw/Source.h" |
| #include "androidfw/StringPiece.h" |
| #include "util/Files.h" |
| #include "util/Util.h" |
| #include "utils/FileMap.h" |
| |
| using ::android::StringPiece; |
| using ::android::base::SystemErrorCodeToString; |
| |
| namespace aapt { |
| namespace io { |
| |
| RegularFile::RegularFile(const android::Source& source) : source_(source) { |
| } |
| |
| std::unique_ptr<IData> RegularFile::OpenAsData() { |
| android::FileMap map; |
| if (std::optional<android::FileMap> map = file::MmapPath(source_.path, nullptr)) { |
| if (map.value().getDataPtr() && map.value().getDataLength() > 0) { |
| return util::make_unique<MmappedData>(std::move(map.value())); |
| } |
| return util::make_unique<EmptyData>(); |
| } |
| return {}; |
| } |
| |
| std::unique_ptr<android::InputStream> RegularFile::OpenInputStream() { |
| return util::make_unique<android::FileInputStream>(source_.path); |
| } |
| |
| const android::Source& RegularFile::GetSource() const { |
| return source_; |
| } |
| |
| bool RegularFile::GetModificationTime(struct tm* buf) const { |
| if (buf == nullptr) { |
| return false; |
| } |
| struct stat stat_buf; |
| if (stat(source_.path.c_str(), &stat_buf) != 0) { |
| return false; |
| } |
| |
| struct tm* ptm; |
| struct tm tm_result; |
| ptm = localtime_r(&stat_buf.st_mtime, &tm_result); |
| |
| *buf = *ptm; |
| return true; |
| } |
| |
| FileCollectionIterator::FileCollectionIterator(FileCollection* collection) |
| : current_(collection->files_.begin()), end_(collection->files_.end()) {} |
| |
| bool FileCollectionIterator::HasNext() { |
| return current_ != end_; |
| } |
| |
| IFile* FileCollectionIterator::Next() { |
| IFile* result = current_->second.get(); |
| ++current_; |
| return result; |
| } |
| |
| std::unique_ptr<FileCollection> FileCollection::Create(android::StringPiece root, |
| std::string* outError) { |
| std::unique_ptr<FileCollection> collection = |
| std::unique_ptr<FileCollection>(new FileCollection()); |
| |
| std::unique_ptr<DIR, decltype(closedir) *> d(opendir(root.data()), closedir); |
| if (!d) { |
| *outError = "failed to open directory: " + SystemErrorCodeToString(errno); |
| return nullptr; |
| } |
| |
| std::vector<std::string> sorted_files; |
| while (struct dirent *entry = readdir(d.get())) { |
| std::string prefix_path(root); |
| file::AppendPath(&prefix_path, entry->d_name); |
| |
| // The directory to iterate over looking for files |
| if (file::GetFileType(prefix_path) != file::FileType::kDirectory |
| || file::IsHidden(prefix_path)) { |
| continue; |
| } |
| |
| std::unique_ptr<DIR, decltype(closedir)*> subdir(opendir(prefix_path.data()), closedir); |
| if (!subdir) { |
| *outError = "failed to open directory: " + SystemErrorCodeToString(errno); |
| return nullptr; |
| } |
| |
| while (struct dirent* leaf_entry = readdir(subdir.get())) { |
| std::string full_path = prefix_path; |
| file::AppendPath(&full_path, leaf_entry->d_name); |
| |
| // Do not add folders to the file collection |
| if (file::GetFileType(full_path) == file::FileType::kDirectory |
| || file::IsHidden(full_path)) { |
| continue; |
| } |
| |
| sorted_files.push_back(full_path); |
| } |
| } |
| |
| std::sort(sorted_files.begin(), sorted_files.end()); |
| for (const std::string& full_path : sorted_files) { |
| collection->InsertFile(full_path); |
| } |
| |
| return collection; |
| } |
| |
| IFile* FileCollection::InsertFile(StringPiece path) { |
| auto file = util::make_unique<RegularFile>(android::Source(path)); |
| auto it = files_.lower_bound(path); |
| if (it != files_.end() && it->first == path) { |
| it->second = std::move(file); |
| } else { |
| it = files_.emplace_hint(it, path, std::move(file)); |
| } |
| return it->second.get(); |
| } |
| |
| IFile* FileCollection::FindFile(StringPiece path) { |
| auto iter = files_.find(path); |
| if (iter != files_.end()) { |
| return iter->second.get(); |
| } |
| return nullptr; |
| } |
| |
| std::unique_ptr<IFileCollectionIterator> FileCollection::Iterator() { |
| return util::make_unique<FileCollectionIterator>(this); |
| } |
| |
| char FileCollection::GetDirSeparator() { |
| return file::sDirSep; |
| } |
| |
| } // namespace io |
| } // namespace aapt |