xref: /aosp_15_r20/frameworks/base/tools/aapt2/util/Files.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker  *
4*d57664e9SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker  *
8*d57664e9SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker  *
10*d57664e9SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker  * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker  */
16*d57664e9SAndroid Build Coastguard Worker 
17*d57664e9SAndroid Build Coastguard Worker #include "util/Files.h"
18*d57664e9SAndroid Build Coastguard Worker 
19*d57664e9SAndroid Build Coastguard Worker #include <dirent.h>
20*d57664e9SAndroid Build Coastguard Worker #include <sys/stat.h>
21*d57664e9SAndroid Build Coastguard Worker 
22*d57664e9SAndroid Build Coastguard Worker #include <algorithm>
23*d57664e9SAndroid Build Coastguard Worker #include <cerrno>
24*d57664e9SAndroid Build Coastguard Worker #include <cstdio>
25*d57664e9SAndroid Build Coastguard Worker #include <string>
26*d57664e9SAndroid Build Coastguard Worker 
27*d57664e9SAndroid Build Coastguard Worker #include "android-base/errors.h"
28*d57664e9SAndroid Build Coastguard Worker #include "android-base/file.h"
29*d57664e9SAndroid Build Coastguard Worker #include "android-base/logging.h"
30*d57664e9SAndroid Build Coastguard Worker #include "android-base/unique_fd.h"
31*d57664e9SAndroid Build Coastguard Worker #include "android-base/utf8.h"
32*d57664e9SAndroid Build Coastguard Worker 
33*d57664e9SAndroid Build Coastguard Worker #include "util/Util.h"
34*d57664e9SAndroid Build Coastguard Worker 
35*d57664e9SAndroid Build Coastguard Worker #ifdef _WIN32
36*d57664e9SAndroid Build Coastguard Worker // Windows includes.
37*d57664e9SAndroid Build Coastguard Worker #include <windows.h>
38*d57664e9SAndroid Build Coastguard Worker #endif
39*d57664e9SAndroid Build Coastguard Worker 
40*d57664e9SAndroid Build Coastguard Worker using ::android::FileMap;
41*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
42*d57664e9SAndroid Build Coastguard Worker using ::android::base::ReadFileToString;
43*d57664e9SAndroid Build Coastguard Worker using ::android::base::SystemErrorCodeToString;
44*d57664e9SAndroid Build Coastguard Worker using ::android::base::unique_fd;
45*d57664e9SAndroid Build Coastguard Worker 
46*d57664e9SAndroid Build Coastguard Worker namespace aapt {
47*d57664e9SAndroid Build Coastguard Worker namespace file {
48*d57664e9SAndroid Build Coastguard Worker 
49*d57664e9SAndroid Build Coastguard Worker #ifdef _WIN32
GetFileType(const std::string & path)50*d57664e9SAndroid Build Coastguard Worker FileType GetFileType(const std::string& path) {
51*d57664e9SAndroid Build Coastguard Worker   std::wstring path_utf16;
52*d57664e9SAndroid Build Coastguard Worker   if (!::android::base::UTF8PathToWindowsLongPath(path.c_str(), &path_utf16)) {
53*d57664e9SAndroid Build Coastguard Worker     return FileType::kNonExistant;
54*d57664e9SAndroid Build Coastguard Worker   }
55*d57664e9SAndroid Build Coastguard Worker 
56*d57664e9SAndroid Build Coastguard Worker   DWORD result = GetFileAttributesW(path_utf16.c_str());
57*d57664e9SAndroid Build Coastguard Worker   if (result == INVALID_FILE_ATTRIBUTES) {
58*d57664e9SAndroid Build Coastguard Worker     return FileType::kNonExistant;
59*d57664e9SAndroid Build Coastguard Worker   }
60*d57664e9SAndroid Build Coastguard Worker 
61*d57664e9SAndroid Build Coastguard Worker   if (result & FILE_ATTRIBUTE_DIRECTORY) {
62*d57664e9SAndroid Build Coastguard Worker     return FileType::kDirectory;
63*d57664e9SAndroid Build Coastguard Worker   }
64*d57664e9SAndroid Build Coastguard Worker 
65*d57664e9SAndroid Build Coastguard Worker   // Too many types to consider, just let open fail later.
66*d57664e9SAndroid Build Coastguard Worker   return FileType::kRegular;
67*d57664e9SAndroid Build Coastguard Worker }
68*d57664e9SAndroid Build Coastguard Worker #else
69*d57664e9SAndroid Build Coastguard Worker FileType GetFileType(const std::string& path) {
70*d57664e9SAndroid Build Coastguard Worker   struct stat sb;
71*d57664e9SAndroid Build Coastguard Worker   int result = stat(path.c_str(), &sb);
72*d57664e9SAndroid Build Coastguard Worker 
73*d57664e9SAndroid Build Coastguard Worker   if (result == -1) {
74*d57664e9SAndroid Build Coastguard Worker     if (errno == ENOENT || errno == ENOTDIR) {
75*d57664e9SAndroid Build Coastguard Worker       return FileType::kNonExistant;
76*d57664e9SAndroid Build Coastguard Worker     }
77*d57664e9SAndroid Build Coastguard Worker     return FileType::kUnknown;
78*d57664e9SAndroid Build Coastguard Worker   }
79*d57664e9SAndroid Build Coastguard Worker 
80*d57664e9SAndroid Build Coastguard Worker   if (S_ISREG(sb.st_mode)) {
81*d57664e9SAndroid Build Coastguard Worker     return FileType::kRegular;
82*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISDIR(sb.st_mode)) {
83*d57664e9SAndroid Build Coastguard Worker     return FileType::kDirectory;
84*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISCHR(sb.st_mode)) {
85*d57664e9SAndroid Build Coastguard Worker     return FileType::kCharDev;
86*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISBLK(sb.st_mode)) {
87*d57664e9SAndroid Build Coastguard Worker     return FileType::kBlockDev;
88*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISFIFO(sb.st_mode)) {
89*d57664e9SAndroid Build Coastguard Worker     return FileType::kFifo;
90*d57664e9SAndroid Build Coastguard Worker #if defined(S_ISLNK)
91*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISLNK(sb.st_mode)) {
92*d57664e9SAndroid Build Coastguard Worker     return FileType::kSymlink;
93*d57664e9SAndroid Build Coastguard Worker #endif
94*d57664e9SAndroid Build Coastguard Worker #if defined(S_ISSOCK)
95*d57664e9SAndroid Build Coastguard Worker   } else if (S_ISSOCK(sb.st_mode)) {
96*d57664e9SAndroid Build Coastguard Worker     return FileType::kSocket;
97*d57664e9SAndroid Build Coastguard Worker #endif
98*d57664e9SAndroid Build Coastguard Worker   } else {
99*d57664e9SAndroid Build Coastguard Worker     return FileType::kUnknown;
100*d57664e9SAndroid Build Coastguard Worker   }
101*d57664e9SAndroid Build Coastguard Worker }
102*d57664e9SAndroid Build Coastguard Worker #endif
103*d57664e9SAndroid Build Coastguard Worker 
mkdirs(const std::string & path)104*d57664e9SAndroid Build Coastguard Worker bool mkdirs(const std::string& path) {
105*d57664e9SAndroid Build Coastguard Worker  #ifdef _WIN32
106*d57664e9SAndroid Build Coastguard Worker   // Start after the long path prefix if present.
107*d57664e9SAndroid Build Coastguard Worker   bool require_drive = false;
108*d57664e9SAndroid Build Coastguard Worker   size_t current_pos = 0u;
109*d57664e9SAndroid Build Coastguard Worker   if (util::StartsWith(path, R"(\\?\)")) {
110*d57664e9SAndroid Build Coastguard Worker     require_drive = true;
111*d57664e9SAndroid Build Coastguard Worker     current_pos = 4u;
112*d57664e9SAndroid Build Coastguard Worker   }
113*d57664e9SAndroid Build Coastguard Worker 
114*d57664e9SAndroid Build Coastguard Worker   // Start after the drive path if present.
115*d57664e9SAndroid Build Coastguard Worker   if (path.size() >= 3 && path[current_pos + 1] == ':' &&
116*d57664e9SAndroid Build Coastguard Worker        (path[current_pos + 2] == '\\' || path[current_pos + 2] == '/')) {
117*d57664e9SAndroid Build Coastguard Worker     current_pos += 3u;
118*d57664e9SAndroid Build Coastguard Worker   } else if (require_drive) {
119*d57664e9SAndroid Build Coastguard Worker     return false;
120*d57664e9SAndroid Build Coastguard Worker   }
121*d57664e9SAndroid Build Coastguard Worker  #else
122*d57664e9SAndroid Build Coastguard Worker   // Start after the first character so that we don't consume the root '/'.
123*d57664e9SAndroid Build Coastguard Worker   // This is safe to do with unicode because '/' will never match with a continuation character.
124*d57664e9SAndroid Build Coastguard Worker   size_t current_pos = 1u;
125*d57664e9SAndroid Build Coastguard Worker  #endif
126*d57664e9SAndroid Build Coastguard Worker   constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP;
127*d57664e9SAndroid Build Coastguard Worker   while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) {
128*d57664e9SAndroid Build Coastguard Worker     std::string parent_path = path.substr(0, current_pos);
129*d57664e9SAndroid Build Coastguard Worker     if (parent_path.empty()) {
130*d57664e9SAndroid Build Coastguard Worker       continue;
131*d57664e9SAndroid Build Coastguard Worker     }
132*d57664e9SAndroid Build Coastguard Worker 
133*d57664e9SAndroid Build Coastguard Worker     int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode);
134*d57664e9SAndroid Build Coastguard Worker     if (result < 0 && errno != EEXIST) {
135*d57664e9SAndroid Build Coastguard Worker       return false;
136*d57664e9SAndroid Build Coastguard Worker     }
137*d57664e9SAndroid Build Coastguard Worker     current_pos += 1;
138*d57664e9SAndroid Build Coastguard Worker   }
139*d57664e9SAndroid Build Coastguard Worker   return ::android::base::utf8::mkdir(path.c_str(), mode) == 0 || errno == EEXIST;
140*d57664e9SAndroid Build Coastguard Worker }
141*d57664e9SAndroid Build Coastguard Worker 
GetStem(StringPiece path)142*d57664e9SAndroid Build Coastguard Worker StringPiece GetStem(StringPiece path) {
143*d57664e9SAndroid Build Coastguard Worker   const char* start = path.begin();
144*d57664e9SAndroid Build Coastguard Worker   const char* end = path.end();
145*d57664e9SAndroid Build Coastguard Worker   for (const char* current = end - 1; current != start - 1; --current) {
146*d57664e9SAndroid Build Coastguard Worker     if (*current == sDirSep) {
147*d57664e9SAndroid Build Coastguard Worker       return StringPiece(start, current - start);
148*d57664e9SAndroid Build Coastguard Worker     }
149*d57664e9SAndroid Build Coastguard Worker   }
150*d57664e9SAndroid Build Coastguard Worker   return {};
151*d57664e9SAndroid Build Coastguard Worker }
152*d57664e9SAndroid Build Coastguard Worker 
GetFilename(StringPiece path)153*d57664e9SAndroid Build Coastguard Worker StringPiece GetFilename(StringPiece path) {
154*d57664e9SAndroid Build Coastguard Worker   const char* end = path.end();
155*d57664e9SAndroid Build Coastguard Worker   const char* last_dir_sep = path.begin();
156*d57664e9SAndroid Build Coastguard Worker   for (const char* c = path.begin(); c != end; ++c) {
157*d57664e9SAndroid Build Coastguard Worker     if (*c == sDirSep || *c == sInvariantDirSep) {
158*d57664e9SAndroid Build Coastguard Worker       last_dir_sep = c + 1;
159*d57664e9SAndroid Build Coastguard Worker     }
160*d57664e9SAndroid Build Coastguard Worker   }
161*d57664e9SAndroid Build Coastguard Worker   return StringPiece(last_dir_sep, end - last_dir_sep);
162*d57664e9SAndroid Build Coastguard Worker }
163*d57664e9SAndroid Build Coastguard Worker 
GetExtension(StringPiece path)164*d57664e9SAndroid Build Coastguard Worker StringPiece GetExtension(StringPiece path) {
165*d57664e9SAndroid Build Coastguard Worker   StringPiece filename = GetFilename(path);
166*d57664e9SAndroid Build Coastguard Worker   const char* const end = filename.end();
167*d57664e9SAndroid Build Coastguard Worker   const char* c = std::find(filename.begin(), end, '.');
168*d57664e9SAndroid Build Coastguard Worker   if (c != end) {
169*d57664e9SAndroid Build Coastguard Worker     return StringPiece(c, end - c);
170*d57664e9SAndroid Build Coastguard Worker   }
171*d57664e9SAndroid Build Coastguard Worker   return {};
172*d57664e9SAndroid Build Coastguard Worker }
173*d57664e9SAndroid Build Coastguard Worker 
IsHidden(android::StringPiece path)174*d57664e9SAndroid Build Coastguard Worker bool IsHidden(android::StringPiece path) {
175*d57664e9SAndroid Build Coastguard Worker   return util::StartsWith(GetFilename(path), ".");
176*d57664e9SAndroid Build Coastguard Worker }
177*d57664e9SAndroid Build Coastguard Worker 
AppendPath(std::string * base,StringPiece part)178*d57664e9SAndroid Build Coastguard Worker void AppendPath(std::string* base, StringPiece part) {
179*d57664e9SAndroid Build Coastguard Worker   CHECK(base != nullptr);
180*d57664e9SAndroid Build Coastguard Worker   const bool base_has_trailing_sep = (!base->empty() && *(base->end() - 1) == sDirSep);
181*d57664e9SAndroid Build Coastguard Worker   const bool part_has_leading_sep = (!part.empty() && *(part.begin()) == sDirSep);
182*d57664e9SAndroid Build Coastguard Worker   if (base_has_trailing_sep && part_has_leading_sep) {
183*d57664e9SAndroid Build Coastguard Worker     // Remove the part's leading sep
184*d57664e9SAndroid Build Coastguard Worker     part = part.substr(1, part.size() - 1);
185*d57664e9SAndroid Build Coastguard Worker   } else if (!base_has_trailing_sep && !part_has_leading_sep) {
186*d57664e9SAndroid Build Coastguard Worker     // None of the pieces has a separator.
187*d57664e9SAndroid Build Coastguard Worker     *base += sDirSep;
188*d57664e9SAndroid Build Coastguard Worker   }
189*d57664e9SAndroid Build Coastguard Worker   base->append(part.data(), part.size());
190*d57664e9SAndroid Build Coastguard Worker }
191*d57664e9SAndroid Build Coastguard Worker 
BuildPath(const std::vector<StringPiece> & args)192*d57664e9SAndroid Build Coastguard Worker std::string BuildPath(const std::vector<StringPiece>& args) {
193*d57664e9SAndroid Build Coastguard Worker   if (args.empty()) {
194*d57664e9SAndroid Build Coastguard Worker     return "";
195*d57664e9SAndroid Build Coastguard Worker   }
196*d57664e9SAndroid Build Coastguard Worker   std::string out{args[0]};
197*d57664e9SAndroid Build Coastguard Worker   for (int i = 1; i < args.size(); i++) {
198*d57664e9SAndroid Build Coastguard Worker     file::AppendPath(&out, args[i]);
199*d57664e9SAndroid Build Coastguard Worker   }
200*d57664e9SAndroid Build Coastguard Worker   return out;
201*d57664e9SAndroid Build Coastguard Worker }
202*d57664e9SAndroid Build Coastguard Worker 
PackageToPath(StringPiece package)203*d57664e9SAndroid Build Coastguard Worker std::string PackageToPath(StringPiece package) {
204*d57664e9SAndroid Build Coastguard Worker   std::string out_path;
205*d57664e9SAndroid Build Coastguard Worker   for (StringPiece part : util::Tokenize(package, '.')) {
206*d57664e9SAndroid Build Coastguard Worker     AppendPath(&out_path, part);
207*d57664e9SAndroid Build Coastguard Worker   }
208*d57664e9SAndroid Build Coastguard Worker   return out_path;
209*d57664e9SAndroid Build Coastguard Worker }
210*d57664e9SAndroid Build Coastguard Worker 
MmapPath(const std::string & path,std::string * out_error)211*d57664e9SAndroid Build Coastguard Worker std::optional<FileMap> MmapPath(const std::string& path, std::string* out_error) {
212*d57664e9SAndroid Build Coastguard Worker   int flags = O_RDONLY | O_CLOEXEC | O_BINARY;
213*d57664e9SAndroid Build Coastguard Worker   unique_fd fd(TEMP_FAILURE_RETRY(::android::base::utf8::open(path.c_str(), flags)));
214*d57664e9SAndroid Build Coastguard Worker   if (fd == -1) {
215*d57664e9SAndroid Build Coastguard Worker     if (out_error) {
216*d57664e9SAndroid Build Coastguard Worker       *out_error = SystemErrorCodeToString(errno);
217*d57664e9SAndroid Build Coastguard Worker     }
218*d57664e9SAndroid Build Coastguard Worker     return {};
219*d57664e9SAndroid Build Coastguard Worker   }
220*d57664e9SAndroid Build Coastguard Worker 
221*d57664e9SAndroid Build Coastguard Worker   struct stat filestats = {};
222*d57664e9SAndroid Build Coastguard Worker   if (fstat(fd, &filestats) != 0) {
223*d57664e9SAndroid Build Coastguard Worker     if (out_error) {
224*d57664e9SAndroid Build Coastguard Worker       *out_error = SystemErrorCodeToString(errno);
225*d57664e9SAndroid Build Coastguard Worker     }
226*d57664e9SAndroid Build Coastguard Worker     return {};
227*d57664e9SAndroid Build Coastguard Worker   }
228*d57664e9SAndroid Build Coastguard Worker 
229*d57664e9SAndroid Build Coastguard Worker   FileMap filemap;
230*d57664e9SAndroid Build Coastguard Worker   if (filestats.st_size == 0) {
231*d57664e9SAndroid Build Coastguard Worker     // mmap doesn't like a length of 0. Instead we return an empty FileMap.
232*d57664e9SAndroid Build Coastguard Worker     return std::move(filemap);
233*d57664e9SAndroid Build Coastguard Worker   }
234*d57664e9SAndroid Build Coastguard Worker 
235*d57664e9SAndroid Build Coastguard Worker   if (!filemap.create(path.c_str(), fd, 0, filestats.st_size, true)) {
236*d57664e9SAndroid Build Coastguard Worker     if (out_error) {
237*d57664e9SAndroid Build Coastguard Worker       *out_error = SystemErrorCodeToString(errno);
238*d57664e9SAndroid Build Coastguard Worker     }
239*d57664e9SAndroid Build Coastguard Worker     return {};
240*d57664e9SAndroid Build Coastguard Worker   }
241*d57664e9SAndroid Build Coastguard Worker   return std::move(filemap);
242*d57664e9SAndroid Build Coastguard Worker }
243*d57664e9SAndroid Build Coastguard Worker 
AppendArgsFromFile(StringPiece path,std::vector<std::string> * out_arglist,std::string * out_error)244*d57664e9SAndroid Build Coastguard Worker bool AppendArgsFromFile(StringPiece path, std::vector<std::string>* out_arglist,
245*d57664e9SAndroid Build Coastguard Worker                         std::string* out_error) {
246*d57664e9SAndroid Build Coastguard Worker   std::string contents;
247*d57664e9SAndroid Build Coastguard Worker   if (!ReadFileToString(std::string(path), &contents, true /*follow_symlinks*/)) {
248*d57664e9SAndroid Build Coastguard Worker     if (out_error) {
249*d57664e9SAndroid Build Coastguard Worker       *out_error = "failed to read argument-list file";
250*d57664e9SAndroid Build Coastguard Worker     }
251*d57664e9SAndroid Build Coastguard Worker     return false;
252*d57664e9SAndroid Build Coastguard Worker   }
253*d57664e9SAndroid Build Coastguard Worker 
254*d57664e9SAndroid Build Coastguard Worker   for (StringPiece line : util::Tokenize(contents, '\n')) {
255*d57664e9SAndroid Build Coastguard Worker     line = util::TrimWhitespace(line);
256*d57664e9SAndroid Build Coastguard Worker     for (StringPiece arg : util::Tokenize(line, ' ')) {
257*d57664e9SAndroid Build Coastguard Worker       arg = util::TrimWhitespace(arg);
258*d57664e9SAndroid Build Coastguard Worker       if (!arg.empty()) {
259*d57664e9SAndroid Build Coastguard Worker         out_arglist->emplace_back(arg);
260*d57664e9SAndroid Build Coastguard Worker       }
261*d57664e9SAndroid Build Coastguard Worker     }
262*d57664e9SAndroid Build Coastguard Worker   }
263*d57664e9SAndroid Build Coastguard Worker   return true;
264*d57664e9SAndroid Build Coastguard Worker }
265*d57664e9SAndroid Build Coastguard Worker 
AppendSetArgsFromFile(StringPiece path,std::unordered_set<std::string> * out_argset,std::string * out_error)266*d57664e9SAndroid Build Coastguard Worker bool AppendSetArgsFromFile(StringPiece path, std::unordered_set<std::string>* out_argset,
267*d57664e9SAndroid Build Coastguard Worker                            std::string* out_error) {
268*d57664e9SAndroid Build Coastguard Worker   std::string contents;
269*d57664e9SAndroid Build Coastguard Worker   if (!ReadFileToString(std::string(path), &contents, true /*follow_symlinks*/)) {
270*d57664e9SAndroid Build Coastguard Worker     if (out_error) {
271*d57664e9SAndroid Build Coastguard Worker       *out_error = "failed to read argument-list file";
272*d57664e9SAndroid Build Coastguard Worker     }
273*d57664e9SAndroid Build Coastguard Worker     return false;
274*d57664e9SAndroid Build Coastguard Worker   }
275*d57664e9SAndroid Build Coastguard Worker 
276*d57664e9SAndroid Build Coastguard Worker   for (StringPiece line : util::Tokenize(contents, '\n')) {
277*d57664e9SAndroid Build Coastguard Worker     line = util::TrimWhitespace(line);
278*d57664e9SAndroid Build Coastguard Worker     for (StringPiece arg : util::Tokenize(line, ' ')) {
279*d57664e9SAndroid Build Coastguard Worker       arg = util::TrimWhitespace(arg);
280*d57664e9SAndroid Build Coastguard Worker       if (!arg.empty()) {
281*d57664e9SAndroid Build Coastguard Worker         out_argset->emplace(arg);
282*d57664e9SAndroid Build Coastguard Worker       }
283*d57664e9SAndroid Build Coastguard Worker     }
284*d57664e9SAndroid Build Coastguard Worker   }
285*d57664e9SAndroid Build Coastguard Worker   return true;
286*d57664e9SAndroid Build Coastguard Worker }
287*d57664e9SAndroid Build Coastguard Worker 
SetPattern(StringPiece pattern)288*d57664e9SAndroid Build Coastguard Worker bool FileFilter::SetPattern(StringPiece pattern) {
289*d57664e9SAndroid Build Coastguard Worker   pattern_tokens_ = util::SplitAndLowercase(pattern, ':');
290*d57664e9SAndroid Build Coastguard Worker   return true;
291*d57664e9SAndroid Build Coastguard Worker }
292*d57664e9SAndroid Build Coastguard Worker 
operator ()(const std::string & filename,FileType type) const293*d57664e9SAndroid Build Coastguard Worker bool FileFilter::operator()(const std::string& filename, FileType type) const {
294*d57664e9SAndroid Build Coastguard Worker   if (filename == "." || filename == "..") {
295*d57664e9SAndroid Build Coastguard Worker     return false;
296*d57664e9SAndroid Build Coastguard Worker   }
297*d57664e9SAndroid Build Coastguard Worker 
298*d57664e9SAndroid Build Coastguard Worker   const char kDir[] = "dir";
299*d57664e9SAndroid Build Coastguard Worker   const char kFile[] = "file";
300*d57664e9SAndroid Build Coastguard Worker   const size_t filename_len = filename.length();
301*d57664e9SAndroid Build Coastguard Worker   bool chatty = true;
302*d57664e9SAndroid Build Coastguard Worker   for (const std::string& token : pattern_tokens_) {
303*d57664e9SAndroid Build Coastguard Worker     const char* token_str = token.c_str();
304*d57664e9SAndroid Build Coastguard Worker     if (*token_str == '!') {
305*d57664e9SAndroid Build Coastguard Worker       chatty = false;
306*d57664e9SAndroid Build Coastguard Worker       token_str++;
307*d57664e9SAndroid Build Coastguard Worker     }
308*d57664e9SAndroid Build Coastguard Worker 
309*d57664e9SAndroid Build Coastguard Worker     if (strncasecmp(token_str, kDir, sizeof(kDir)) == 0) {
310*d57664e9SAndroid Build Coastguard Worker       if (type != FileType::kDirectory) {
311*d57664e9SAndroid Build Coastguard Worker         continue;
312*d57664e9SAndroid Build Coastguard Worker       }
313*d57664e9SAndroid Build Coastguard Worker       token_str += sizeof(kDir);
314*d57664e9SAndroid Build Coastguard Worker     }
315*d57664e9SAndroid Build Coastguard Worker 
316*d57664e9SAndroid Build Coastguard Worker     if (strncasecmp(token_str, kFile, sizeof(kFile)) == 0) {
317*d57664e9SAndroid Build Coastguard Worker       if (type != FileType::kRegular) {
318*d57664e9SAndroid Build Coastguard Worker         continue;
319*d57664e9SAndroid Build Coastguard Worker       }
320*d57664e9SAndroid Build Coastguard Worker       token_str += sizeof(kFile);
321*d57664e9SAndroid Build Coastguard Worker     }
322*d57664e9SAndroid Build Coastguard Worker 
323*d57664e9SAndroid Build Coastguard Worker     bool ignore = false;
324*d57664e9SAndroid Build Coastguard Worker     size_t n = strlen(token_str);
325*d57664e9SAndroid Build Coastguard Worker     if (*token_str == '*') {
326*d57664e9SAndroid Build Coastguard Worker       // Math suffix.
327*d57664e9SAndroid Build Coastguard Worker       token_str++;
328*d57664e9SAndroid Build Coastguard Worker       n--;
329*d57664e9SAndroid Build Coastguard Worker       if (n <= filename_len) {
330*d57664e9SAndroid Build Coastguard Worker         ignore =
331*d57664e9SAndroid Build Coastguard Worker             strncasecmp(token_str, filename.c_str() + filename_len - n, n) == 0;
332*d57664e9SAndroid Build Coastguard Worker       }
333*d57664e9SAndroid Build Coastguard Worker     } else if (n > 1 && token_str[n - 1] == '*') {
334*d57664e9SAndroid Build Coastguard Worker       // Match prefix.
335*d57664e9SAndroid Build Coastguard Worker       ignore = strncasecmp(token_str, filename.c_str(), n - 1) == 0;
336*d57664e9SAndroid Build Coastguard Worker     } else {
337*d57664e9SAndroid Build Coastguard Worker       ignore = strcasecmp(token_str, filename.c_str()) == 0;
338*d57664e9SAndroid Build Coastguard Worker     }
339*d57664e9SAndroid Build Coastguard Worker 
340*d57664e9SAndroid Build Coastguard Worker     if (ignore) {
341*d57664e9SAndroid Build Coastguard Worker       if (chatty) {
342*d57664e9SAndroid Build Coastguard Worker         diag_->Warn(android::DiagMessage()
343*d57664e9SAndroid Build Coastguard Worker                     << "skipping " << (type == FileType::kDirectory ? "dir '" : "file '")
344*d57664e9SAndroid Build Coastguard Worker                     << filename << "' due to ignore pattern '" << token << "'");
345*d57664e9SAndroid Build Coastguard Worker       }
346*d57664e9SAndroid Build Coastguard Worker       return false;
347*d57664e9SAndroid Build Coastguard Worker     }
348*d57664e9SAndroid Build Coastguard Worker   }
349*d57664e9SAndroid Build Coastguard Worker   return true;
350*d57664e9SAndroid Build Coastguard Worker }
351*d57664e9SAndroid Build Coastguard Worker 
FindFiles(android::StringPiece path,android::IDiagnostics * diag,const FileFilter * filter)352*d57664e9SAndroid Build Coastguard Worker std::optional<std::vector<std::string>> FindFiles(android::StringPiece path,
353*d57664e9SAndroid Build Coastguard Worker                                                   android::IDiagnostics* diag,
354*d57664e9SAndroid Build Coastguard Worker                                                   const FileFilter* filter) {
355*d57664e9SAndroid Build Coastguard Worker   const auto& root_dir = path;
356*d57664e9SAndroid Build Coastguard Worker   std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir);
357*d57664e9SAndroid Build Coastguard Worker   if (!d) {
358*d57664e9SAndroid Build Coastguard Worker     diag->Error(android::DiagMessage() << SystemErrorCodeToString(errno) << ": " << root_dir);
359*d57664e9SAndroid Build Coastguard Worker     return {};
360*d57664e9SAndroid Build Coastguard Worker   }
361*d57664e9SAndroid Build Coastguard Worker 
362*d57664e9SAndroid Build Coastguard Worker   std::vector<std::string> files;
363*d57664e9SAndroid Build Coastguard Worker   std::vector<std::string> subdirs;
364*d57664e9SAndroid Build Coastguard Worker   while (struct dirent* entry = readdir(d.get())) {
365*d57664e9SAndroid Build Coastguard Worker     if (util::StartsWith(entry->d_name, ".")) {
366*d57664e9SAndroid Build Coastguard Worker       continue;
367*d57664e9SAndroid Build Coastguard Worker     }
368*d57664e9SAndroid Build Coastguard Worker 
369*d57664e9SAndroid Build Coastguard Worker     std::string file_name = entry->d_name;
370*d57664e9SAndroid Build Coastguard Worker     std::string full_path{root_dir};
371*d57664e9SAndroid Build Coastguard Worker     AppendPath(&full_path, file_name);
372*d57664e9SAndroid Build Coastguard Worker     const FileType file_type = GetFileType(full_path);
373*d57664e9SAndroid Build Coastguard Worker 
374*d57664e9SAndroid Build Coastguard Worker     if (filter != nullptr) {
375*d57664e9SAndroid Build Coastguard Worker       if (!(*filter)(file_name, file_type)) {
376*d57664e9SAndroid Build Coastguard Worker         continue;
377*d57664e9SAndroid Build Coastguard Worker       }
378*d57664e9SAndroid Build Coastguard Worker     }
379*d57664e9SAndroid Build Coastguard Worker 
380*d57664e9SAndroid Build Coastguard Worker     if (file_type == file::FileType::kDirectory) {
381*d57664e9SAndroid Build Coastguard Worker       subdirs.push_back(std::move(file_name));
382*d57664e9SAndroid Build Coastguard Worker     } else {
383*d57664e9SAndroid Build Coastguard Worker       files.push_back(std::move(file_name));
384*d57664e9SAndroid Build Coastguard Worker     }
385*d57664e9SAndroid Build Coastguard Worker   }
386*d57664e9SAndroid Build Coastguard Worker 
387*d57664e9SAndroid Build Coastguard Worker   // Now process subdirs.
388*d57664e9SAndroid Build Coastguard Worker   for (const std::string& subdir : subdirs) {
389*d57664e9SAndroid Build Coastguard Worker     std::string full_subdir{root_dir};
390*d57664e9SAndroid Build Coastguard Worker     AppendPath(&full_subdir, subdir);
391*d57664e9SAndroid Build Coastguard Worker     std::optional<std::vector<std::string>> subfiles = FindFiles(full_subdir, diag, filter);
392*d57664e9SAndroid Build Coastguard Worker     if (!subfiles) {
393*d57664e9SAndroid Build Coastguard Worker       return {};
394*d57664e9SAndroid Build Coastguard Worker     }
395*d57664e9SAndroid Build Coastguard Worker 
396*d57664e9SAndroid Build Coastguard Worker     for (const std::string& subfile : subfiles.value()) {
397*d57664e9SAndroid Build Coastguard Worker       std::string new_file = subdir;
398*d57664e9SAndroid Build Coastguard Worker       AppendPath(&new_file, subfile);
399*d57664e9SAndroid Build Coastguard Worker       files.push_back(new_file);
400*d57664e9SAndroid Build Coastguard Worker     }
401*d57664e9SAndroid Build Coastguard Worker   }
402*d57664e9SAndroid Build Coastguard Worker   return files;
403*d57664e9SAndroid Build Coastguard Worker }
404*d57664e9SAndroid Build Coastguard Worker 
405*d57664e9SAndroid Build Coastguard Worker }  // namespace file
406*d57664e9SAndroid Build Coastguard Worker }  // namespace aapt
407