Merge remote-tracking branch 'aosp/upstream' into kati

4421dda [C++] ifdef should fail when there are multiple variables
407d8d4 [C++] Resolve symlinks in the top directory
31505ba [C++] Fix find -name for symlinks
f2d3172 [C++] Fix a memory corruption
6ce977d [C++] Regenerate ninja files when symlink was changed
9c5ec1d [go] Mark find_command.mk and wildcard.mk as FAIL
b717561 [C++] Make FindEmulator's symlink support better
14ea0f1 [C++] Fix NormalizePath("../../foo")
bd3bb23 [C++] Fix FindEmulator for paths in symlinks
c58db99 [C++] Make the fast pass of EscapeShell consistent with the rest
5de5826 [C++] Do not escape ! in a shell script
71cf60b [C++] Stop using realpath(1) to handle $(realpath) in recipe
cb4724e [C++] Fix realpath implementation for multiple parameters
34556cc [C++] Do not find first Makefile when -f is specified
171dc82 [C++] Make the output of find_test compatible with find(1)
680e98b Exit 1 in runtest.rb
9fe05bf Install realpath
9d918f8 Remove ninja_normalized_path2.mk
212b7a5 [C++] Fix a off-by-one error in StripShellComment
d0251f7 Change the shell in Travis CI to bash
0613e01 Revert "Always use SHELL=/bin/bash in our test"
a73e93b Always use SHELL=/bin/bash in our test
b7be8f1 [C++] Fix err_include.mk for -c -n
3c60c12 [C++] Fix err_include.mk
e3c6231 Install ninja-build on Travis CI

Bug: 24580686
Change-Id: I2419d78affcd4cfd1de284a7dc79c0e8a4535fc9
diff --git a/.travis.yml b/.travis.yml
index 9d2dcc5..9c785dc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,8 @@
 before_script:
   - sudo add-apt-repository -y "deb http://archive.ubuntu.com/ubuntu/ trusty main universe"
   - sudo apt-get update -qq
-  - sudo apt-get install -y libstdc++-4.8-dev clang-3.5
+  - sudo apt-get install -y libstdc++-4.8-dev clang-3.5 ninja-build realpath
+  - sudo ln -sf bash /bin/sh
 
 script:
   - make -j4 ckati
