blob: a31adcedeee0845fe0a2fc75fef211f84e70590e [file] [log] [blame]
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001// Copyright 2015 Google Inc. All rights reserved
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build ignore
16
17#include "find.h"
18
19#include <dirent.h>
20#include <fnmatch.h>
21#include <limits.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
25
Dan Willemsen439f6f12016-10-19 01:13:54 -070026#include <algorithm>
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090027#include <memory>
28#include <vector>
29
30//#undef NOLOG
31
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +090032#include "fileutil.h"
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090033#include "log.h"
34#include "string_piece.h"
35#include "strutil.h"
Shinichiro Hamajicbd34cd2015-07-05 02:53:04 +090036#include "timeutil.h"
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090037
Dan Willemsen3ce083f2017-10-11 22:17:48 -070038#define FIND_WARN_LOC(...) \
39 do { \
Dan Willemsenf63a3fd2017-04-27 23:39:57 -070040 if (g_flags.werror_find_emulator) { \
41 ERROR_LOC(__VA_ARGS__); \
42 } else { \
43 WARN_LOC(__VA_ARGS__); \
44 } \
45 } while (0)
46
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090047class FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090048 public:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090049 virtual ~FindCond() = default;
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090050 virtual bool IsTrue(const string& path, unsigned char type) const = 0;
Dan Willemsen439f6f12016-10-19 01:13:54 -070051 virtual bool Countable() const = 0;
52 virtual unsigned Count() const = 0;
Dan Willemsen3ce083f2017-10-11 22:17:48 -070053
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090054 protected:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090055 FindCond() = default;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090056};
57
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090058namespace {
59
60class NameCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090061 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -070062 explicit NameCond(const string& n) : name_(n) {
Dan Willemsen439f6f12016-10-19 01:13:54 -070063 has_wildcard_ = (n.find_first_of("?*[") != string::npos);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090064 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090065 virtual bool IsTrue(const string& path, unsigned char) const override {
66 return fnmatch(name_.c_str(), Basename(path).data(), 0) == 0;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090067 }
Dan Willemsen3ce083f2017-10-11 22:17:48 -070068 virtual bool Countable() const override { return !has_wildcard_; }
69 virtual unsigned Count() const override { return 1; }
70
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090071 private:
72 string name_;
Dan Willemsen439f6f12016-10-19 01:13:54 -070073 bool has_wildcard_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090074};
75
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090076class TypeCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090077 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -070078 explicit TypeCond(unsigned char t) : type_(t) {}
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090079 virtual bool IsTrue(const string&, unsigned char type) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090080 return type == type_;
81 }
Dan Willemsen3ce083f2017-10-11 22:17:48 -070082 virtual bool Countable() const override { return false; }
83 virtual unsigned Count() const override { return 0; }
84
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090085 private:
86 unsigned char type_;
87};
88
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090089class NotCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090090 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -070091 NotCond(FindCond* c) : c_(c) {}
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090092 virtual bool IsTrue(const string& path, unsigned char type) const override {
93 return !c_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090094 }
Dan Willemsen3ce083f2017-10-11 22:17:48 -070095 virtual bool Countable() const override { return false; }
96 virtual unsigned Count() const override { return 0; }
97
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090098 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090099 unique_ptr<FindCond> c_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900100};
101
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900102class AndCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900103 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700104 AndCond(FindCond* c1, FindCond* c2) : c1_(c1), c2_(c2) {}
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900105 virtual bool IsTrue(const string& path, unsigned char type) const override {
106 if (c1_->IsTrue(path, type))
107 return c2_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900108 return false;
109 }
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700110 virtual bool Countable() const override { return false; }
111 virtual unsigned Count() const override { return 0; }
112
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900113 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900114 unique_ptr<FindCond> c1_, c2_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900115};
116
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900117class OrCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900118 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700119 OrCond(FindCond* c1, FindCond* c2) : c1_(c1), c2_(c2) {}
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900120 virtual bool IsTrue(const string& path, unsigned char type) const override {
121 if (!c1_->IsTrue(path, type))
122 return c2_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900123 return true;
124 }
Dan Willemsen439f6f12016-10-19 01:13:54 -0700125 virtual bool Countable() const override {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700126 return c1_->Countable() && c2_->Countable();
127 ;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700128 }
129 virtual unsigned Count() const override {
130 return c1_->Count() + c2_->Count();
131 }
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700132
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900133 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900134 unique_ptr<FindCond> c1_, c2_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900135};
136
137class DirentNode {
138 public:
139 virtual ~DirentNode() = default;
140
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700141 virtual const DirentNode* FindDir(StringPiece) const { return NULL; }
142 virtual bool RunFind(const FindCommand& fc,
143 const Loc& loc,
144 int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900145 string* path,
146 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700147 vector<string>& out) const = 0;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900148
149 virtual bool IsDirectory() const = 0;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900150
151 const string& base() const { return base_; }
152
153 protected:
154 explicit DirentNode(const string& name) {
155 base_ = Basename(name).as_string();
156 }
157
158 void PrintIfNecessary(const FindCommand& fc,
159 const string& path,
160 unsigned char type,
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900161 int d,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700162 vector<string>& out) const {
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900163 if (fc.print_cond && !fc.print_cond->IsTrue(path, type))
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900164 return;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900165 if (d < fc.mindepth)
166 return;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700167 out.push_back(path);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900168 }
169
170 string base_;
171};
172
173class DirentFileNode : public DirentNode {
174 public:
175 DirentFileNode(const string& name, unsigned char type)
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700176 : DirentNode(name), type_(type) {}
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900177
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700178 virtual bool RunFind(const FindCommand& fc,
179 const Loc&,
180 int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900181 string* path,
182 unordered_map<const DirentNode*, string>*,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700183 vector<string>& out) const override {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900184 PrintIfNecessary(fc, *path, type_, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900185 return true;
186 }
187
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900188 virtual bool IsDirectory() const override { return false; }
189
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900190 private:
191 unsigned char type_;
192};
193
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900194struct ScopedReadDirTracker {
195 public:
196 ScopedReadDirTracker(const DirentNode* n,
197 const string& path,
198 unordered_map<const DirentNode*, string>* cur_read_dirs)
199 : n_(NULL), cur_read_dirs_(cur_read_dirs) {
200 const auto& p = cur_read_dirs->emplace(n, path);
201 if (p.second) {
202 n_ = n;
203 } else {
204 conflicted_ = p.first->second;
205 }
206 }
207
208 ~ScopedReadDirTracker() {
209 if (n_)
210 cur_read_dirs_->erase(n_);
211 }
212
213 bool ok() const { return conflicted_.empty(); }
214 const string& conflicted() const { return conflicted_; }
215
216 private:
217 string conflicted_;
218 const DirentNode* n_;
219 unordered_map<const DirentNode*, string>* cur_read_dirs_;
220};
221
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900222class DirentDirNode : public DirentNode {
223 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700224 explicit DirentDirNode(const string& name) : DirentNode(name) {}
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900225
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900226 ~DirentDirNode() {
227 for (auto& p : children_) {
228 delete p.second;
229 }
230 }
231
Shinichiro Hamaji4b351ab2016-02-12 19:42:30 +0900232 virtual const DirentNode* FindDir(StringPiece d) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900233 if (d.empty() || d == ".")
234 return this;
235 size_t index = d.find('/');
236 const string& p = d.substr(0, index).as_string();
Dan Willemsen4be0f092017-07-25 20:26:54 -0700237 if (p.empty() || p == ".")
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700238 return FindDir(d.substr(index + 1));
239 ;
Dan Willemsen48d6e8c2015-08-05 14:38:34 -0700240 for (auto& child : children_) {
241 if (p == child.first) {
242 if (index == string::npos)
243 return child.second;
244 StringPiece nd = d.substr(index + 1);
245 return child.second->FindDir(nd);
246 }
247 }
248 return NULL;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900249 }
250
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700251 virtual bool RunFind(const FindCommand& fc,
252 const Loc& loc,
253 int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900254 string* path,
255 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700256 vector<string>& out) const override {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900257 ScopedReadDirTracker srdt(this, *path, cur_read_dirs);
258 if (!srdt.ok()) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700259 FIND_WARN_LOC(loc,
260 "FindEmulator: find: File system loop detected; `%s' "
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700261 "is part of the same file system loop as `%s'.",
262 path->c_str(), srdt.conflicted().c_str());
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900263 return true;
264 }
265
266 fc.read_dirs->insert(*path);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900267
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900268 if (fc.prune_cond && fc.prune_cond->IsTrue(*path, DT_DIR)) {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900269 if (fc.type != FindCommandType::FINDLEAVES) {
Dan Willemsen439f6f12016-10-19 01:13:54 -0700270 out.push_back(*path);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900271 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900272 return true;
273 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900274
275 PrintIfNecessary(fc, *path, DT_DIR, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900276
Shinichiro Hamajid45a09d2015-07-02 00:35:57 +0900277 if (d >= fc.depth)
278 return true;
279
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900280 size_t orig_path_size = path->size();
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900281 if (fc.type == FindCommandType::FINDLEAVES) {
Dan Willemsen439f6f12016-10-19 01:13:54 -0700282 size_t orig_out_size = out.size();
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900283 for (const auto& p : children_) {
284 DirentNode* c = p.second;
285 // We will handle directories later.
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900286 if (c->IsDirectory())
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900287 continue;
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700288 if ((*path)[path->size() - 1] != '/')
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900289 *path += '/';
290 *path += c->base();
Dan Willemsen692e64e2017-02-22 15:38:33 -0800291 if (!c->RunFind(fc, loc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900292 return false;
293 path->resize(orig_path_size);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900294 }
295
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700296 // Found a leaf, stop the search.
Dan Willemsen439f6f12016-10-19 01:13:54 -0700297 if (orig_out_size != out.size()) {
298 // If we've found all possible files in this directory, we don't need
299 // to add a regen dependency on the directory, we just need to ensure
300 // that the files are not removed.
301 if (fc.print_cond->Countable() &&
302 fc.print_cond->Count() == out.size() - orig_out_size) {
303 fc.read_dirs->erase(*path);
304 for (unsigned i = orig_out_size; i < out.size(); i++) {
305 fc.found_files->push_back(out[i]);
306 }
307 }
308
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700309 return true;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700310 }
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700311
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900312 for (const auto& p : children_) {
313 DirentNode* c = p.second;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900314 if (!c->IsDirectory())
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900315 continue;
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700316 if ((*path)[path->size() - 1] != '/')
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900317 *path += '/';
318 *path += c->base();
Dan Willemsen692e64e2017-02-22 15:38:33 -0800319 if (!c->RunFind(fc, loc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900320 return false;
321 path->resize(orig_path_size);
322 }
323 } else {
324 for (const auto& p : children_) {
325 DirentNode* c = p.second;
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700326 if ((*path)[path->size() - 1] != '/')
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900327 *path += '/';
328 *path += c->base();
Dan Willemsen692e64e2017-02-22 15:38:33 -0800329 if (!c->RunFind(fc, loc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900330 return false;
331 path->resize(orig_path_size);
332 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900333 }
334 return true;
335 }
336
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900337 virtual bool IsDirectory() const override { return true; }
338
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900339 void Add(const string& name, DirentNode* c) {
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900340 children_.emplace(children_.end(), name, c);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900341 }
342
343 private:
Dan Willemsen48d6e8c2015-08-05 14:38:34 -0700344 vector<pair<string, DirentNode*>> children_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900345};
346
347class DirentSymlinkNode : public DirentNode {
348 public:
349 explicit DirentSymlinkNode(const string& name)
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700350 : DirentNode(name), to_(NULL), errno_(0) {}
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900351
Shinichiro Hamaji4b351ab2016-02-12 19:42:30 +0900352 virtual const DirentNode* FindDir(StringPiece d) const override {
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900353 if (errno_ == 0 && to_)
354 return to_->FindDir(d);
355 return NULL;
356 }
357
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700358 virtual bool RunFind(const FindCommand& fc,
359 const Loc& loc,
360 int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900361 string* path,
362 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700363 vector<string>& out) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900364 unsigned char type = DT_LNK;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900365 if (fc.follows_symlinks && errno_ != ENOENT) {
366 if (errno_) {
367 if (fc.type != FindCommandType::FINDLEAVES) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700368 FIND_WARN_LOC(loc, "FindEmulator: find: `%s': %s", path->c_str(),
369 strerror(errno_));
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900370 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900371 return true;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900372 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900373
374 if (!to_) {
375 LOG("FindEmulator does not support %s", path->c_str());
376 return false;
377 }
378
Dan Willemsen692e64e2017-02-22 15:38:33 -0800379 return to_->RunFind(fc, loc, d, path, cur_read_dirs, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900380 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900381 PrintIfNecessary(fc, *path, type, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900382 return true;
383 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900384
385 virtual bool IsDirectory() const override {
386 return errno_ == 0 && to_ && to_->IsDirectory();
387 }
388
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700389 void set_to(const DirentNode* to) { to_ = to; }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900390
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700391 void set_errno(int e) { errno_ = e; }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900392
393 private:
394 const DirentNode* to_;
395 int errno_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900396};
397
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900398class FindCommandParser {
399 public:
400 FindCommandParser(StringPiece cmd, FindCommand* fc)
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700401 : cmd_(cmd), fc_(fc), has_if_(false) {}
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900402
403 bool Parse() {
404 cur_ = cmd_;
405 if (!ParseImpl()) {
406 LOG("FindEmulator: Unsupported find command: %.*s", SPF(cmd_));
407 return false;
408 }
409 CHECK(TrimLeftSpace(cur_).empty());
410 return true;
411 }
412
413 private:
414 bool GetNextToken(StringPiece* tok) {
415 if (!unget_tok_.empty()) {
416 *tok = unget_tok_;
417 unget_tok_.clear();
418 return true;
419 }
420
421 cur_ = TrimLeftSpace(cur_);
422
423 if (cur_[0] == ';') {
424 *tok = cur_.substr(0, 1);
425 cur_ = cur_.substr(1);
426 return true;
427 }
428 if (cur_[0] == '&') {
429 if (cur_.get(1) != '&') {
430 return false;
431 }
432 *tok = cur_.substr(0, 2);
433 cur_ = cur_.substr(2);
434 return true;
435 }
436
437 size_t i = 0;
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700438 while (i < cur_.size() && !isspace(cur_[i]) && cur_[i] != ';' &&
439 cur_[i] != '&') {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900440 i++;
441 }
442
443 *tok = cur_.substr(0, i);
444 cur_ = cur_.substr(i);
445
446 const char c = tok->get(0);
447 if (c == '\'' || c == '"') {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700448 if (tok->size() < 2 || (*tok)[tok->size() - 1] != c)
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900449 return false;
450 *tok = tok->substr(1, tok->size() - 2);
451 return true;
Dan Willemsen1e015922018-02-12 18:16:35 -0800452 } else {
453 // Support stripping off a leading backslash
454 if (c == '\\') {
455 *tok = tok->substr(1);
456 }
457 // But if there are any others, we can't support it, as unescaping would
458 // require allocation
459 if (tok->find("\\") != string::npos) {
460 return false;
461 }
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900462 }
463
464 return true;
465 }
466
467 void UngetToken(StringPiece tok) {
468 CHECK(unget_tok_.empty());
469 if (!tok.empty())
470 unget_tok_ = tok;
471 }
472
473 bool ParseTest() {
474 if (has_if_ || !fc_->testdir.empty())
475 return false;
476 StringPiece tok;
477 if (!GetNextToken(&tok) || tok != "-d")
478 return false;
479 if (!GetNextToken(&tok) || tok.empty())
480 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900481 fc_->testdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900482 return true;
483 }
484
485 FindCond* ParseFact(StringPiece tok) {
Dan Willemsen1e015922018-02-12 18:16:35 -0800486 if (tok == "-not" || tok == "!") {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900487 if (!GetNextToken(&tok) || tok.empty())
488 return NULL;
489 unique_ptr<FindCond> c(ParseFact(tok));
490 if (!c.get())
491 return NULL;
492 return new NotCond(c.release());
Dan Willemsen1e015922018-02-12 18:16:35 -0800493 } else if (tok == "(") {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900494 if (!GetNextToken(&tok) || tok.empty())
495 return NULL;
496 unique_ptr<FindCond> c(ParseExpr(tok));
Dan Willemsen1e015922018-02-12 18:16:35 -0800497 if (!GetNextToken(&tok) || tok != ")") {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900498 return NULL;
499 }
500 return c.release();
501 } else if (tok == "-name") {
502 if (!GetNextToken(&tok) || tok.empty())
503 return NULL;
504 return new NameCond(tok.as_string());
505 } else if (tok == "-type") {
506 if (!GetNextToken(&tok) || tok.empty())
507 return NULL;
508 char type;
509 if (tok == "b")
510 type = DT_BLK;
511 else if (tok == "c")
512 type = DT_CHR;
513 else if (tok == "d")
514 type = DT_DIR;
515 else if (tok == "p")
516 type = DT_FIFO;
517 else if (tok == "l")
518 type = DT_LNK;
519 else if (tok == "f")
520 type = DT_REG;
521 else if (tok == "s")
522 type = DT_SOCK;
523 else
524 return NULL;
525 return new TypeCond(type);
526 } else {
527 UngetToken(tok);
528 return NULL;
529 }
530 }
531
532 FindCond* ParseTerm(StringPiece tok) {
533 unique_ptr<FindCond> c(ParseFact(tok));
534 if (!c.get())
535 return NULL;
536 while (true) {
537 if (!GetNextToken(&tok))
538 return NULL;
Dan Willemsen23fa8432018-02-05 20:58:09 -0800539 if (tok == "-and" || tok == "-a") {
540 if (!GetNextToken(&tok) || tok.empty())
541 return NULL;
542 } else {
Dan Willemsen1e015922018-02-12 18:16:35 -0800543 if (tok != "-not" && tok != "!" && tok != "(" && tok != "-name" &&
Dan Willemsen23fa8432018-02-05 20:58:09 -0800544 tok != "-type") {
545 UngetToken(tok);
546 return c.release();
547 }
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900548 }
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900549 unique_ptr<FindCond> r(ParseFact(tok));
550 if (!r.get()) {
551 return NULL;
552 }
553 c.reset(new AndCond(c.release(), r.release()));
554 }
555 }
556
557 FindCond* ParseExpr(StringPiece tok) {
558 unique_ptr<FindCond> c(ParseTerm(tok));
559 if (!c.get())
560 return NULL;
561 while (true) {
562 if (!GetNextToken(&tok))
563 return NULL;
564 if (tok != "-or" && tok != "-o") {
565 UngetToken(tok);
566 return c.release();
567 }
568 if (!GetNextToken(&tok) || tok.empty())
569 return NULL;
570 unique_ptr<FindCond> r(ParseTerm(tok));
571 if (!r.get()) {
572 return NULL;
573 }
574 c.reset(new OrCond(c.release(), r.release()));
575 }
576 }
577
578 // <expr> ::= <term> {<or> <term>}
Dan Willemsen23fa8432018-02-05 20:58:09 -0800579 // <term> ::= <fact> {[<and>] <fact>}
Dan Willemsen1e015922018-02-12 18:16:35 -0800580 // <fact> ::= <not> <fact> | '(' <expr> ')' | <pred>
581 // <not> ::= '-not' | '!'
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900582 // <and> ::= '-and' | '-a'
583 // <or> ::= '-or' | '-o'
584 // <pred> ::= <name> | <type> | <maxdepth>
585 // <name> ::= '-name' NAME
586 // <type> ::= '-type' TYPE
587 // <maxdepth> ::= '-maxdepth' MAXDEPTH
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700588 FindCond* ParseFindCond(StringPiece tok) { return ParseExpr(tok); }
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900589
590 bool ParseFind() {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900591 fc_->type = FindCommandType::FIND;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900592 StringPiece tok;
593 while (true) {
594 if (!GetNextToken(&tok))
595 return false;
596 if (tok.empty() || tok == ";")
597 return true;
598
599 if (tok == "-L") {
600 fc_->follows_symlinks = true;
601 } else if (tok == "-prune") {
602 if (!fc_->print_cond || fc_->prune_cond)
603 return false;
604 if (!GetNextToken(&tok) || tok != "-o")
605 return false;
606 fc_->prune_cond.reset(fc_->print_cond.release());
607 } else if (tok == "-print") {
608 if (!GetNextToken(&tok) || !tok.empty())
609 return false;
610 return true;
611 } else if (tok == "-maxdepth") {
612 if (!GetNextToken(&tok) || tok.empty())
613 return false;
614 const string& depth_str = tok.as_string();
615 char* endptr;
616 long d = strtol(depth_str.c_str(), &endptr, 10);
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700617 if (endptr != depth_str.data() + depth_str.size() || d < 0 ||
618 d > INT_MAX) {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900619 return false;
620 }
621 fc_->depth = d;
Dan Willemsen1e015922018-02-12 18:16:35 -0800622 } else if (tok[0] == '-' || tok == "(" || tok == "!") {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900623 if (fc_->print_cond.get())
624 return false;
625 FindCond* c = ParseFindCond(tok);
626 if (!c)
627 return false;
628 fc_->print_cond.reset(c);
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900629 } else if (tok == "2>") {
630 if (!GetNextToken(&tok) || tok != "/dev/null") {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900631 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900632 }
633 fc_->redirect_to_devnull = true;
634 } else if (tok.find_first_of("|;&><*'\"") != string::npos) {
635 return false;
636 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700637 fc_->finddirs.push_back(tok.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900638 }
639 }
640 }
641
642 bool ParseFindLeaves() {
643 fc_->type = FindCommandType::FINDLEAVES;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900644 fc_->follows_symlinks = true;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900645 StringPiece tok;
Dan Willemsen5131f842016-09-16 20:33:31 -0700646 vector<string> findfiles;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900647 while (true) {
648 if (!GetNextToken(&tok))
649 return false;
650 if (tok.empty()) {
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700651 if (fc_->finddirs.size() == 0) {
652 // backwards compatibility
653 if (findfiles.size() < 2)
654 return false;
655 fc_->finddirs.swap(findfiles);
Dan Willemsen5131f842016-09-16 20:33:31 -0700656 fc_->print_cond.reset(new NameCond(fc_->finddirs.back()));
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700657 fc_->finddirs.pop_back();
658 } else {
659 if (findfiles.size() < 1)
660 return false;
661 for (auto& file : findfiles) {
Dan Willemsen5131f842016-09-16 20:33:31 -0700662 FindCond* cond = new NameCond(file);
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700663 if (fc_->print_cond.get()) {
664 cond = new OrCond(fc_->print_cond.release(), cond);
665 }
666 CHECK(!fc_->print_cond.get());
667 fc_->print_cond.reset(cond);
668 }
669 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900670 return true;
671 }
672
673 if (HasPrefix(tok, "--prune=")) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700674 FindCond* cond =
675 new NameCond(tok.substr(strlen("--prune=")).as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900676 if (fc_->prune_cond.get()) {
677 cond = new OrCond(fc_->prune_cond.release(), cond);
678 }
679 CHECK(!fc_->prune_cond.get());
680 fc_->prune_cond.reset(cond);
681 } else if (HasPrefix(tok, "--mindepth=")) {
682 string mindepth_str = tok.substr(strlen("--mindepth=")).as_string();
683 char* endptr;
684 long d = strtol(mindepth_str.c_str(), &endptr, 10);
685 if (endptr != mindepth_str.data() + mindepth_str.size() ||
686 d < INT_MIN || d > INT_MAX) {
687 return false;
688 }
689 fc_->mindepth = d;
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700690 } else if (HasPrefix(tok, "--dir=")) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700691 StringPiece dir = tok.substr(strlen("--dir="));
Dan Willemsen5131f842016-09-16 20:33:31 -0700692 fc_->finddirs.push_back(dir.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900693 } else if (HasPrefix(tok, "--")) {
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700694 if (g_flags.werror_find_emulator) {
695 ERROR("Unknown flag in findleaves.py: %.*s", SPF(tok));
696 } else {
697 WARN("Unknown flag in findleaves.py: %.*s", SPF(tok));
698 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900699 return false;
700 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700701 findfiles.push_back(tok.as_string());
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900702 }
703 }
704 }
705
706 bool ParseImpl() {
707 while (true) {
708 StringPiece tok;
709 if (!GetNextToken(&tok))
710 return false;
711
712 if (tok.empty())
713 return true;
714
715 if (tok == "cd") {
716 if (!GetNextToken(&tok) || tok.empty() || !fc_->chdir.empty())
717 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900718 fc_->chdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900719 if (!GetNextToken(&tok) || (tok != ";" && tok != "&&"))
720 return false;
721 } else if (tok == "if") {
722 if (!GetNextToken(&tok) || tok != "[")
723 return false;
724 if (!ParseTest())
725 return false;
726 if (!GetNextToken(&tok) || tok != "]")
727 return false;
728 if (!GetNextToken(&tok) || tok != ";")
729 return false;
730 if (!GetNextToken(&tok) || tok != "then")
731 return false;
732 has_if_ = true;
733 } else if (tok == "test") {
734 if (!fc_->chdir.empty())
735 return false;
736 if (!ParseTest())
737 return false;
738 if (!GetNextToken(&tok) || tok != "&&")
739 return false;
740 } else if (tok == "find") {
741 if (!ParseFind())
742 return false;
743 if (has_if_) {
744 if (!GetNextToken(&tok) || tok != "fi")
745 return false;
746 }
747 if (!GetNextToken(&tok) || !tok.empty())
748 return false;
749 return true;
Dan Willemsen2ac11b62018-01-31 13:54:04 -0800750 } else if (tok == "build/tools/findleaves.py" ||
751 tok == "build/make/tools/findleaves.py") {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900752 if (!ParseFindLeaves())
753 return false;
754 return true;
Shinichiro Hamaji39970312015-07-31 16:10:34 +0900755 } else {
756 return false;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900757 }
758 }
759 }
760
761 StringPiece cmd_;
762 StringPiece cur_;
763 FindCommand* fc_;
764 bool has_if_;
765 StringPiece unget_tok_;
766};
767
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900768static FindEmulator* g_instance;
769
770class FindEmulatorImpl : public FindEmulator {
771 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700772 FindEmulatorImpl() : node_cnt_(0), is_initialized_(false) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900773 g_instance = this;
774 }
775
776 virtual ~FindEmulatorImpl() = default;
777
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900778 bool CanHandle(StringPiece s) const {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700779 return (!HasPrefix(s, "../") && !HasPrefix(s, "/") &&
780 !HasPrefix(s, ".repo") && !HasPrefix(s, ".git"));
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900781 }
782
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900783 const DirentNode* FindDir(StringPiece d, bool* should_fallback) {
784 const DirentNode* r = root_->FindDir(d);
785 if (!r) {
786 *should_fallback = Exists(d);
787 }
788 return r;
789 }
790
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700791 virtual bool HandleFind(const string& cmd UNUSED,
792 const FindCommand& fc,
793 const Loc& loc,
794 string* out) override {
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900795 if (!CanHandle(fc.chdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700796 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", SPF(fc.chdir),
797 cmd.c_str());
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900798 return false;
799 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900800
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900801 if (!is_initialized_) {
802 ScopedTimeReporter tr("init find emulator time");
803 root_.reset(ConstructDirectoryTree(""));
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800804 if (!root_) {
805 ERROR("FindEmulator: Cannot open root directory");
806 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900807 ResolveSymlinks();
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900808 LOG_STAT("%d find nodes", node_cnt_);
809 is_initialized_ = true;
810 }
811
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900812 if (!fc.testdir.empty()) {
813 if (!CanHandle(fc.testdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700814 LOG("FindEmulator: Cannot handle test dir (%.*s): %s", SPF(fc.testdir),
815 cmd.c_str());
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900816 return false;
817 }
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900818 bool should_fallback = false;
819 if (!FindDir(fc.testdir, &should_fallback)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700820 LOG("FindEmulator: Test dir (%.*s) not found: %s", SPF(fc.testdir),
821 cmd.c_str());
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900822 return !should_fallback;
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900823 }
824 }
825
Dan Willemsen4be0f092017-07-25 20:26:54 -0700826 const DirentNode* root = root_.get();
827
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900828 if (!fc.chdir.empty()) {
829 if (!CanHandle(fc.chdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700830 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", SPF(fc.chdir),
831 cmd.c_str());
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900832 return false;
833 }
Dan Willemsen4be0f092017-07-25 20:26:54 -0700834 root = root->FindDir(fc.chdir);
835 if (!root) {
836 if (Exists(fc.chdir))
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900837 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900838 if (!fc.redirect_to_devnull) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700839 FIND_WARN_LOC(loc,
840 "FindEmulator: cd: %.*s: No such file or directory",
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700841 SPF(fc.chdir));
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900842 }
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900843 return true;
844 }
845 }
846
Dan Willemsen439f6f12016-10-19 01:13:54 -0700847 vector<string> results;
Dan Willemsen5131f842016-09-16 20:33:31 -0700848 for (const string& finddir : fc.finddirs) {
Dan Willemsen4be0f092017-07-25 20:26:54 -0700849 if (!CanHandle(finddir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700850 LOG("FindEmulator: Cannot handle find dir (%s): %s", finddir.c_str(),
851 cmd.c_str());
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900852 return false;
853 }
854
Dan Willemsen4be0f092017-07-25 20:26:54 -0700855 const DirentNode* base;
856 base = root->FindDir(finddir);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900857 if (!base) {
Dan Willemsen4be0f092017-07-25 20:26:54 -0700858 if (Exists(finddir)) {
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900859 return false;
860 }
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900861 if (!fc.redirect_to_devnull) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700862 FIND_WARN_LOC(loc,
863 "FindEmulator: find: `%s': No such file or directory",
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700864 ConcatDir(fc.chdir, finddir).c_str());
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900865 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900866 continue;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900867 }
868
Dan Willemsen5131f842016-09-16 20:33:31 -0700869 string path = finddir;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900870 unordered_map<const DirentNode*, string> cur_read_dirs;
Dan Willemsen692e64e2017-02-22 15:38:33 -0800871 if (!base->RunFind(fc, loc, 0, &path, &cur_read_dirs, results)) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900872 LOG("FindEmulator: RunFind failed: %s", cmd.c_str());
873 return false;
874 }
875 }
876
Dan Willemsen439f6f12016-10-19 01:13:54 -0700877 if (results.size() > 0) {
878 // Calculate and reserve necessary space in out
879 size_t new_length = 0;
880 for (const string& result : results) {
881 new_length += result.size() + 1;
882 }
883 out->reserve(out->size() + new_length - 1);
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900884
Dan Willemsen439f6f12016-10-19 01:13:54 -0700885 if (fc.type == FindCommandType::FINDLEAVES) {
886 sort(results.begin(), results.end());
887 }
888
889 WordWriter writer(out);
890 for (const string& result : results) {
891 writer.Write(result);
892 }
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900893 }
894
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900895 LOG("FindEmulator: OK");
896 return true;
897 }
898
899 private:
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900900 static unsigned char GetDtTypeFromStat(const struct stat& st) {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900901 if (S_ISREG(st.st_mode)) {
902 return DT_REG;
903 } else if (S_ISDIR(st.st_mode)) {
904 return DT_DIR;
905 } else if (S_ISCHR(st.st_mode)) {
906 return DT_CHR;
907 } else if (S_ISBLK(st.st_mode)) {
908 return DT_BLK;
909 } else if (S_ISFIFO(st.st_mode)) {
910 return DT_FIFO;
911 } else if (S_ISLNK(st.st_mode)) {
912 return DT_LNK;
913 } else if (S_ISSOCK(st.st_mode)) {
914 return DT_SOCK;
915 } else {
916 return DT_UNKNOWN;
917 }
918 }
919
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900920 static unsigned char GetDtType(const string& path) {
921 struct stat st;
922 if (lstat(path.c_str(), &st)) {
923 PERROR("stat for %s", path.c_str());
924 }
925 return GetDtTypeFromStat(st);
926 }
927
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900928 DirentNode* ConstructDirectoryTree(const string& path) {
929 DIR* dir = opendir(path.empty() ? "." : path.c_str());
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800930 if (!dir) {
Dan Willemsen09279ad2017-05-12 13:57:40 -0700931 if (errno == ENOENT || errno == EACCES) {
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800932 LOG("opendir failed: %s", path.c_str());
933 return NULL;
934 } else {
935 PERROR("opendir failed: %s", path.c_str());
936 }
937 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900938
939 DirentDirNode* n = new DirentDirNode(path);
940
941 struct dirent* ent;
942 while ((ent = readdir(dir)) != NULL) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700943 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") ||
944 !strcmp(ent->d_name, ".repo") || !strcmp(ent->d_name, ".git"))
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900945 continue;
946
947 string npath = path;
948 if (!path.empty())
949 npath += '/';
950 npath += ent->d_name;
951
952 DirentNode* c = NULL;
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900953 auto d_type = ent->d_type;
954 if (d_type == DT_UNKNOWN) {
955 d_type = GetDtType(npath);
956 CHECK(d_type != DT_UNKNOWN);
957 }
958 if (d_type == DT_DIR) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900959 c = ConstructDirectoryTree(npath);
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800960 if (c == NULL) {
961 continue;
962 }
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900963 } else if (d_type == DT_LNK) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900964 auto s = new DirentSymlinkNode(npath);
965 symlinks_.push_back(make_pair(npath, s));
966 c = s;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900967 } else {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900968 c = new DirentFileNode(npath, d_type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900969 }
Shinichiro Hamaji802ef962015-07-06 15:37:40 +0900970 node_cnt_++;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900971 n->Add(ent->d_name, c);
972 }
973 closedir(dir);
974
975 return n;
976 }
977
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900978 void ResolveSymlinks() {
Shinichiro Hamajif2d31722015-10-15 17:30:32 +0900979 vector<pair<string, DirentSymlinkNode*>> symlinks;
980 symlinks.swap(symlinks_);
981 for (const auto& p : symlinks) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900982 const string& path = p.first;
983 DirentSymlinkNode* s = p.second;
984
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700985 char buf[PATH_MAX + 1];
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900986 buf[PATH_MAX] = 0;
987 ssize_t len = readlink(path.c_str(), buf, PATH_MAX);
988 if (len < 0) {
989 WARN("readlink failed: %s", path.c_str());
990 continue;
991 }
992 buf[len] = 0;
993
994 struct stat st;
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900995 unsigned char type = DT_UNKNOWN;
996 if (stat(path.c_str(), &st) == 0) {
997 type = GetDtTypeFromStat(st);
998 } else {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900999 s->set_errno(errno);
1000 LOG("stat failed: %s: %s", path.c_str(), strerror(errno));
1001 }
1002
1003 if (*buf != '/') {
1004 const string npath = ConcatDir(Dirname(path), buf);
1005 bool should_fallback = false;
1006 const DirentNode* to = FindDir(npath, &should_fallback);
1007 if (to) {
1008 s->set_to(to);
1009 continue;
1010 }
1011 }
1012
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001013 if (type == DT_DIR) {
1014 if (path.find('/') == string::npos) {
Dan Willemsen6f3f0f42017-02-23 00:10:04 -08001015 DirentNode* dir = ConstructDirectoryTree(path);
1016 if (dir != NULL) {
1017 s->set_to(dir);
1018 } else {
1019 s->set_errno(errno);
1020 }
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001021 }
1022 } else if (type != DT_LNK && type != DT_UNKNOWN) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001023 s->set_to(new DirentFileNode(path, type));
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001024 }
1025 }
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001026
1027 if (!symlinks_.empty())
1028 ResolveSymlinks();
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001029 }
1030
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001031 unique_ptr<DirentNode> root_;
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001032 vector<pair<string, DirentSymlinkNode*>> symlinks_;
Shinichiro Hamaji802ef962015-07-06 15:37:40 +09001033 int node_cnt_;
Shinichiro Hamajia87bd132015-07-31 12:48:22 +09001034 bool is_initialized_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001035};
1036
1037} // namespace
1038
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001039FindCommand::FindCommand()
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001040 : follows_symlinks(false),
1041 depth(INT_MAX),
1042 mindepth(INT_MIN),
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +09001043 redirect_to_devnull(false),
Dan Willemsen439f6f12016-10-19 01:13:54 -07001044 found_files(new vector<string>()),
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001045 read_dirs(new unordered_set<string>()) {}
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001046
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001047FindCommand::~FindCommand() {}
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +09001048
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001049bool FindCommand::Parse(const string& cmd) {
1050 FindCommandParser fcp(cmd, this);
Dan Willemsen2ac11b62018-01-31 13:54:04 -08001051 if (!HasWord(cmd, "find") && !HasWord(cmd, "build/tools/findleaves.py") &&
1052 !HasWord(cmd, "build/make/tools/findleaves.py"))
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001053 return false;
1054
1055 if (!fcp.Parse())
1056 return false;
1057
Shinichiro Hamaji3498f342015-08-11 14:23:12 +09001058 NormalizePath(&chdir);
1059 NormalizePath(&testdir);
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001060 if (finddirs.empty())
1061 finddirs.push_back(".");
1062 return true;
1063}
1064
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001065FindEmulator* FindEmulator::Get() {
1066 return g_instance;
1067}
1068
1069void InitFindEmulator() {
1070 new FindEmulatorImpl();
1071}