blob: 5d9cab6f7bd4dc74d44ba409697979dedcd215e5 [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
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090038class FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090039 public:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090040 virtual ~FindCond() = default;
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090041 virtual bool IsTrue(const string& path, unsigned char type) const = 0;
Dan Willemsen439f6f12016-10-19 01:13:54 -070042 virtual bool Countable() const = 0;
43 virtual unsigned Count() const = 0;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090044 protected:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090045 FindCond() = default;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090046};
47
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090048namespace {
49
50class NameCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090051 public:
52 explicit NameCond(const string& n)
53 : name_(n) {
Dan Willemsen439f6f12016-10-19 01:13:54 -070054 has_wildcard_ = (n.find_first_of("?*[") != string::npos);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090055 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090056 virtual bool IsTrue(const string& path, unsigned char) const override {
57 return fnmatch(name_.c_str(), Basename(path).data(), 0) == 0;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090058 }
Dan Willemsen439f6f12016-10-19 01:13:54 -070059 virtual bool Countable() const override {
60 return !has_wildcard_;
61 }
62 virtual unsigned Count() const override {
63 return 1;
64 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090065 private:
66 string name_;
Dan Willemsen439f6f12016-10-19 01:13:54 -070067 bool has_wildcard_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090068};
69
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090070class TypeCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090071 public:
72 explicit TypeCond(unsigned char t)
73 : type_(t) {
74 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090075 virtual bool IsTrue(const string&, unsigned char type) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090076 return type == type_;
77 }
Dan Willemsen439f6f12016-10-19 01:13:54 -070078 virtual bool Countable() const override {
79 return false;
80 }
81 virtual unsigned Count() const override {
82 return 0;
83 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090084 private:
85 unsigned char type_;
86};
87
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090088class NotCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090089 public:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +090090 NotCond(FindCond* c)
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090091 : c_(c) {
92 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +090093 virtual bool IsTrue(const string& path, unsigned char type) const override {
94 return !c_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +090095 }
Dan Willemsen439f6f12016-10-19 01:13:54 -070096 virtual bool Countable() const override {
97 return false;
98 }
99 virtual unsigned Count() const override {
100 return 0;
101 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900102 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900103 unique_ptr<FindCond> c_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900104};
105
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900106class AndCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900107 public:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900108 AndCond(FindCond* c1, FindCond* c2)
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900109 : c1_(c1), c2_(c2) {
110 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900111 virtual bool IsTrue(const string& path, unsigned char type) const override {
112 if (c1_->IsTrue(path, type))
113 return c2_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900114 return false;
115 }
Dan Willemsen439f6f12016-10-19 01:13:54 -0700116 virtual bool Countable() const override {
117 return false;
118 }
119 virtual unsigned Count() const override {
120 return 0;
121 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900122 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900123 unique_ptr<FindCond> c1_, c2_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900124};
125
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900126class OrCond : public FindCond {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900127 public:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900128 OrCond(FindCond* c1, FindCond* c2)
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900129 : c1_(c1), c2_(c2) {
130 }
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900131 virtual bool IsTrue(const string& path, unsigned char type) const override {
132 if (!c1_->IsTrue(path, type))
133 return c2_->IsTrue(path, type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900134 return true;
135 }
Dan Willemsen439f6f12016-10-19 01:13:54 -0700136 virtual bool Countable() const override {
137 return c1_->Countable() && c2_->Countable();;
138 }
139 virtual unsigned Count() const override {
140 return c1_->Count() + c2_->Count();
141 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900142 private:
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +0900143 unique_ptr<FindCond> c1_, c2_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900144};
145
146class DirentNode {
147 public:
148 virtual ~DirentNode() = default;
149
150 virtual const DirentNode* FindDir(StringPiece) const {
151 return NULL;
152 }
Shinichiro Hamajid45a09d2015-07-02 00:35:57 +0900153 virtual bool RunFind(const FindCommand& fc, int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900154 string* path,
155 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700156 vector<string>& out) const = 0;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900157
158 virtual bool IsDirectory() const = 0;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900159
160 const string& base() const { return base_; }
161
162 protected:
163 explicit DirentNode(const string& name) {
164 base_ = Basename(name).as_string();
165 }
166
167 void PrintIfNecessary(const FindCommand& fc,
168 const string& path,
169 unsigned char type,
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900170 int d,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700171 vector<string>& out) const {
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900172 if (fc.print_cond && !fc.print_cond->IsTrue(path, type))
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900173 return;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900174 if (d < fc.mindepth)
175 return;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700176 out.push_back(path);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900177 }
178
179 string base_;
180};
181
182class DirentFileNode : public DirentNode {
183 public:
184 DirentFileNode(const string& name, unsigned char type)
185 : DirentNode(name), type_(type) {
186 }
187
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900188 virtual bool RunFind(const FindCommand& fc, int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900189 string* path,
190 unordered_map<const DirentNode*, string>*,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700191 vector<string>& out) const override {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900192 PrintIfNecessary(fc, *path, type_, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900193 return true;
194 }
195
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900196 virtual bool IsDirectory() const override { return false; }
197
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900198 private:
199 unsigned char type_;
200};
201
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900202struct ScopedReadDirTracker {
203 public:
204 ScopedReadDirTracker(const DirentNode* n,
205 const string& path,
206 unordered_map<const DirentNode*, string>* cur_read_dirs)
207 : n_(NULL), cur_read_dirs_(cur_read_dirs) {
208 const auto& p = cur_read_dirs->emplace(n, path);
209 if (p.second) {
210 n_ = n;
211 } else {
212 conflicted_ = p.first->second;
213 }
214 }
215
216 ~ScopedReadDirTracker() {
217 if (n_)
218 cur_read_dirs_->erase(n_);
219 }
220
221 bool ok() const { return conflicted_.empty(); }
222 const string& conflicted() const { return conflicted_; }
223
224 private:
225 string conflicted_;
226 const DirentNode* n_;
227 unordered_map<const DirentNode*, string>* cur_read_dirs_;
228};
229
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900230class DirentDirNode : public DirentNode {
231 public:
232 explicit DirentDirNode(const string& name)
233 : DirentNode(name) {
234 }
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900235
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900236 ~DirentDirNode() {
237 for (auto& p : children_) {
238 delete p.second;
239 }
240 }
241
Shinichiro Hamaji4b351ab2016-02-12 19:42:30 +0900242 virtual const DirentNode* FindDir(StringPiece d) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900243 if (d.empty() || d == ".")
244 return this;
245 size_t index = d.find('/');
246 const string& p = d.substr(0, index).as_string();
Dan Willemsen48d6e8c2015-08-05 14:38:34 -0700247 for (auto& child : children_) {
248 if (p == child.first) {
249 if (index == string::npos)
250 return child.second;
251 StringPiece nd = d.substr(index + 1);
252 return child.second->FindDir(nd);
253 }
254 }
255 return NULL;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900256 }
257
Shinichiro Hamajid45a09d2015-07-02 00:35:57 +0900258 virtual bool RunFind(const FindCommand& fc, int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900259 string* path,
260 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700261 vector<string>& out) const override {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900262 ScopedReadDirTracker srdt(this, *path, cur_read_dirs);
263 if (!srdt.ok()) {
264 fprintf(stderr, "FindEmulator: find: File system loop detected; `%s' is "
265 "part of the same file system loop as `%s'.\n",
266 path->c_str(), srdt.conflicted().c_str());
267 return true;
268 }
269
270 fc.read_dirs->insert(*path);
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +0900271
Shinichiro Hamaji31505ba2015-10-15 17:44:51 +0900272 if (fc.prune_cond && fc.prune_cond->IsTrue(*path, DT_DIR)) {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900273 if (fc.type != FindCommandType::FINDLEAVES) {
Dan Willemsen439f6f12016-10-19 01:13:54 -0700274 out.push_back(*path);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900275 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900276 return true;
277 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900278
279 PrintIfNecessary(fc, *path, DT_DIR, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900280
Shinichiro Hamajid45a09d2015-07-02 00:35:57 +0900281 if (d >= fc.depth)
282 return true;
283
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900284 size_t orig_path_size = path->size();
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900285 if (fc.type == FindCommandType::FINDLEAVES) {
Dan Willemsen439f6f12016-10-19 01:13:54 -0700286 size_t orig_out_size = out.size();
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900287 for (const auto& p : children_) {
288 DirentNode* c = p.second;
289 // We will handle directories later.
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900290 if (c->IsDirectory())
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900291 continue;
292 if ((*path)[path->size()-1] != '/')
293 *path += '/';
294 *path += c->base();
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900295 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900296 return false;
297 path->resize(orig_path_size);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900298 }
299
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700300 // Found a leaf, stop the search.
Dan Willemsen439f6f12016-10-19 01:13:54 -0700301 if (orig_out_size != out.size()) {
302 // If we've found all possible files in this directory, we don't need
303 // to add a regen dependency on the directory, we just need to ensure
304 // that the files are not removed.
305 if (fc.print_cond->Countable() &&
306 fc.print_cond->Count() == out.size() - orig_out_size) {
307 fc.read_dirs->erase(*path);
308 for (unsigned i = orig_out_size; i < out.size(); i++) {
309 fc.found_files->push_back(out[i]);
310 }
311 }
312
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700313 return true;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700314 }
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700315
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900316 for (const auto& p : children_) {
317 DirentNode* c = p.second;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900318 if (!c->IsDirectory())
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900319 continue;
320 if ((*path)[path->size()-1] != '/')
321 *path += '/';
322 *path += c->base();
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900323 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900324 return false;
325 path->resize(orig_path_size);
326 }
327 } else {
328 for (const auto& p : children_) {
329 DirentNode* c = p.second;
330 if ((*path)[path->size()-1] != '/')
331 *path += '/';
332 *path += c->base();
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900333 if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900334 return false;
335 path->resize(orig_path_size);
336 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900337 }
338 return true;
339 }
340
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900341 virtual bool IsDirectory() const override { return true; }
342
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900343 void Add(const string& name, DirentNode* c) {
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900344 children_.emplace(children_.end(), name, c);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900345 }
346
347 private:
Dan Willemsen48d6e8c2015-08-05 14:38:34 -0700348 vector<pair<string, DirentNode*>> children_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900349};
350
351class DirentSymlinkNode : public DirentNode {
352 public:
353 explicit DirentSymlinkNode(const string& name)
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900354 : DirentNode(name), to_(NULL), errno_(0) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900355 }
356
Shinichiro Hamaji4b351ab2016-02-12 19:42:30 +0900357 virtual const DirentNode* FindDir(StringPiece d) const override {
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900358 if (errno_ == 0 && to_)
359 return to_->FindDir(d);
360 return NULL;
361 }
362
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900363 virtual bool RunFind(const FindCommand& fc, int d,
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900364 string* path,
365 unordered_map<const DirentNode*, string>* cur_read_dirs,
Dan Willemsen439f6f12016-10-19 01:13:54 -0700366 vector<string>& out) const override {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900367 unsigned char type = DT_LNK;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900368 if (fc.follows_symlinks && errno_ != ENOENT) {
369 if (errno_) {
370 if (fc.type != FindCommandType::FINDLEAVES) {
371 fprintf(stderr, "FindEmulator: find: `%s': %s\n",
372 path->c_str(), strerror(errno_));
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900373 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900374 return true;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900375 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900376
377 if (!to_) {
378 LOG("FindEmulator does not support %s", path->c_str());
379 return false;
380 }
381
382 return to_->RunFind(fc, d, path, cur_read_dirs, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900383 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900384 PrintIfNecessary(fc, *path, type, d, out);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900385 return true;
386 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900387
388 virtual bool IsDirectory() const override {
389 return errno_ == 0 && to_ && to_->IsDirectory();
390 }
391
392 void set_to(const DirentNode* to) {
393 to_ = to;
394 }
395
396 void set_errno(int e) {
397 errno_ = e;
398 }
399
400 private:
401 const DirentNode* to_;
402 int errno_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900403};
404
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900405class FindCommandParser {
406 public:
407 FindCommandParser(StringPiece cmd, FindCommand* fc)
408 : cmd_(cmd), fc_(fc), has_if_(false) {
409 }
410
411 bool Parse() {
412 cur_ = cmd_;
413 if (!ParseImpl()) {
414 LOG("FindEmulator: Unsupported find command: %.*s", SPF(cmd_));
415 return false;
416 }
417 CHECK(TrimLeftSpace(cur_).empty());
418 return true;
419 }
420
421 private:
422 bool GetNextToken(StringPiece* tok) {
423 if (!unget_tok_.empty()) {
424 *tok = unget_tok_;
425 unget_tok_.clear();
426 return true;
427 }
428
429 cur_ = TrimLeftSpace(cur_);
430
431 if (cur_[0] == ';') {
432 *tok = cur_.substr(0, 1);
433 cur_ = cur_.substr(1);
434 return true;
435 }
436 if (cur_[0] == '&') {
437 if (cur_.get(1) != '&') {
438 return false;
439 }
440 *tok = cur_.substr(0, 2);
441 cur_ = cur_.substr(2);
442 return true;
443 }
444
445 size_t i = 0;
446 while (i < cur_.size() && !isspace(cur_[i]) &&
447 cur_[i] != ';' && cur_[i] != '&') {
448 i++;
449 }
450
451 *tok = cur_.substr(0, i);
452 cur_ = cur_.substr(i);
453
454 const char c = tok->get(0);
455 if (c == '\'' || c == '"') {
456 if (tok->size() < 2 || (*tok)[tok->size()-1] != c)
457 return false;
458 *tok = tok->substr(1, tok->size() - 2);
459 return true;
460 }
461
462 return true;
463 }
464
465 void UngetToken(StringPiece tok) {
466 CHECK(unget_tok_.empty());
467 if (!tok.empty())
468 unget_tok_ = tok;
469 }
470
471 bool ParseTest() {
472 if (has_if_ || !fc_->testdir.empty())
473 return false;
474 StringPiece tok;
475 if (!GetNextToken(&tok) || tok != "-d")
476 return false;
477 if (!GetNextToken(&tok) || tok.empty())
478 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900479 fc_->testdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900480 return true;
481 }
482
483 FindCond* ParseFact(StringPiece tok) {
484 if (tok == "-not" || tok == "\\!") {
485 if (!GetNextToken(&tok) || tok.empty())
486 return NULL;
487 unique_ptr<FindCond> c(ParseFact(tok));
488 if (!c.get())
489 return NULL;
490 return new NotCond(c.release());
491 } else if (tok == "\\(") {
492 if (!GetNextToken(&tok) || tok.empty())
493 return NULL;
494 unique_ptr<FindCond> c(ParseExpr(tok));
495 if (!GetNextToken(&tok) || tok != "\\)") {
496 return NULL;
497 }
498 return c.release();
499 } else if (tok == "-name") {
500 if (!GetNextToken(&tok) || tok.empty())
501 return NULL;
502 return new NameCond(tok.as_string());
503 } else if (tok == "-type") {
504 if (!GetNextToken(&tok) || tok.empty())
505 return NULL;
506 char type;
507 if (tok == "b")
508 type = DT_BLK;
509 else if (tok == "c")
510 type = DT_CHR;
511 else if (tok == "d")
512 type = DT_DIR;
513 else if (tok == "p")
514 type = DT_FIFO;
515 else if (tok == "l")
516 type = DT_LNK;
517 else if (tok == "f")
518 type = DT_REG;
519 else if (tok == "s")
520 type = DT_SOCK;
521 else
522 return NULL;
523 return new TypeCond(type);
524 } else {
525 UngetToken(tok);
526 return NULL;
527 }
528 }
529
530 FindCond* ParseTerm(StringPiece tok) {
531 unique_ptr<FindCond> c(ParseFact(tok));
532 if (!c.get())
533 return NULL;
534 while (true) {
535 if (!GetNextToken(&tok))
536 return NULL;
537 if (tok != "-and" && tok != "-a") {
538 UngetToken(tok);
539 return c.release();
540 }
541 if (!GetNextToken(&tok) || tok.empty())
542 return NULL;
543 unique_ptr<FindCond> r(ParseFact(tok));
544 if (!r.get()) {
545 return NULL;
546 }
547 c.reset(new AndCond(c.release(), r.release()));
548 }
549 }
550
551 FindCond* ParseExpr(StringPiece tok) {
552 unique_ptr<FindCond> c(ParseTerm(tok));
553 if (!c.get())
554 return NULL;
555 while (true) {
556 if (!GetNextToken(&tok))
557 return NULL;
558 if (tok != "-or" && tok != "-o") {
559 UngetToken(tok);
560 return c.release();
561 }
562 if (!GetNextToken(&tok) || tok.empty())
563 return NULL;
564 unique_ptr<FindCond> r(ParseTerm(tok));
565 if (!r.get()) {
566 return NULL;
567 }
568 c.reset(new OrCond(c.release(), r.release()));
569 }
570 }
571
572 // <expr> ::= <term> {<or> <term>}
573 // <term> ::= <fact> {<and> <fact>}
574 // <fact> ::= <not> <fact> | '\(' <expr> '\)' | <pred>
575 // <not> ::= '-not' | '\!'
576 // <and> ::= '-and' | '-a'
577 // <or> ::= '-or' | '-o'
578 // <pred> ::= <name> | <type> | <maxdepth>
579 // <name> ::= '-name' NAME
580 // <type> ::= '-type' TYPE
581 // <maxdepth> ::= '-maxdepth' MAXDEPTH
582 FindCond* ParseFindCond(StringPiece tok) {
583 return ParseExpr(tok);
584 }
585
586 bool ParseFind() {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900587 fc_->type = FindCommandType::FIND;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900588 StringPiece tok;
589 while (true) {
590 if (!GetNextToken(&tok))
591 return false;
592 if (tok.empty() || tok == ";")
593 return true;
594
595 if (tok == "-L") {
596 fc_->follows_symlinks = true;
597 } else if (tok == "-prune") {
598 if (!fc_->print_cond || fc_->prune_cond)
599 return false;
600 if (!GetNextToken(&tok) || tok != "-o")
601 return false;
602 fc_->prune_cond.reset(fc_->print_cond.release());
603 } else if (tok == "-print") {
604 if (!GetNextToken(&tok) || !tok.empty())
605 return false;
606 return true;
607 } else if (tok == "-maxdepth") {
608 if (!GetNextToken(&tok) || tok.empty())
609 return false;
610 const string& depth_str = tok.as_string();
611 char* endptr;
612 long d = strtol(depth_str.c_str(), &endptr, 10);
613 if (endptr != depth_str.data() + depth_str.size() ||
614 d < 0 || d > INT_MAX) {
615 return false;
616 }
617 fc_->depth = d;
618 } else if (tok[0] == '-' || tok == "\\(") {
619 if (fc_->print_cond.get())
620 return false;
621 FindCond* c = ParseFindCond(tok);
622 if (!c)
623 return false;
624 fc_->print_cond.reset(c);
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900625 } else if (tok == "2>") {
626 if (!GetNextToken(&tok) || tok != "/dev/null") {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900627 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900628 }
629 fc_->redirect_to_devnull = true;
630 } else if (tok.find_first_of("|;&><*'\"") != string::npos) {
631 return false;
632 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700633 fc_->finddirs.push_back(tok.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900634 }
635 }
636 }
637
638 bool ParseFindLeaves() {
639 fc_->type = FindCommandType::FINDLEAVES;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900640 fc_->follows_symlinks = true;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900641 StringPiece tok;
Dan Willemsen5131f842016-09-16 20:33:31 -0700642 vector<string> findfiles;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900643 while (true) {
644 if (!GetNextToken(&tok))
645 return false;
646 if (tok.empty()) {
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700647 if (fc_->finddirs.size() == 0) {
648 // backwards compatibility
649 if (findfiles.size() < 2)
650 return false;
651 fc_->finddirs.swap(findfiles);
Dan Willemsen5131f842016-09-16 20:33:31 -0700652 fc_->print_cond.reset(new NameCond(fc_->finddirs.back()));
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700653 fc_->finddirs.pop_back();
654 } else {
655 if (findfiles.size() < 1)
656 return false;
657 for (auto& file : findfiles) {
Dan Willemsen5131f842016-09-16 20:33:31 -0700658 FindCond* cond = new NameCond(file);
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700659 if (fc_->print_cond.get()) {
660 cond = new OrCond(fc_->print_cond.release(), cond);
661 }
662 CHECK(!fc_->print_cond.get());
663 fc_->print_cond.reset(cond);
664 }
665 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900666 return true;
667 }
668
669 if (HasPrefix(tok, "--prune=")) {
670 FindCond* cond = new NameCond(
671 tok.substr(strlen("--prune=")).as_string());
672 if (fc_->prune_cond.get()) {
673 cond = new OrCond(fc_->prune_cond.release(), cond);
674 }
675 CHECK(!fc_->prune_cond.get());
676 fc_->prune_cond.reset(cond);
677 } else if (HasPrefix(tok, "--mindepth=")) {
678 string mindepth_str = tok.substr(strlen("--mindepth=")).as_string();
679 char* endptr;
680 long d = strtol(mindepth_str.c_str(), &endptr, 10);
681 if (endptr != mindepth_str.data() + mindepth_str.size() ||
682 d < INT_MIN || d > INT_MAX) {
683 return false;
684 }
685 fc_->mindepth = d;
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700686 } else if (HasPrefix(tok, "--dir=")) {
687 StringPiece dir= tok.substr(strlen("--dir="));
Dan Willemsen5131f842016-09-16 20:33:31 -0700688 fc_->finddirs.push_back(dir.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900689 } else if (HasPrefix(tok, "--")) {
690 WARN("Unknown flag in findleaves.py: %.*s", SPF(tok));
691 return false;
692 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700693 findfiles.push_back(tok.as_string());
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900694 }
695 }
696 }
697
698 bool ParseImpl() {
699 while (true) {
700 StringPiece tok;
701 if (!GetNextToken(&tok))
702 return false;
703
704 if (tok.empty())
705 return true;
706
707 if (tok == "cd") {
708 if (!GetNextToken(&tok) || tok.empty() || !fc_->chdir.empty())
709 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900710 fc_->chdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900711 if (!GetNextToken(&tok) || (tok != ";" && tok != "&&"))
712 return false;
713 } else if (tok == "if") {
714 if (!GetNextToken(&tok) || tok != "[")
715 return false;
716 if (!ParseTest())
717 return false;
718 if (!GetNextToken(&tok) || tok != "]")
719 return false;
720 if (!GetNextToken(&tok) || tok != ";")
721 return false;
722 if (!GetNextToken(&tok) || tok != "then")
723 return false;
724 has_if_ = true;
725 } else if (tok == "test") {
726 if (!fc_->chdir.empty())
727 return false;
728 if (!ParseTest())
729 return false;
730 if (!GetNextToken(&tok) || tok != "&&")
731 return false;
732 } else if (tok == "find") {
733 if (!ParseFind())
734 return false;
735 if (has_if_) {
736 if (!GetNextToken(&tok) || tok != "fi")
737 return false;
738 }
739 if (!GetNextToken(&tok) || !tok.empty())
740 return false;
741 return true;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900742 } else if (tok == "build/tools/findleaves.py") {
743 if (!ParseFindLeaves())
744 return false;
745 return true;
Shinichiro Hamaji39970312015-07-31 16:10:34 +0900746 } else {
747 return false;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900748 }
749 }
750 }
751
752 StringPiece cmd_;
753 StringPiece cur_;
754 FindCommand* fc_;
755 bool has_if_;
756 StringPiece unget_tok_;
757};
758
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900759static FindEmulator* g_instance;
760
761class FindEmulatorImpl : public FindEmulator {
762 public:
Shinichiro Hamaji802ef962015-07-06 15:37:40 +0900763 FindEmulatorImpl()
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900764 : node_cnt_(0), is_initialized_(false) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900765 g_instance = this;
766 }
767
768 virtual ~FindEmulatorImpl() = default;
769
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900770 bool CanHandle(StringPiece s) const {
771 return (!HasPrefix(s, "../") &&
772 !HasPrefix(s, "/") &&
773 !HasPrefix(s, ".repo") &&
Dan Willemsen85433272016-10-18 17:38:28 -0700774 !HasPrefix(s, ".git"));
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900775 }
776
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900777 const DirentNode* FindDir(StringPiece d, bool* should_fallback) {
778 const DirentNode* r = root_->FindDir(d);
779 if (!r) {
780 *should_fallback = Exists(d);
781 }
782 return r;
783 }
784
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +0900785 virtual bool HandleFind(const string& cmd UNUSED, const FindCommand& fc,
786 string* out) override {
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900787 if (!CanHandle(fc.chdir)) {
788 LOG("FindEmulator: Cannot handle chdir (%.*s): %s",
789 SPF(fc.chdir), cmd.c_str());
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900790 return false;
791 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900792
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900793 if (!is_initialized_) {
794 ScopedTimeReporter tr("init find emulator time");
795 root_.reset(ConstructDirectoryTree(""));
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900796 ResolveSymlinks();
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900797 LOG_STAT("%d find nodes", node_cnt_);
798 is_initialized_ = true;
799 }
800
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900801 if (!fc.testdir.empty()) {
802 if (!CanHandle(fc.testdir)) {
803 LOG("FindEmulator: Cannot handle test dir (%.*s): %s",
804 SPF(fc.testdir), cmd.c_str());
805 return false;
806 }
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900807 bool should_fallback = false;
808 if (!FindDir(fc.testdir, &should_fallback)) {
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900809 LOG("FindEmulator: Test dir (%.*s) not found: %s",
810 SPF(fc.testdir), cmd.c_str());
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900811 return !should_fallback;
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900812 }
813 }
814
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900815 if (!fc.chdir.empty()) {
816 if (!CanHandle(fc.chdir)) {
817 LOG("FindEmulator: Cannot handle chdir (%.*s): %s",
818 SPF(fc.chdir), cmd.c_str());
819 return false;
820 }
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900821 bool should_fallback = false;
822 if (!FindDir(fc.chdir, &should_fallback)) {
823 if (should_fallback)
824 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900825 if (!fc.redirect_to_devnull) {
826 fprintf(stderr,
827 "FindEmulator: cd: %.*s: No such file or directory\n",
828 SPF(fc.chdir));
829 }
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900830 return true;
831 }
832 }
833
Dan Willemsen439f6f12016-10-19 01:13:54 -0700834 vector<string> results;
Dan Willemsen5131f842016-09-16 20:33:31 -0700835 for (const string& finddir : fc.finddirs) {
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900836 const string dir = ConcatDir(fc.chdir, finddir);
837
838 if (!CanHandle(dir)) {
839 LOG("FindEmulator: Cannot handle find dir (%s): %s",
840 dir.c_str(), cmd.c_str());
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900841 return false;
842 }
843
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900844 bool should_fallback = false;
845 const DirentNode* base = FindDir(dir, &should_fallback);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900846 if (!base) {
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900847 if (should_fallback) {
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900848 return false;
849 }
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900850 if (!fc.redirect_to_devnull) {
851 fprintf(stderr,
852 "FindEmulator: find: `%s': No such file or directory\n",
853 ConcatDir(fc.chdir, finddir).c_str());
854 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900855 continue;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900856 }
857
Dan Willemsen5131f842016-09-16 20:33:31 -0700858 string path = finddir;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900859 unordered_map<const DirentNode*, string> cur_read_dirs;
Dan Willemsen439f6f12016-10-19 01:13:54 -0700860 if (!base->RunFind(fc, 0, &path, &cur_read_dirs, results)) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900861 LOG("FindEmulator: RunFind failed: %s", cmd.c_str());
862 return false;
863 }
864 }
865
Dan Willemsen439f6f12016-10-19 01:13:54 -0700866 if (results.size() > 0) {
867 // Calculate and reserve necessary space in out
868 size_t new_length = 0;
869 for (const string& result : results) {
870 new_length += result.size() + 1;
871 }
872 out->reserve(out->size() + new_length - 1);
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900873
Dan Willemsen439f6f12016-10-19 01:13:54 -0700874 if (fc.type == FindCommandType::FINDLEAVES) {
875 sort(results.begin(), results.end());
876 }
877
878 WordWriter writer(out);
879 for (const string& result : results) {
880 writer.Write(result);
881 }
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900882 }
883
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900884 LOG("FindEmulator: OK");
885 return true;
886 }
887
888 private:
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900889 static unsigned char GetDtTypeFromStat(const struct stat& st) {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900890 if (S_ISREG(st.st_mode)) {
891 return DT_REG;
892 } else if (S_ISDIR(st.st_mode)) {
893 return DT_DIR;
894 } else if (S_ISCHR(st.st_mode)) {
895 return DT_CHR;
896 } else if (S_ISBLK(st.st_mode)) {
897 return DT_BLK;
898 } else if (S_ISFIFO(st.st_mode)) {
899 return DT_FIFO;
900 } else if (S_ISLNK(st.st_mode)) {
901 return DT_LNK;
902 } else if (S_ISSOCK(st.st_mode)) {
903 return DT_SOCK;
904 } else {
905 return DT_UNKNOWN;
906 }
907 }
908
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900909 static unsigned char GetDtType(const string& path) {
910 struct stat st;
911 if (lstat(path.c_str(), &st)) {
912 PERROR("stat for %s", path.c_str());
913 }
914 return GetDtTypeFromStat(st);
915 }
916
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900917 DirentNode* ConstructDirectoryTree(const string& path) {
918 DIR* dir = opendir(path.empty() ? "." : path.c_str());
919 if (!dir)
920 PERROR("opendir failed: %s", path.c_str());
921
922 DirentDirNode* n = new DirentDirNode(path);
923
924 struct dirent* ent;
925 while ((ent = readdir(dir)) != NULL) {
926 if (!strcmp(ent->d_name, ".") ||
927 !strcmp(ent->d_name, "..") ||
928 !strcmp(ent->d_name, ".repo") ||
Dan Willemsen85433272016-10-18 17:38:28 -0700929 !strcmp(ent->d_name, ".git"))
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900930 continue;
931
932 string npath = path;
933 if (!path.empty())
934 npath += '/';
935 npath += ent->d_name;
936
937 DirentNode* c = NULL;
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900938 auto d_type = ent->d_type;
939 if (d_type == DT_UNKNOWN) {
940 d_type = GetDtType(npath);
941 CHECK(d_type != DT_UNKNOWN);
942 }
943 if (d_type == DT_DIR) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900944 c = ConstructDirectoryTree(npath);
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900945 } else if (d_type == DT_LNK) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900946 auto s = new DirentSymlinkNode(npath);
947 symlinks_.push_back(make_pair(npath, s));
948 c = s;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900949 } else {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900950 c = new DirentFileNode(npath, d_type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900951 }
Shinichiro Hamaji802ef962015-07-06 15:37:40 +0900952 node_cnt_++;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900953 n->Add(ent->d_name, c);
954 }
955 closedir(dir);
956
957 return n;
958 }
959
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900960 void ResolveSymlinks() {
Shinichiro Hamajif2d31722015-10-15 17:30:32 +0900961 vector<pair<string, DirentSymlinkNode*>> symlinks;
962 symlinks.swap(symlinks_);
963 for (const auto& p : symlinks) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900964 const string& path = p.first;
965 DirentSymlinkNode* s = p.second;
966
967 char buf[PATH_MAX+1];
968 buf[PATH_MAX] = 0;
969 ssize_t len = readlink(path.c_str(), buf, PATH_MAX);
970 if (len < 0) {
971 WARN("readlink failed: %s", path.c_str());
972 continue;
973 }
974 buf[len] = 0;
975
976 struct stat st;
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900977 unsigned char type = DT_UNKNOWN;
978 if (stat(path.c_str(), &st) == 0) {
979 type = GetDtTypeFromStat(st);
980 } else {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900981 s->set_errno(errno);
982 LOG("stat failed: %s: %s", path.c_str(), strerror(errno));
983 }
984
985 if (*buf != '/') {
986 const string npath = ConcatDir(Dirname(path), buf);
987 bool should_fallback = false;
988 const DirentNode* to = FindDir(npath, &should_fallback);
989 if (to) {
990 s->set_to(to);
991 continue;
992 }
993 }
994
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900995 if (type == DT_DIR) {
996 if (path.find('/') == string::npos) {
997 s->set_to(ConstructDirectoryTree(path));
998 }
999 } else if (type != DT_LNK && type != DT_UNKNOWN) {
1000 s->set_to(new DirentFileNode(path, type));
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001001 }
1002 }
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001003
1004 if (!symlinks_.empty())
1005 ResolveSymlinks();
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001006 }
1007
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001008 unique_ptr<DirentNode> root_;
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001009 vector<pair<string, DirentSymlinkNode*>> symlinks_;
Shinichiro Hamaji802ef962015-07-06 15:37:40 +09001010 int node_cnt_;
Shinichiro Hamajia87bd132015-07-31 12:48:22 +09001011 bool is_initialized_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001012};
1013
1014} // namespace
1015
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001016FindCommand::FindCommand()
Shinichiro Hamaji5a71a8b2015-08-06 19:23:18 +09001017 : follows_symlinks(false), depth(INT_MAX), mindepth(INT_MIN),
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +09001018 redirect_to_devnull(false),
Dan Willemsen439f6f12016-10-19 01:13:54 -07001019 found_files(new vector<string>()),
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001020 read_dirs(new unordered_set<string>()) {
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001021}
1022
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +09001023FindCommand::~FindCommand() {
1024}
1025
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001026bool FindCommand::Parse(const string& cmd) {
1027 FindCommandParser fcp(cmd, this);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +09001028 if (!HasWord(cmd, "find") && !HasWord(cmd, "build/tools/findleaves.py"))
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001029 return false;
1030
1031 if (!fcp.Parse())
1032 return false;
1033
Shinichiro Hamaji3498f342015-08-11 14:23:12 +09001034 NormalizePath(&chdir);
1035 NormalizePath(&testdir);
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001036 if (finddirs.empty())
1037 finddirs.push_back(".");
1038 return true;
1039}
1040
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001041FindEmulator* FindEmulator::Get() {
1042 return g_instance;
1043}
1044
1045void InitFindEmulator() {
1046 new FindEmulatorImpl();
1047}