Files.cpp revision 96917c25f09bb2f33e22f20cdfa4ece8f310b084
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 <cerrno>
21#include <cstdio>
22#include <dirent.h>
23#include <string>
24#include <sys/stat.h>
25
26#ifdef _WIN32
27// Windows includes.
28#include <direct.h>
29#endif
30
31namespace aapt {
32namespace file {
33
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;
53#if defined(S_ISLNK)
54    } else if (S_ISLNK(sb.st_mode)) {
55        return FileType::kSymlink;
56#endif
57#if defined(S_ISSOCK)
58    } else if (S_ISSOCK(sb.st_mode)) {
59        return FileType::kSocket;
60#endif
61    } else {
62        return FileType::kUnknown;
63    }
64}
65
66std::vector<std::string> listFiles(const StringPiece& root, std::string* outError) {
67    DIR* dir = opendir(root.data());
68    if (dir == nullptr) {
69        if (outError) {
70            std::stringstream errorStr;
71            errorStr << "unable to open file: " << strerror(errno);
72            *outError = errorStr.str();
73            return {};
74        }
75    }
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) {
88#ifdef _WIN32
89    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) {
99        if (*current == sDirSep && current != start) {
100            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
110StringPiece getStem(const StringPiece& path) {
111    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) {
115            return StringPiece(start, current - start);
116        }
117    }
118    return {};
119}
120
121StringPiece 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
142void 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
156std::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;
180    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
185    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
192bool 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) {
245                mDiag->warn(DiagMessage() << "skipping "
246                            << (type == FileType::kDirectory ? "dir '" : "file '")
247                            << filename << "' due to ignore pattern '"
248                            << token << "'");
249            }
250            return false;
251        }
252    }
253    return true;
254}
255
256} // namespace file
257} // namespace aapt
258