diff --git a/eval.cc b/eval.cc
index 72661e3..25b9c52 100644
--- a/eval.cc
+++ b/eval.cc
@@ -187,7 +187,11 @@
   switch (stmt->op) {
     case CondOp::IFDEF:
     case CondOp::IFNDEF: {
-      Symbol lhs = Intern(stmt->lhs->Eval(this));
+      string var_name;
+      stmt->lhs->Eval(this, &var_name);
+      if (var_name.find_first_of(" \t") != string::npos)
+        Error("*** invalid syntax in conditional.");
+      Symbol lhs = Intern(var_name);
       Var* v = LookupVarInCurrentScope(lhs);
       const string&& s = v->Eval(this);
       is_true = (s.empty() == (stmt->op == CondOp::IFNDEF));
diff --git a/fileutil.cc b/fileutil.cc
index d2035f3..abfad9d 100644
--- a/fileutil.cc
+++ b/fileutil.cc
@@ -41,17 +41,21 @@
   return true;
 }
 
+double GetTimestampFromStat(const struct stat& st) {
+#if defined(__linux__)
+  return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
+#else
+  return st.st_mtime;
+#endif
+}
+
 double GetTimestamp(StringPiece filename) {
   CHECK(filename.size() < PATH_MAX);
   struct stat st;
   if (stat(filename.as_string().c_str(), &st) < 0) {
     return -2.0;
   }
-#if defined(__linux__)
-  return st.st_mtime + st.st_mtim.tv_nsec * 0.001 * 0.001 * 0.001;
-#else
-  return st.st_mtime;
-#endif
+  return GetTimestampFromStat(st);
 }
 
 int RunCommand(const string& shell, const string& cmd,
diff --git a/fileutil.h b/fileutil.h
index ff7b9e0..5a4740d 100644
--- a/fileutil.h
+++ b/fileutil.h
@@ -25,6 +25,7 @@
 using namespace std;
 
 bool Exists(StringPiece f);
+double GetTimestampFromStat(const struct stat& st);
 double GetTimestamp(StringPiece f);
 
 enum struct RedirectStderr {
diff --git a/find.cc b/find.cc
index eb796f1..4c7cd8f 100644
--- a/find.cc
+++ b/find.cc
@@ -28,6 +28,7 @@
 
 //#undef NOLOG
 
+#include "fileutil.h"
 #include "log.h"
 #include "string_piece.h"
 #include "strutil.h"
@@ -36,7 +37,7 @@
 class FindCond {
  public:
   virtual ~FindCond() = default;
-  virtual bool IsTrue(const string& name, unsigned char type) const = 0;
+  virtual bool IsTrue(const string& path, unsigned char type) const = 0;
  protected:
   FindCond() = default;
 };
@@ -48,8 +49,8 @@
   explicit NameCond(const string& n)
       : name_(n) {
   }
-  virtual bool IsTrue(const string& name, unsigned char) const {
-    return fnmatch(name_.c_str(), name.c_str(), 0) == 0;
+  virtual bool IsTrue(const string& path, unsigned char) const override {
+    return fnmatch(name_.c_str(), Basename(path).data(), 0) == 0;
   }
  private:
   string name_;
@@ -60,7 +61,7 @@
   explicit TypeCond(unsigned char t)
       : type_(t) {
   }
-  virtual bool IsTrue(const string&, unsigned char type) const {
+  virtual bool IsTrue(const string&, unsigned char type) const override {
     return type == type_;
   }
  private:
@@ -72,8 +73,8 @@
   NotCond(FindCond* c)
       : c_(c) {
   }
-  virtual bool IsTrue(const string& name, unsigned char type) const {
-    return !c_->IsTrue(name, type);
+  virtual bool IsTrue(const string& path, unsigned char type) const override {
+    return !c_->IsTrue(path, type);
   }
  private:
   unique_ptr<FindCond> c_;
@@ -84,9 +85,9 @@
   AndCond(FindCond* c1, FindCond* c2)
       : c1_(c1), c2_(c2) {
   }
-  virtual bool IsTrue(const string& name, unsigned char type) const {
-    if (c1_->IsTrue(name, type))
-      return c2_->IsTrue(name, type);
+  virtual bool IsTrue(const string& path, unsigned char type) const override {
+    if (c1_->IsTrue(path, type))
+      return c2_->IsTrue(path, type);
     return false;
   }
  private:
@@ -98,9 +99,9 @@
   OrCond(FindCond* c1, FindCond* c2)
       : c1_(c1), c2_(c2) {
   }
-  virtual bool IsTrue(const string& name, unsigned char type) const {
-    if (!c1_->IsTrue(name, type))
-      return c2_->IsTrue(name, type);
+  virtual bool IsTrue(const string& path, unsigned char type) const override {
+    if (!c1_->IsTrue(path, type))
+      return c2_->IsTrue(path, type);
     return true;
   }
  private:
@@ -115,7 +116,11 @@
     return NULL;
   }
   virtual bool RunFind(const FindCommand& fc, int d,
-                       string* path, string* out) const = 0;
+                       string* path,
+                       unordered_map<const DirentNode*, string>* cur_read_dirs,
+                       string* out) const = 0;
+
+  virtual bool IsDirectory() const = 0;
 
   const string& base() const { return base_; }
 
@@ -129,7 +134,7 @@
                         unsigned char type,
                         int d,
                         string* out) const {
-    if (fc.print_cond && !fc.print_cond->IsTrue(base_, type))
+    if (fc.print_cond && !fc.print_cond->IsTrue(path, type))
       return;
     if (d < fc.mindepth)
       return;
@@ -147,15 +152,47 @@
   }
 
   virtual bool RunFind(const FindCommand& fc, int d,
-                       string* path, string* out) const {
+                       string* path,
+                       unordered_map<const DirentNode*, string>*,
+                       string* out) const {
     PrintIfNecessary(fc, *path, type_, d, out);
     return true;
   }
 
+  virtual bool IsDirectory() const override { return false; }
+
  private:
   unsigned char type_;
 };
 
+struct ScopedReadDirTracker {
+ public:
+  ScopedReadDirTracker(const DirentNode* n,
+                       const string& path,
+                       unordered_map<const DirentNode*, string>* cur_read_dirs)
+      : n_(NULL), cur_read_dirs_(cur_read_dirs) {
+    const auto& p = cur_read_dirs->emplace(n, path);
+    if (p.second) {
+      n_ = n;
+    } else {
+      conflicted_ = p.first->second;
+    }
+  }
+
+  ~ScopedReadDirTracker() {
+    if (n_)
+      cur_read_dirs_->erase(n_);
+  }
+
+  bool ok() const { return conflicted_.empty(); }
+  const string& conflicted() const { return conflicted_; }
+
+ private:
+  string conflicted_;
+  const DirentNode* n_;
+  unordered_map<const DirentNode*, string>* cur_read_dirs_;
+};
+
 class DirentDirNode : public DirentNode {
  public:
   explicit DirentDirNode(const string& name)
@@ -185,10 +222,20 @@
   }
 
   virtual bool RunFind(const FindCommand& fc, int d,
-                       string* path, string* out) const {
-    fc.read_dirs->push_back(*path);
+                       string* path,
+                       unordered_map<const DirentNode*, string>* cur_read_dirs,
+                       string* out) const {
+    ScopedReadDirTracker srdt(this, *path, cur_read_dirs);
+    if (!srdt.ok()) {
+      fprintf(stderr, "FindEmulator: find: File system loop detected; `%s' is "
+              "part of the same file system loop as `%s'.\n",
+              path->c_str(), srdt.conflicted().c_str());
+      return true;
+    }
 
-    if (fc.prune_cond && fc.prune_cond->IsTrue(base_, DT_DIR)) {
+    fc.read_dirs->insert(*path);
+
+    if (fc.prune_cond && fc.prune_cond->IsTrue(*path, DT_DIR)) {
       if (fc.type != FindCommandType::FINDLEAVES) {
         *out += *path;
         *out += ' ';
@@ -207,12 +254,12 @@
       for (const auto& p : children_) {
         DirentNode* c = p.second;
         // We will handle directories later.
-        if (dynamic_cast<DirentDirNode*>(c))
+        if (c->IsDirectory())
           continue;
         if ((*path)[path->size()-1] != '/')
           *path += '/';
         *path += c->base();
-        if (!c->RunFind(fc, d + 1, path, out))
+        if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
           return false;
         path->resize(orig_path_size);
         // Found a leaf, stop the search.
@@ -222,12 +269,12 @@
 
       for (const auto& p : children_) {
         DirentNode* c = p.second;
-        if (!dynamic_cast<DirentDirNode*>(c))
+        if (!c->IsDirectory())
           continue;
         if ((*path)[path->size()-1] != '/')
           *path += '/';
         *path += c->base();
-        if (!c->RunFind(fc, d + 1, path, out))
+        if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
           return false;
         path->resize(orig_path_size);
       }
@@ -237,7 +284,7 @@
         if ((*path)[path->size()-1] != '/')
           *path += '/';
         *path += c->base();
-        if (!c->RunFind(fc, d + 1, path, out))
+        if (!c->RunFind(fc, d + 1, path, cur_read_dirs, out))
           return false;
         path->resize(orig_path_size);
       }
@@ -245,6 +292,8 @@
     return true;
   }
 
+  virtual bool IsDirectory() const override { return true; }
+
   void Add(const string& name, DirentNode* c) {
     children_.emplace(children_.end(), name, c);
   }
@@ -256,58 +305,55 @@
 class DirentSymlinkNode : public DirentNode {
  public:
   explicit DirentSymlinkNode(const string& name)
-      : DirentNode(name) {
+      : DirentNode(name), to_(NULL), errno_(0) {
+  }
+
+  virtual const DirentNode* FindDir(StringPiece d) const {
+    if (errno_ == 0 && to_)
+      return to_->FindDir(d);
+    return NULL;
   }
 
   virtual bool RunFind(const FindCommand& fc, int d,
-                       string* path, string* out) const {
+                       string* path,
+                       unordered_map<const DirentNode*, string>* cur_read_dirs,
+                       string* out) const {
     unsigned char type = DT_LNK;
-    if (fc.follows_symlinks) {
-      // TODO
-      LOG("FindEmulator: symlink is hard");
-      return false;
-
-      char buf[PATH_MAX+1];
-      buf[PATH_MAX] = 0;
-      LOG("path=%s", path->c_str());
-      ssize_t len = readlink(path->c_str(), buf, PATH_MAX);
-      if (len > 0) {
-        buf[len] = 0;
-        string oldpath;
-        if (buf[0] != '/') {
-          Dirname(*path).AppendToString(&oldpath);
-          oldpath += '/';
+    if (fc.follows_symlinks && errno_ != ENOENT) {
+      if (errno_) {
+        if (fc.type != FindCommandType::FINDLEAVES) {
+          fprintf(stderr, "FindEmulator: find: `%s': %s\n",
+                  path->c_str(), strerror(errno_));
         }
-        oldpath += buf;
-
-        LOG("buf=%s old=%s", buf, oldpath.c_str());
-
-        struct stat st;
-        if (stat(oldpath.c_str(), &st) == 0) {
-          LOG("st OK");
-          if (S_ISREG(st.st_mode)) {
-            type = DT_REG;
-          } else if (S_ISDIR(st.st_mode)) {
-            type = DT_DIR;
-          } else if (S_ISCHR(st.st_mode)) {
-            type = DT_CHR;
-          } else if (S_ISBLK(st.st_mode)) {
-            type = DT_BLK;
-          } else if (S_ISFIFO(st.st_mode)) {
-            type = DT_FIFO;
-          } else if (S_ISLNK(st.st_mode)) {
-            type = DT_LNK;
-          } else if (S_ISSOCK(st.st_mode)) {
-            type = DT_SOCK;
-          } else {
-            return false;
-          }
-        }
+        return true;
       }
+
+      if (!to_) {
+        LOG("FindEmulator does not support %s", path->c_str());
+        return false;
+      }
+
+      return to_->RunFind(fc, d, path, cur_read_dirs, out);
     }
     PrintIfNecessary(fc, *path, type, d, out);
     return true;
   }
+
+  virtual bool IsDirectory() const override {
+    return errno_ == 0 && to_ && to_->IsDirectory();
+  }
+
+  void set_to(const DirentNode* to) {
+    to_ = to;
+  }
+
+  void set_errno(int e) {
+    errno_ = e;
+  }
+
+ private:
+  const DirentNode* to_;
+  int errno_;
 };
 
 class FindCommandParser {
@@ -545,6 +591,7 @@
 
   bool ParseFindLeaves() {
     fc_->type = FindCommandType::FINDLEAVES;
+    fc_->follows_symlinks = true;
     StringPiece tok;
     while (true) {
       if (!GetNextToken(&tok))
@@ -663,6 +710,14 @@
             !HasPrefix(s, "out"));
   }
 
+  const DirentNode* FindDir(StringPiece d, bool* should_fallback) {
+    const DirentNode* r = root_->FindDir(d);
+    if (!r) {
+      *should_fallback = Exists(d);
+    }
+    return r;
+  }
+
   virtual bool HandleFind(const string& cmd UNUSED, const FindCommand& fc,
                           string* out) override {
     if (!CanHandle(fc.chdir)) {
@@ -674,6 +729,7 @@
     if (!is_initialized_) {
       ScopedTimeReporter tr("init find emulator time");
       root_.reset(ConstructDirectoryTree(""));
+      ResolveSymlinks();
       LOG_STAT("%d find nodes", node_cnt_);
       is_initialized_ = true;
     }
@@ -684,10 +740,11 @@
             SPF(fc.testdir), cmd.c_str());
         return false;
       }
-      if (!root_->FindDir(fc.testdir)) {
+      bool should_fallback = false;
+      if (!FindDir(fc.testdir, &should_fallback)) {
         LOG("FindEmulator: Test dir (%.*s) not found: %s",
             SPF(fc.testdir), cmd.c_str());
-        return true;
+        return !should_fallback;
       }
     }
 
@@ -697,7 +754,10 @@
             SPF(fc.chdir), cmd.c_str());
         return false;
       }
-      if (!root_->FindDir(fc.chdir)) {
+      bool should_fallback = false;
+      if (!FindDir(fc.chdir, &should_fallback)) {
+        if (should_fallback)
+          return false;
         if (!fc.redirect_to_devnull) {
           fprintf(stderr,
                   "FindEmulator: cd: %.*s: No such file or directory\n",
@@ -718,8 +778,13 @@
         return false;
       }
 
-      const DirentNode* base = root_->FindDir(dir);
+      bool should_fallback = false;
+      const DirentNode* base = FindDir(dir, &should_fallback);
       if (!base) {
+        if (should_fallback) {
+          out->resize(orig_out_size);
+          return false;
+        }
         if (!fc.redirect_to_devnull) {
           fprintf(stderr,
                   "FindEmulator: find: `%s': No such file or directory\n",
@@ -729,7 +794,8 @@
       }
 
       string path = finddir.as_string();
-      if (!base->RunFind(fc, 0, &path, out)) {
+      unordered_map<const DirentNode*, string> cur_read_dirs;
+      if (!base->RunFind(fc, 0, &path, &cur_read_dirs, out)) {
         LOG("FindEmulator: RunFind failed: %s", cmd.c_str());
         out->resize(orig_out_size);
         return false;
@@ -748,12 +814,7 @@
   }
 
  private:
-  static unsigned char GetDtType(const string& path) {
-    struct stat st;
-    if (lstat(path.c_str(), &st)) {
-      PERROR("stat for %s", path.c_str());
-    }
-
+  static unsigned char GetDtTypeFromStat(const struct stat& st) {
     if (S_ISREG(st.st_mode)) {
       return DT_REG;
     } else if (S_ISDIR(st.st_mode)) {
@@ -773,6 +834,14 @@
     }
   }
 
+  static unsigned char GetDtType(const string& path) {
+    struct stat st;
+    if (lstat(path.c_str(), &st)) {
+      PERROR("stat for %s", path.c_str());
+    }
+    return GetDtTypeFromStat(st);
+  }
+
   DirentNode* ConstructDirectoryTree(const string& path) {
     DIR* dir = opendir(path.empty() ? "." : path.c_str());
     if (!dir)
@@ -803,7 +872,9 @@
       if (d_type == DT_DIR) {
         c = ConstructDirectoryTree(npath);
       } else if (d_type == DT_LNK) {
-        c = new DirentSymlinkNode(npath);
+        auto s = new DirentSymlinkNode(npath);
+        symlinks_.push_back(make_pair(npath, s));
+        c = s;
       } else {
         c = new DirentFileNode(npath, d_type);
       }
@@ -815,7 +886,56 @@
     return n;
   }
 
+  void ResolveSymlinks() {
+    vector<pair<string, DirentSymlinkNode*>> symlinks;
+    symlinks.swap(symlinks_);
+    for (const auto& p : symlinks) {
+      const string& path = p.first;
+      DirentSymlinkNode* s = p.second;
+
+      char buf[PATH_MAX+1];
+      buf[PATH_MAX] = 0;
+      ssize_t len = readlink(path.c_str(), buf, PATH_MAX);
+      if (len < 0) {
+        WARN("readlink failed: %s", path.c_str());
+        continue;
+      }
+      buf[len] = 0;
+
+      struct stat st;
+      unsigned char type = DT_UNKNOWN;
+      if (stat(path.c_str(), &st) == 0) {
+        type = GetDtTypeFromStat(st);
+      } else {
+        s->set_errno(errno);
+        LOG("stat failed: %s: %s", path.c_str(), strerror(errno));
+      }
+
+      if (*buf != '/') {
+        const string npath = ConcatDir(Dirname(path), buf);
+        bool should_fallback = false;
+        const DirentNode* to = FindDir(npath, &should_fallback);
+        if (to) {
+          s->set_to(to);
+          continue;
+        }
+      }
+
+      if (type == DT_DIR) {
+        if (path.find('/') == string::npos) {
+          s->set_to(ConstructDirectoryTree(path));
+        }
+      } else if (type != DT_LNK && type != DT_UNKNOWN) {
+          s->set_to(new DirentFileNode(path, type));
+      }
+    }
+
+    if (!symlinks_.empty())
+      ResolveSymlinks();
+  }
+
   unique_ptr<DirentNode> root_;
+  vector<pair<string, DirentSymlinkNode*>> symlinks_;
   int node_cnt_;
   bool is_initialized_;
 };
@@ -825,7 +945,7 @@
 FindCommand::FindCommand()
     : follows_symlinks(false), depth(INT_MAX), mindepth(INT_MIN),
       redirect_to_devnull(false),
-      read_dirs(new vector<string>()) {
+      read_dirs(new unordered_set<string>()) {
 }
 
 FindCommand::~FindCommand() {
diff --git a/find.h b/find.h
index 725451c..ccd50e0 100644
--- a/find.h
+++ b/find.h
@@ -17,6 +17,7 @@
 
 #include <memory>
 #include <string>
+#include <unordered_set>
 #include <vector>
 
 #include "string_piece.h"
@@ -48,7 +49,7 @@
   int mindepth;
   bool redirect_to_devnull;
 
-  unique_ptr<vector<string>> read_dirs;
+  unique_ptr<unordered_set<string>> read_dirs;
 
  private:
   FindCommand(const FindCommand&) = delete;
diff --git a/find_test.cc b/find_test.cc
index b4664a2..1ba451a 100644
--- a/find_test.cc
+++ b/find_test.cc
@@ -18,6 +18,8 @@
 
 #include <string>
 
+#include "strutil.h"
+
 int main(int argc, char* argv[]) {
   if (argc == 1) {
     fprintf(stderr, "TODO: Write unit tests\n");
@@ -41,5 +43,8 @@
     fprintf(stderr, "Find emulator does not support this command\n");
     return 1;
   }
-  printf("%s\n", out.c_str());
+
+  for (StringPiece tok : WordScanner(out)) {
+    printf("%.*s\n", SPF(tok));
+  }
 }
diff --git a/func.cc b/func.cc
index 6368e02..34fca3e 100644
--- a/func.cc
+++ b/func.cc
@@ -58,7 +58,7 @@
     switch (*in) {
       case '#':
         if (quote == 0 && isspace(prev_char)) {
-          while (*in && *in != '\n')
+          while (in[1] && *in != '\n')
             in++;
           break;
         }
@@ -383,7 +383,11 @@
 void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   const string&& text = args[0]->Eval(ev);
   if (ev->avoid_io()) {
-    *s += "$(realpath ";
+    *s += "$(";
+    string kati_binary;
+    GetExecutablePath(&kati_binary);
+    *s += kati_binary;
+    *s += " --realpath ";
     *s += text;
     *s += " 2> /dev/null)";
     return;
@@ -394,7 +398,7 @@
     ScopedTerminator st(tok);
     char buf[PATH_MAX];
     if (realpath(tok.data(), buf))
-      *s += buf;
+      ww.Write(buf);
   }
 }
 
diff --git a/main.cc b/main.cc
index 5918601..930348b 100644
--- a/main.cc
+++ b/main.cc
@@ -47,18 +47,6 @@
   InitFuncTable();
   InitDepNodePool();
   InitParser();
-
-  if (g_flags.makefile == NULL) {
-    if (Exists("GNUmakefile")) {
-      g_flags.makefile = "GNUmakefile";
-#if !defined(__APPLE__)
-    } else if (Exists("makefile")) {
-      g_flags.makefile = "makefile";
-#endif
-    } else if (Exists("Makefile")) {
-      g_flags.makefile = "Makefile";
-    }
-  }
 }
 
 static void Quit() {
@@ -225,7 +213,33 @@
   return 0;
 }
 
+static void FindFirstMakefie() {
+  if (g_flags.makefile != NULL)
+    return;
+  if (Exists("GNUmakefile")) {
+    g_flags.makefile = "GNUmakefile";
+#if !defined(__APPLE__)
+  } else if (Exists("makefile")) {
+    g_flags.makefile = "makefile";
+#endif
+  } else if (Exists("Makefile")) {
+    g_flags.makefile = "Makefile";
+  }
+}
+
+static void HandleRealpath(int argc, char** argv) {
+  char buf[PATH_MAX];
+  for (int i = 0; i < argc; i++) {
+    if (realpath(argv[i], buf))
+      printf("%s\n", buf);
+  }
+}
+
 int main(int argc, char* argv[]) {
+  if (argc >= 2 && !strcmp(argv[1], "--realpath")) {
+    HandleRealpath(argc - 2, argv + 2);
+    return 0;
+  }
   Init();
   string orig_args;
   for (int i = 0; i < argc; i++) {
@@ -234,6 +248,7 @@
     orig_args += argv[i];
   }
   g_flags.Parse(argc, argv);
+  FindFirstMakefie();
   if (g_flags.makefile == NULL)
     ERROR("*** No targets specified and no makefile found.");
   // This depends on command line flags.
diff --git a/ninja.cc b/ninja.cc
index 4e4c460..21c935f 100644
--- a/ninja.cc
+++ b/ninja.cc
@@ -471,7 +471,7 @@
   }
 
   void EscapeShell(string* s) const {
-    if (s->find_first_of("$`!\\\"") == string::npos)
+    if (s->find_first_of("$`\\\"") == string::npos)
       return;
     string r;
     bool last_dollar = false;
@@ -489,7 +489,6 @@
           break;
         case '`':
         case '"':
-        case '!':
         case '\\':
           r += '\\';
           // fall through.
@@ -932,8 +931,21 @@
           // directory which affects the results of find command.
           if (s == "" || s == "." || ShouldIgnoreDirty(s))
             continue;
-          double ts = GetTimestamp(s);
-          should_run_command |= (ts < 0 || gen_time < ts);
+
+          struct stat st;
+          if (lstat(s.c_str(), &st) != 0) {
+            should_run_command = true;
+            continue;
+          }
+          double ts = GetTimestampFromStat(st);
+          if (gen_time < ts) {
+            should_run_command = true;
+            continue;
+          }
+          if (S_ISLNK(st.st_mode)) {
+            ts = GetTimestamp(s);
+            should_run_command |= (ts < 0 || gen_time < ts);
+          }
         }
 
         if (!should_run_command) {
diff --git a/runtest.rb b/runtest.rb
index a3babfe..c4938b9 100755
--- a/runtest.rb
+++ b/runtest.rb
@@ -102,7 +102,7 @@
   log.gsub!(/^ninja: error: (.*, needed by .*),.*/,
             '*** No rule to make target \\1.')
   log.gsub!(/^ninja: warning: multiple rules generate (.*)\. builds involving this target will not be correct.*$/,
-	    'ninja: warning: multiple rules generate \\1.')
+            'ninja: warning: multiple rules generate \\1.')
   if mk =~ /err_error_in_recipe.mk/
     # This test expects ninja fails. Strip ninja specific error logs.
     log.gsub!(/^FAILED: .*\n/, '')
@@ -135,6 +135,8 @@
   if !via_ninja
     expected.gsub!(/^ninja: warning: .*\n/, '')
   end
+  # Normalization for "include foo" with C++ kati.
+  expected.gsub!(/(: )(\S+): (No such file or directory)\n\*\*\* No rule to make target "\2"./, '\1\2: \3')
 
   expected
 end
@@ -145,8 +147,6 @@
   output.gsub!(/^\*kati\*.*\n/, '')
   output.gsub!(/^c?kati: /, '')
   output.gsub!(/[`'"]/, '"')
-  output.gsub!(/(: )(?:open )?(\S+): [Nn](o such file or directory)\nNOTE:.*/,
-               "\\1\\2: N\\3\n*** No rule to make target \"\\2\".")
   output.gsub!(/\/bin\/sh: ([^:]*): command not found/,
                "\\1: Command not found")
   output.gsub!(/.*: warning for parse error in an unevaluated line: .*\n/, '')
@@ -154,6 +154,9 @@
   output.gsub!(/^\/bin\/sh: line 0: /, '')
   output.gsub!(/ (\.\/+)+kati\.\S+/, '') # kati log files in find_command.mk
   output.gsub!(/ (\.\/+)+test\S+.json/, '') # json files in find_command.mk
+  # Normalization for "include foo" with Go kati.
+  output.gsub!(/(: )open (\S+): n(o such file or directory)\nNOTE:.*/,
+               "\\1\\2: N\\3")
   output
 end
 
@@ -390,6 +393,7 @@
 
 if !unexpected_passes.empty? || !failures.empty?
   puts "FAIL! (#{failures.size + unexpected_passes.size} fails #{passes.size} passes)"
+  exit 1
 else
   puts 'PASS!'
 end
diff --git a/strutil.cc b/strutil.cc
index 09deaf4..7707482 100644
--- a/strutil.cc
+++ b/strutil.cc
@@ -297,6 +297,7 @@
         // /..
         j = start_index;
       } else {
+        size_t orig_j = j;
         j -= 4;
         j = o->rfind('/', j);
         if (j == string::npos) {
@@ -304,6 +305,11 @@
         } else {
           j++;
         }
+        if (StringPiece(o->data() + j, 3) == "../") {
+          j = orig_j;
+          (*o)[j] = c;
+          j++;
+        }
       }
     } else if (!prev_dir.empty()) {
       if (c) {
diff --git a/strutil_test.cc b/strutil_test.cc
index 048a974..eb1e195 100644
--- a/strutil_test.cc
+++ b/strutil_test.cc
@@ -109,6 +109,10 @@
   ASSERT_EQ(NormalizePath("/../../foo"), "/foo");
   ASSERT_EQ(NormalizePath("/a/../../foo"), "/foo");
   ASSERT_EQ(NormalizePath("/a/b/.."), "/a");
+  ASSERT_EQ(NormalizePath("../../a/b"), "../../a/b");
+  ASSERT_EQ(NormalizePath("../../../a/b"), "../../../a/b");
+  ASSERT_EQ(NormalizePath(".././../a/b"), "../../a/b");
+  ASSERT_EQ(NormalizePath("./../../a/b"), "../../a/b");
 }
 
 }  // namespace
diff --git a/testcase/err_ifdef_two_args.mk b/testcase/err_ifdef_two_args.mk
new file mode 100644
index 0000000..4c9eb74
--- /dev/null
+++ b/testcase/err_ifdef_two_args.mk
@@ -0,0 +1,4 @@
+# TODO(go): Fix
+
+ifdef a b
+endif
diff --git a/testcase/err_ifdef_two_args2.mk b/testcase/err_ifdef_two_args2.mk
new file mode 100644
index 0000000..43231f7
--- /dev/null
+++ b/testcase/err_ifdef_two_args2.mk
@@ -0,0 +1,5 @@
+# TODO(go): Fix
+
+x := a b
+ifdef $(x)
+endif
diff --git a/testcase/excl_in_shell.mk b/testcase/excl_in_shell.mk
new file mode 100644
index 0000000..1eccf4d
--- /dev/null
+++ b/testcase/excl_in_shell.mk
@@ -0,0 +1,2 @@
+test:
+	@if ! false; then echo PASS; else echo FAIL; fi
diff --git a/testcase/find_command.mk b/testcase/find_command.mk
index 0947091..a0bbc82 100644
--- a/testcase/find_command.mk
+++ b/testcase/find_command.mk
@@ -1,5 +1,5 @@
-# TODO(ninja): This test is only for ckati. ninja: multiple problems
-# go: implement generic builtin find
+# TODO(go|ninja): This test is only for ckati. ninja: multiple problems
+# go: symlink support isn't enough.
 # ninja: find . finds ninja temporary files
 # ninja: escaping ! doesn't seem to be working
 # ninja: stderr gets reordered
@@ -33,6 +33,20 @@
 	mkdir -p build/tools
 	cp ../../testcase/tools/findleaves.py build/tools
 
+	mkdir -p testdir3/b/c/d
+	ln -s b testdir3/a
+	touch testdir3/b/c/d/e
+
+	mkdir -p testdir4/a/b
+	ln -s self testdir4/self
+	ln -s .. testdir4/a/b/c
+	ln -s b testdir4/a/l
+
+	mkdir -p testdir5
+	ln -s a testdir5/a
+	ln -s b testdir5/c
+	ln -s c testdir5/b
+
 test2:
 	@echo no options
 	$(call run_find, find testdir)
@@ -94,14 +108,27 @@
 	$(call run_find, find testdir -maxdepth 1hoge)
 	$(call run_find, find testdir -maxdepth -1)
 	@echo findleaves
-	$(call run_find, build/tools/findleaves.py . file1)
-	$(call run_find, build/tools/findleaves.py . file3)
-	$(call run_find, build/tools/findleaves.py --prune=dir1 . file3)
-	$(call run_find, build/tools/findleaves.py --prune=dir1 --prune=dir2 . file3)
-	$(call run_find, build/tools/findleaves.py --mindepth=1 . file1)
-	$(call run_find, build/tools/findleaves.py --mindepth=2 . file1)
-	$(call run_find, build/tools/findleaves.py --mindepth=3 . file1)
+	$(call run_find, build/tools/findleaves.py testdir file1)
+	$(call run_find, build/tools/findleaves.py testdir file3)
+	$(call run_find, build/tools/findleaves.py --prune=dir1 testdir file3)
+	$(call run_find, build/tools/findleaves.py --prune=dir1 --prune=dir2 testdir file3)
+	$(call run_find, build/tools/findleaves.py --mindepth=1 testdir file1)
+	$(call run_find, build/tools/findleaves.py --mindepth=2 testdir file1)
+	$(call run_find, build/tools/findleaves.py --mindepth=3 testdir file1)
 	$(call run_find, build/tools/findleaves.py --mindepth=2 testdir file1)
 	@echo missing chdir / testdir
 	$(call run_find, cd xxx && find .)
 	$(call run_find, if [ -d xxx ]; then find .; fi)
+
+test3:
+	$(call run_find, find testdir3/a/c)
+	$(call run_find, if [ -d testdir3/a/c ]; then find testdir3/a/c; fi)
+	$(call run_find, cd testdir3/a/c && find .)
+	$(call run_find, build/tools/findleaves.py testdir3 e)
+
+test4:
+	$(call run_find, find -L testdir4)
+
+test5:
+	$(call run_find, find -L testdir5)
+	$(call run_find, build/tools/findleaves.py testdir5 x)
diff --git a/testcase/ifdef_ret_in_arg.mk b/testcase/ifdef_ret_in_arg.mk
new file mode 100644
index 0000000..91d5fca
--- /dev/null
+++ b/testcase/ifdef_ret_in_arg.mk
@@ -0,0 +1,8 @@
+define x
+a
+b
+endef
+$(x):=PASS
+ifdef $(x)
+$(info $($(x)))
+endif
diff --git a/testcase/ninja_normalized_path2.mk b/testcase/ninja_normalized_path2.mk
deleted file mode 100644
index 0ec71f0..0000000
--- a/testcase/ninja_normalized_path2.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-# Ninja only supports duplicated targets for pure dependencies.
-# These will both be mapped to the same target. Two rules like
-# this should cause an warning (and really should cause an warning
-# in make too -- this can be very confusing, and can be racy)
-ifndef KATI
-$(info ninja: warning: multiple rules generate a/b.)
-endif
-
-test: a/b a/./b
-
-a/b:
-	mkdir -p $(dir $@)
-	echo $@
-
-a/./b:
diff --git a/testcase/ninja_regen_find_link.sh b/testcase/ninja_regen_find_link.sh
new file mode 100755
index 0000000..94a6bb1
--- /dev/null
+++ b/testcase/ninja_regen_find_link.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright 2015 Google Inc. All rights reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+mk="$@"
+if echo "${mk}" | grep kati > /dev/null; then
+  mk="${mk} --use_find_emulator"
+fi
+function build() {
+  ${mk} $@ 2> /dev/null
+  if [ -e ninja.sh ]; then ./ninja.sh -j1 $@; fi
+}
+
+cat <<EOF > Makefile
+V := \$(shell find -L linkdir/d/link)
+all:
+	@echo \$(V)
+EOF
+
+mkdir -p dir1 dir2 linkdir/d
+touch dir1/file1 dir2/file2
+ln -s ../../dir1 linkdir/d/link
+build
+
+touch dir1/file1_2
+build
+
+rm linkdir/d/link
+ln -s ../../dir2 linkdir/d/link
+build
diff --git a/testcase/realpath.mk b/testcase/realpath.mk
index 8afb791..0d57e3b 100644
--- a/testcase/realpath.mk
+++ b/testcase/realpath.mk
@@ -1,9 +1,13 @@
 foo = $(realpath ./foo)
 bar = $(realpath ./bar)
+foofoo = $(realpath ./foo ./foo)
+foobar = $(realpath ./foo ./bar)
 
 test: foo
 	echo $(foo)
 	echo $(bar)
+	echo $(foofoo)
+	echo $(foobar)
 
 foo:
 	touch foo
diff --git a/testcase/wildcard.mk b/testcase/wildcard.mk
index 884605e..33adfd6 100644
--- a/testcase/wildcard.mk
+++ b/testcase/wildcard.mk
@@ -1,3 +1,5 @@
+# TODO(go): Fix
+
 MAKEVER:=$(shell make --version | ruby -n0e 'puts $$_[/Make (\d)/,1]')
 
 files = $(wildcard M*)