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