blob: a1675d2f5ed3feee6a26bd2a64f1e7d3a0858c96 [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;
452 }
453
454 return true;
455 }
456
457 void UngetToken(StringPiece tok) {
458 CHECK(unget_tok_.empty());
459 if (!tok.empty())
460 unget_tok_ = tok;
461 }
462
463 bool ParseTest() {
464 if (has_if_ || !fc_->testdir.empty())
465 return false;
466 StringPiece tok;
467 if (!GetNextToken(&tok) || tok != "-d")
468 return false;
469 if (!GetNextToken(&tok) || tok.empty())
470 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900471 fc_->testdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900472 return true;
473 }
474
475 FindCond* ParseFact(StringPiece tok) {
476 if (tok == "-not" || tok == "\\!") {
477 if (!GetNextToken(&tok) || tok.empty())
478 return NULL;
479 unique_ptr<FindCond> c(ParseFact(tok));
480 if (!c.get())
481 return NULL;
482 return new NotCond(c.release());
483 } else if (tok == "\\(") {
484 if (!GetNextToken(&tok) || tok.empty())
485 return NULL;
486 unique_ptr<FindCond> c(ParseExpr(tok));
487 if (!GetNextToken(&tok) || tok != "\\)") {
488 return NULL;
489 }
490 return c.release();
491 } else if (tok == "-name") {
492 if (!GetNextToken(&tok) || tok.empty())
493 return NULL;
494 return new NameCond(tok.as_string());
495 } else if (tok == "-type") {
496 if (!GetNextToken(&tok) || tok.empty())
497 return NULL;
498 char type;
499 if (tok == "b")
500 type = DT_BLK;
501 else if (tok == "c")
502 type = DT_CHR;
503 else if (tok == "d")
504 type = DT_DIR;
505 else if (tok == "p")
506 type = DT_FIFO;
507 else if (tok == "l")
508 type = DT_LNK;
509 else if (tok == "f")
510 type = DT_REG;
511 else if (tok == "s")
512 type = DT_SOCK;
513 else
514 return NULL;
515 return new TypeCond(type);
516 } else {
517 UngetToken(tok);
518 return NULL;
519 }
520 }
521
522 FindCond* ParseTerm(StringPiece tok) {
523 unique_ptr<FindCond> c(ParseFact(tok));
524 if (!c.get())
525 return NULL;
526 while (true) {
527 if (!GetNextToken(&tok))
528 return NULL;
529 if (tok != "-and" && tok != "-a") {
530 UngetToken(tok);
531 return c.release();
532 }
533 if (!GetNextToken(&tok) || tok.empty())
534 return NULL;
535 unique_ptr<FindCond> r(ParseFact(tok));
536 if (!r.get()) {
537 return NULL;
538 }
539 c.reset(new AndCond(c.release(), r.release()));
540 }
541 }
542
543 FindCond* ParseExpr(StringPiece tok) {
544 unique_ptr<FindCond> c(ParseTerm(tok));
545 if (!c.get())
546 return NULL;
547 while (true) {
548 if (!GetNextToken(&tok))
549 return NULL;
550 if (tok != "-or" && tok != "-o") {
551 UngetToken(tok);
552 return c.release();
553 }
554 if (!GetNextToken(&tok) || tok.empty())
555 return NULL;
556 unique_ptr<FindCond> r(ParseTerm(tok));
557 if (!r.get()) {
558 return NULL;
559 }
560 c.reset(new OrCond(c.release(), r.release()));
561 }
562 }
563
564 // <expr> ::= <term> {<or> <term>}
565 // <term> ::= <fact> {<and> <fact>}
566 // <fact> ::= <not> <fact> | '\(' <expr> '\)' | <pred>
567 // <not> ::= '-not' | '\!'
568 // <and> ::= '-and' | '-a'
569 // <or> ::= '-or' | '-o'
570 // <pred> ::= <name> | <type> | <maxdepth>
571 // <name> ::= '-name' NAME
572 // <type> ::= '-type' TYPE
573 // <maxdepth> ::= '-maxdepth' MAXDEPTH
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700574 FindCond* ParseFindCond(StringPiece tok) { return ParseExpr(tok); }
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900575
576 bool ParseFind() {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900577 fc_->type = FindCommandType::FIND;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900578 StringPiece tok;
579 while (true) {
580 if (!GetNextToken(&tok))
581 return false;
582 if (tok.empty() || tok == ";")
583 return true;
584
585 if (tok == "-L") {
586 fc_->follows_symlinks = true;
587 } else if (tok == "-prune") {
588 if (!fc_->print_cond || fc_->prune_cond)
589 return false;
590 if (!GetNextToken(&tok) || tok != "-o")
591 return false;
592 fc_->prune_cond.reset(fc_->print_cond.release());
593 } else if (tok == "-print") {
594 if (!GetNextToken(&tok) || !tok.empty())
595 return false;
596 return true;
597 } else if (tok == "-maxdepth") {
598 if (!GetNextToken(&tok) || tok.empty())
599 return false;
600 const string& depth_str = tok.as_string();
601 char* endptr;
602 long d = strtol(depth_str.c_str(), &endptr, 10);
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700603 if (endptr != depth_str.data() + depth_str.size() || d < 0 ||
604 d > INT_MAX) {
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900605 return false;
606 }
607 fc_->depth = d;
608 } else if (tok[0] == '-' || tok == "\\(") {
609 if (fc_->print_cond.get())
610 return false;
611 FindCond* c = ParseFindCond(tok);
612 if (!c)
613 return false;
614 fc_->print_cond.reset(c);
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900615 } else if (tok == "2>") {
616 if (!GetNextToken(&tok) || tok != "/dev/null") {
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900617 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900618 }
619 fc_->redirect_to_devnull = true;
620 } else if (tok.find_first_of("|;&><*'\"") != string::npos) {
621 return false;
622 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700623 fc_->finddirs.push_back(tok.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900624 }
625 }
626 }
627
628 bool ParseFindLeaves() {
629 fc_->type = FindCommandType::FINDLEAVES;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900630 fc_->follows_symlinks = true;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900631 StringPiece tok;
Dan Willemsen5131f842016-09-16 20:33:31 -0700632 vector<string> findfiles;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900633 while (true) {
634 if (!GetNextToken(&tok))
635 return false;
636 if (tok.empty()) {
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700637 if (fc_->finddirs.size() == 0) {
638 // backwards compatibility
639 if (findfiles.size() < 2)
640 return false;
641 fc_->finddirs.swap(findfiles);
Dan Willemsen5131f842016-09-16 20:33:31 -0700642 fc_->print_cond.reset(new NameCond(fc_->finddirs.back()));
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700643 fc_->finddirs.pop_back();
644 } else {
645 if (findfiles.size() < 1)
646 return false;
647 for (auto& file : findfiles) {
Dan Willemsen5131f842016-09-16 20:33:31 -0700648 FindCond* cond = new NameCond(file);
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700649 if (fc_->print_cond.get()) {
650 cond = new OrCond(fc_->print_cond.release(), cond);
651 }
652 CHECK(!fc_->print_cond.get());
653 fc_->print_cond.reset(cond);
654 }
655 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900656 return true;
657 }
658
659 if (HasPrefix(tok, "--prune=")) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700660 FindCond* cond =
661 new NameCond(tok.substr(strlen("--prune=")).as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900662 if (fc_->prune_cond.get()) {
663 cond = new OrCond(fc_->prune_cond.release(), cond);
664 }
665 CHECK(!fc_->prune_cond.get());
666 fc_->prune_cond.reset(cond);
667 } else if (HasPrefix(tok, "--mindepth=")) {
668 string mindepth_str = tok.substr(strlen("--mindepth=")).as_string();
669 char* endptr;
670 long d = strtol(mindepth_str.c_str(), &endptr, 10);
671 if (endptr != mindepth_str.data() + mindepth_str.size() ||
672 d < INT_MIN || d > INT_MAX) {
673 return false;
674 }
675 fc_->mindepth = d;
Colin Crossf0a6fdf2016-05-27 16:58:29 -0700676 } else if (HasPrefix(tok, "--dir=")) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700677 StringPiece dir = tok.substr(strlen("--dir="));
Dan Willemsen5131f842016-09-16 20:33:31 -0700678 fc_->finddirs.push_back(dir.as_string());
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900679 } else if (HasPrefix(tok, "--")) {
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700680 if (g_flags.werror_find_emulator) {
681 ERROR("Unknown flag in findleaves.py: %.*s", SPF(tok));
682 } else {
683 WARN("Unknown flag in findleaves.py: %.*s", SPF(tok));
684 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900685 return false;
686 } else {
Dan Willemsen5131f842016-09-16 20:33:31 -0700687 findfiles.push_back(tok.as_string());
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900688 }
689 }
690 }
691
692 bool ParseImpl() {
693 while (true) {
694 StringPiece tok;
695 if (!GetNextToken(&tok))
696 return false;
697
698 if (tok.empty())
699 return true;
700
701 if (tok == "cd") {
702 if (!GetNextToken(&tok) || tok.empty() || !fc_->chdir.empty())
703 return false;
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900704 fc_->chdir = tok.as_string();
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900705 if (!GetNextToken(&tok) || (tok != ";" && tok != "&&"))
706 return false;
707 } else if (tok == "if") {
708 if (!GetNextToken(&tok) || tok != "[")
709 return false;
710 if (!ParseTest())
711 return false;
712 if (!GetNextToken(&tok) || tok != "]")
713 return false;
714 if (!GetNextToken(&tok) || tok != ";")
715 return false;
716 if (!GetNextToken(&tok) || tok != "then")
717 return false;
718 has_if_ = true;
719 } else if (tok == "test") {
720 if (!fc_->chdir.empty())
721 return false;
722 if (!ParseTest())
723 return false;
724 if (!GetNextToken(&tok) || tok != "&&")
725 return false;
726 } else if (tok == "find") {
727 if (!ParseFind())
728 return false;
729 if (has_if_) {
730 if (!GetNextToken(&tok) || tok != "fi")
731 return false;
732 }
733 if (!GetNextToken(&tok) || !tok.empty())
734 return false;
735 return true;
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900736 } else if (tok == "build/tools/findleaves.py") {
737 if (!ParseFindLeaves())
738 return false;
739 return true;
Shinichiro Hamaji39970312015-07-31 16:10:34 +0900740 } else {
741 return false;
Shinichiro Hamaji0876e092015-07-31 15:52:43 +0900742 }
743 }
744 }
745
746 StringPiece cmd_;
747 StringPiece cur_;
748 FindCommand* fc_;
749 bool has_if_;
750 StringPiece unget_tok_;
751};
752
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900753static FindEmulator* g_instance;
754
755class FindEmulatorImpl : public FindEmulator {
756 public:
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700757 FindEmulatorImpl() : node_cnt_(0), is_initialized_(false) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900758 g_instance = this;
759 }
760
761 virtual ~FindEmulatorImpl() = default;
762
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900763 bool CanHandle(StringPiece s) const {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700764 return (!HasPrefix(s, "../") && !HasPrefix(s, "/") &&
765 !HasPrefix(s, ".repo") && !HasPrefix(s, ".git"));
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900766 }
767
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900768 const DirentNode* FindDir(StringPiece d, bool* should_fallback) {
769 const DirentNode* r = root_->FindDir(d);
770 if (!r) {
771 *should_fallback = Exists(d);
772 }
773 return r;
774 }
775
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700776 virtual bool HandleFind(const string& cmd UNUSED,
777 const FindCommand& fc,
778 const Loc& loc,
779 string* out) override {
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900780 if (!CanHandle(fc.chdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700781 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", SPF(fc.chdir),
782 cmd.c_str());
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900783 return false;
784 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900785
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900786 if (!is_initialized_) {
787 ScopedTimeReporter tr("init find emulator time");
788 root_.reset(ConstructDirectoryTree(""));
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800789 if (!root_) {
790 ERROR("FindEmulator: Cannot open root directory");
791 }
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900792 ResolveSymlinks();
Shinichiro Hamajia87bd132015-07-31 12:48:22 +0900793 LOG_STAT("%d find nodes", node_cnt_);
794 is_initialized_ = true;
795 }
796
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900797 if (!fc.testdir.empty()) {
798 if (!CanHandle(fc.testdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700799 LOG("FindEmulator: Cannot handle test dir (%.*s): %s", SPF(fc.testdir),
800 cmd.c_str());
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900801 return false;
802 }
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900803 bool should_fallback = false;
804 if (!FindDir(fc.testdir, &should_fallback)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700805 LOG("FindEmulator: Test dir (%.*s) not found: %s", SPF(fc.testdir),
806 cmd.c_str());
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900807 return !should_fallback;
Shinichiro Hamaji9a802b02015-08-11 15:06:45 +0900808 }
809 }
810
Dan Willemsen4be0f092017-07-25 20:26:54 -0700811 const DirentNode* root = root_.get();
812
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900813 if (!fc.chdir.empty()) {
814 if (!CanHandle(fc.chdir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700815 LOG("FindEmulator: Cannot handle chdir (%.*s): %s", SPF(fc.chdir),
816 cmd.c_str());
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900817 return false;
818 }
Dan Willemsen4be0f092017-07-25 20:26:54 -0700819 root = root->FindDir(fc.chdir);
820 if (!root) {
821 if (Exists(fc.chdir))
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900822 return false;
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900823 if (!fc.redirect_to_devnull) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700824 FIND_WARN_LOC(loc,
825 "FindEmulator: cd: %.*s: No such file or directory",
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700826 SPF(fc.chdir));
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900827 }
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900828 return true;
829 }
830 }
831
Dan Willemsen439f6f12016-10-19 01:13:54 -0700832 vector<string> results;
Dan Willemsen5131f842016-09-16 20:33:31 -0700833 for (const string& finddir : fc.finddirs) {
Dan Willemsen4be0f092017-07-25 20:26:54 -0700834 if (!CanHandle(finddir)) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700835 LOG("FindEmulator: Cannot handle find dir (%s): %s", finddir.c_str(),
836 cmd.c_str());
Shinichiro Hamaji3498f342015-08-11 14:23:12 +0900837 return false;
838 }
839
Dan Willemsen4be0f092017-07-25 20:26:54 -0700840 const DirentNode* base;
841 base = root->FindDir(finddir);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900842 if (!base) {
Dan Willemsen4be0f092017-07-25 20:26:54 -0700843 if (Exists(finddir)) {
Shinichiro Hamajibd3bb232015-10-09 16:35:54 +0900844 return false;
845 }
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900846 if (!fc.redirect_to_devnull) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700847 FIND_WARN_LOC(loc,
848 "FindEmulator: find: `%s': No such file or directory",
Dan Willemsenf63a3fd2017-04-27 23:39:57 -0700849 ConcatDir(fc.chdir, finddir).c_str());
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +0900850 }
Shinichiro Hamajie7a68222015-08-06 17:07:29 +0900851 continue;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900852 }
853
Dan Willemsen5131f842016-09-16 20:33:31 -0700854 string path = finddir;
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900855 unordered_map<const DirentNode*, string> cur_read_dirs;
Dan Willemsen692e64e2017-02-22 15:38:33 -0800856 if (!base->RunFind(fc, loc, 0, &path, &cur_read_dirs, results)) {
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900857 LOG("FindEmulator: RunFind failed: %s", cmd.c_str());
858 return false;
859 }
860 }
861
Dan Willemsen439f6f12016-10-19 01:13:54 -0700862 if (results.size() > 0) {
863 // Calculate and reserve necessary space in out
864 size_t new_length = 0;
865 for (const string& result : results) {
866 new_length += result.size() + 1;
867 }
868 out->reserve(out->size() + new_length - 1);
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900869
Dan Willemsen439f6f12016-10-19 01:13:54 -0700870 if (fc.type == FindCommandType::FINDLEAVES) {
871 sort(results.begin(), results.end());
872 }
873
874 WordWriter writer(out);
875 for (const string& result : results) {
876 writer.Write(result);
877 }
Shinichiro Hamajia6960242015-08-06 18:15:27 +0900878 }
879
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900880 LOG("FindEmulator: OK");
881 return true;
882 }
883
884 private:
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900885 static unsigned char GetDtTypeFromStat(const struct stat& st) {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900886 if (S_ISREG(st.st_mode)) {
887 return DT_REG;
888 } else if (S_ISDIR(st.st_mode)) {
889 return DT_DIR;
890 } else if (S_ISCHR(st.st_mode)) {
891 return DT_CHR;
892 } else if (S_ISBLK(st.st_mode)) {
893 return DT_BLK;
894 } else if (S_ISFIFO(st.st_mode)) {
895 return DT_FIFO;
896 } else if (S_ISLNK(st.st_mode)) {
897 return DT_LNK;
898 } else if (S_ISSOCK(st.st_mode)) {
899 return DT_SOCK;
900 } else {
901 return DT_UNKNOWN;
902 }
903 }
904
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900905 static unsigned char GetDtType(const string& path) {
906 struct stat st;
907 if (lstat(path.c_str(), &st)) {
908 PERROR("stat for %s", path.c_str());
909 }
910 return GetDtTypeFromStat(st);
911 }
912
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900913 DirentNode* ConstructDirectoryTree(const string& path) {
914 DIR* dir = opendir(path.empty() ? "." : path.c_str());
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800915 if (!dir) {
Dan Willemsen09279ad2017-05-12 13:57:40 -0700916 if (errno == ENOENT || errno == EACCES) {
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800917 LOG("opendir failed: %s", path.c_str());
918 return NULL;
919 } else {
920 PERROR("opendir failed: %s", path.c_str());
921 }
922 }
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900923
924 DirentDirNode* n = new DirentDirNode(path);
925
926 struct dirent* ent;
927 while ((ent = readdir(dir)) != NULL) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700928 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") ||
929 !strcmp(ent->d_name, ".repo") || !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);
Dan Willemsen6f3f0f42017-02-23 00:10:04 -0800945 if (c == NULL) {
946 continue;
947 }
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900948 } else if (d_type == DT_LNK) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900949 auto s = new DirentSymlinkNode(npath);
950 symlinks_.push_back(make_pair(npath, s));
951 c = s;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900952 } else {
Shinichiro Hamaji2055fa52015-09-08 13:42:38 +0900953 c = new DirentFileNode(npath, d_type);
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900954 }
Shinichiro Hamaji802ef962015-07-06 15:37:40 +0900955 node_cnt_++;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +0900956 n->Add(ent->d_name, c);
957 }
958 closedir(dir);
959
960 return n;
961 }
962
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900963 void ResolveSymlinks() {
Shinichiro Hamajif2d31722015-10-15 17:30:32 +0900964 vector<pair<string, DirentSymlinkNode*>> symlinks;
965 symlinks.swap(symlinks_);
966 for (const auto& p : symlinks) {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900967 const string& path = p.first;
968 DirentSymlinkNode* s = p.second;
969
Dan Willemsen3ce083f2017-10-11 22:17:48 -0700970 char buf[PATH_MAX + 1];
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900971 buf[PATH_MAX] = 0;
972 ssize_t len = readlink(path.c_str(), buf, PATH_MAX);
973 if (len < 0) {
974 WARN("readlink failed: %s", path.c_str());
975 continue;
976 }
977 buf[len] = 0;
978
979 struct stat st;
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900980 unsigned char type = DT_UNKNOWN;
981 if (stat(path.c_str(), &st) == 0) {
982 type = GetDtTypeFromStat(st);
983 } else {
Shinichiro Hamajib7175612015-10-13 16:15:34 +0900984 s->set_errno(errno);
985 LOG("stat failed: %s: %s", path.c_str(), strerror(errno));
986 }
987
988 if (*buf != '/') {
989 const string npath = ConcatDir(Dirname(path), buf);
990 bool should_fallback = false;
991 const DirentNode* to = FindDir(npath, &should_fallback);
992 if (to) {
993 s->set_to(to);
994 continue;
995 }
996 }
997
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +0900998 if (type == DT_DIR) {
999 if (path.find('/') == string::npos) {
Dan Willemsen6f3f0f42017-02-23 00:10:04 -08001000 DirentNode* dir = ConstructDirectoryTree(path);
1001 if (dir != NULL) {
1002 s->set_to(dir);
1003 } else {
1004 s->set_errno(errno);
1005 }
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001006 }
1007 } else if (type != DT_LNK && type != DT_UNKNOWN) {
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001008 s->set_to(new DirentFileNode(path, type));
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001009 }
1010 }
Shinichiro Hamaji407d8d42015-10-15 16:51:09 +09001011
1012 if (!symlinks_.empty())
1013 ResolveSymlinks();
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001014 }
1015
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001016 unique_ptr<DirentNode> root_;
Shinichiro Hamajib7175612015-10-13 16:15:34 +09001017 vector<pair<string, DirentSymlinkNode*>> symlinks_;
Shinichiro Hamaji802ef962015-07-06 15:37:40 +09001018 int node_cnt_;
Shinichiro Hamajia87bd132015-07-31 12:48:22 +09001019 bool is_initialized_;
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001020};
1021
1022} // namespace
1023
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001024FindCommand::FindCommand()
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001025 : follows_symlinks(false),
1026 depth(INT_MAX),
1027 mindepth(INT_MIN),
Shinichiro Hamaji89e0c4f2015-08-11 15:25:24 +09001028 redirect_to_devnull(false),
Dan Willemsen439f6f12016-10-19 01:13:54 -07001029 found_files(new vector<string>()),
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001030 read_dirs(new unordered_set<string>()) {}
Shinichiro Hamajia5a5ef62015-07-31 14:45:41 +09001031
Dan Willemsen3ce083f2017-10-11 22:17:48 -07001032FindCommand::~FindCommand() {}
Shinichiro Hamajic9b0aca2015-07-31 16:47:56 +09001033
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001034bool FindCommand::Parse(const string& cmd) {
1035 FindCommandParser fcp(cmd, this);
Shinichiro Hamajie7a68222015-08-06 17:07:29 +09001036 if (!HasWord(cmd, "find") && !HasWord(cmd, "build/tools/findleaves.py"))
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001037 return false;
1038
1039 if (!fcp.Parse())
1040 return false;
1041
Shinichiro Hamaji3498f342015-08-11 14:23:12 +09001042 NormalizePath(&chdir);
1043 NormalizePath(&testdir);
Shinichiro Hamaji0876e092015-07-31 15:52:43 +09001044 if (finddirs.empty())
1045 finddirs.push_back(".");
1046 return true;
1047}
1048
Shinichiro Hamaji5f57a992015-06-30 19:39:39 +09001049FindEmulator* FindEmulator::Get() {
1050 return g_instance;
1051}
1052
1053void InitFindEmulator() {
1054 new FindEmulatorImpl();
1055}