[C++] Re-generate ninja file when a file is added/removed

With this change, we store the results of file list related
commands in .kati_stamp. If one of them has been changed,
we re-generate ninja file.

Currently, this check is slow. We need to check the timestamp
of directories first like what we are doing for $(wildcard).
diff --git a/func.cc b/func.cc
index 9cb86c1..e9cdca3 100644
--- a/func.cc
+++ b/func.cc
@@ -462,18 +462,6 @@
 
 //#define TEST_FIND_EMULATOR
 
-#ifdef TEST_FIND_EMULATOR
-static string SortWordsInString(StringPiece s) {
-  vector<string> toks;
-  for (StringPiece tok : WordScanner(s)) {
-    toks.push_back(tok.as_string());
-  }
-  sort(toks.begin(), toks.end());
-  return JoinStrings(toks, " ");
-}
-#endif
-
-
 // A hack for Android build. We need to evaluate things like $((3+4))
 // when we emit ninja file, because the result of such expressions
 // will be passed to other make functions.
@@ -487,6 +475,55 @@
   return false;
 }
 
+static void ShellFuncImpl(const string& shell, const string& cmd,
+                          bool is_file_list_command,
+                          string* s, FindCommand** fc) {
+  LOG("ShellFunc: %s", cmd.c_str());
+
+#ifdef TEST_FIND_EMULATOR
+  bool need_check = false;
+  string out2;
+#endif
+  if (FindEmulator::Get()) {
+    *fc = new FindCommand();
+    if ((*fc)->Parse(cmd)) {
+#ifdef TEST_FIND_EMULATOR
+      if (FindEmulator::Get()->HandleFind(cmd, **fc, &out2)) {
+        need_check = true;
+      }
+#else
+      if (FindEmulator::Get()->HandleFind(cmd, **fc, s)) {
+        *s = SortWordsInString(*s);
+        return;
+      }
+#endif
+    }
+    delete *fc;
+    *fc = NULL;
+  }
+
+  COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
+  RunCommand(shell, cmd, RedirectStderr::NONE, s);
+  if (is_file_list_command) {
+    *s = SortWordsInString(*s);
+  } else {
+    FormatForCommandSubstitution(s);
+  }
+
+#ifdef TEST_FIND_EMULATOR
+  if (need_check) {
+    string sorted = SortWordsInString(*s);
+    out2 = SortWordsInString(out2);
+    if (sorted != out2) {
+      ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
+            cmd.c_str(), sorted.c_str(), out2.c_str());
+    }
+  }
+#endif
+}
+
+static vector<FileListCommand*> g_file_list_commands;
+
 void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
   shared_ptr<string> cmd = args[0]->Eval(ev);
   if (ev->avoid_io() && !HasNoIoInShellScript(*cmd)) {
@@ -496,42 +533,23 @@
     return;
   }
 
-  LOG("ShellFunc: %s", cmd->c_str());
-
-#ifdef TEST_FIND_EMULATOR
-  bool need_check = false;
-  string out2;
-  if (FindEmulator::Get() && FindEmulator::Get()->HandleFind(*cmd, &out2)) {
-    need_check = true;
-  }
-#else
-  if (FindEmulator::Get() && FindEmulator::Get()->HandleFind(*cmd, s))
-    return;
-#endif
-
-  COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd->c_str());
-  string out;
   shared_ptr<string> shell = ev->EvalVar(kShellSym);
-  RunCommand(*shell, *cmd, false, &out);
+  const bool is_file_list_command =
+      ((*shell == "/bin/sh" || *shell == "/bin/bash") &&
+       (HasWord(*cmd, "find") || HasWord(*cmd, "ls") ||
+        // For Android.
+        cmd->find("/findleaves.py ") != string::npos));
 
-  while (out[out.size()-1] == '\n')
-    out.pop_back();
-  for (size_t i = 0; i < out.size(); i++) {
-    if (out[i] == '\n')
-      out[i] = ' ';
+  string out;
+  FindCommand* fc = NULL;
+  ShellFuncImpl(*shell, *cmd, is_file_list_command, &out, &fc);
+  if (is_file_list_command) {
+    FileListCommand* flc = new FileListCommand();
+    flc->cmd = *cmd;
+    flc->find.reset(fc);
+    flc->result = out;
+    g_file_list_commands.push_back(flc);
   }
-
-#ifdef TEST_FIND_EMULATOR
-  if (need_check) {
-    string sorted = SortWordsInString(out);
-    out2 = SortWordsInString(out2);
-    if (sorted != out2) {
-      ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
-            cmd->c_str(), sorted.c_str(), out2.c_str());
-    }
-  }
-#endif
-
   *s += out;
 }
 
@@ -688,3 +706,7 @@
     return NULL;
   return found->second;
 }
+
+const vector<FileListCommand*>& GetFileListCommmands() {
+  return g_file_list_commands;
+}