func.cc revision e41c7556c22bda359c2b97cd98d59082110add95
1// 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 "func.h"
18
19#include <errno.h>
20#include <fcntl.h>
21#include <limits.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <sys/stat.h>
25#include <unistd.h>
26
27#include <algorithm>
28#include <iterator>
29#include <memory>
30#include <unordered_map>
31
32#include "eval.h"
33#include "fileutil.h"
34#include "find.h"
35#include "log.h"
36#include "parser.h"
37#include "stats.h"
38#include "stmt.h"
39#include "strutil.h"
40#include "symtab.h"
41#include "var.h"
42
43namespace {
44
45// TODO: This code is very similar to
46// NinjaGenerator::TranslateCommand. Factor them out.
47void StripShellComment(string* cmd) {
48  if (cmd->find('#') == string::npos)
49    return;
50
51  string res;
52  bool prev_backslash = false;
53  // Set space as an initial value so the leading comment will be
54  // stripped out.
55  char prev_char = ' ';
56  char quote = 0;
57  bool done = false;
58  const char* in = cmd->c_str();
59  for (; *in && !done; in++) {
60    switch (*in) {
61      case '#':
62        if (quote == 0 && isspace(prev_char)) {
63          while (in[1] && *in != '\n')
64            in++;
65          break;
66        }
67
68      case '\'':
69      case '"':
70      case '`':
71        if (quote) {
72          if (quote == *in)
73            quote = 0;
74        } else if (!prev_backslash) {
75          quote = *in;
76        }
77        res += *in;
78        break;
79
80      case '\\':
81        res += '\\';
82        break;
83
84      default:
85        res += *in;
86    }
87
88    if (*in == '\\') {
89      prev_backslash = !prev_backslash;
90    } else {
91      prev_backslash = false;
92    }
93
94    prev_char = *in;
95  }
96  cmd->swap(res);
97}
98
99void PatsubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
100  const string&& pat_str = args[0]->Eval(ev);
101  const string&& repl = args[1]->Eval(ev);
102  const string&& str = args[2]->Eval(ev);
103  WordWriter ww(s);
104  Pattern pat(pat_str);
105  for (StringPiece tok : WordScanner(str)) {
106    ww.MaybeAddWhitespace();
107    pat.AppendSubst(tok, repl, s);
108  }
109}
110
111void StripFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
112  const string&& str = args[0]->Eval(ev);
113  WordWriter ww(s);
114  for (StringPiece tok : WordScanner(str)) {
115    ww.Write(tok);
116  }
117}
118
119void SubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
120  const string&& pat = args[0]->Eval(ev);
121  const string&& repl = args[1]->Eval(ev);
122  const string&& str = args[2]->Eval(ev);
123  if (pat.empty()) {
124    *s += str;
125    *s += repl;
126    return;
127  }
128  size_t index = 0;
129  while (index < str.size()) {
130    size_t found = str.find(pat, index);
131    if (found == string::npos)
132      break;
133    AppendString(StringPiece(str).substr(index, found - index), s);
134    AppendString(repl, s);
135    index = found + pat.size();
136  }
137  AppendString(StringPiece(str).substr(index), s);
138}
139
140void FindstringFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
141  const string&& find = args[0]->Eval(ev);
142  const string&& in = args[1]->Eval(ev);
143  if (in.find(find) != string::npos)
144    AppendString(find, s);
145}
146
147void FilterFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
148  const string&& pat_buf = args[0]->Eval(ev);
149  const string&& text = args[1]->Eval(ev);
150  vector<Pattern> pats;
151  for (StringPiece pat : WordScanner(pat_buf)) {
152    pats.push_back(Pattern(pat));
153  }
154  WordWriter ww(s);
155  for (StringPiece tok : WordScanner(text)) {
156    for (const Pattern& pat : pats) {
157      if (pat.Match(tok)) {
158        ww.Write(tok);
159        break;
160      }
161    }
162  }
163}
164
165void FilterOutFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
166  const string&& pat_buf = args[0]->Eval(ev);
167  const string&& text = args[1]->Eval(ev);
168  vector<Pattern> pats;
169  for (StringPiece pat : WordScanner(pat_buf)) {
170    pats.push_back(Pattern(pat));
171  }
172  WordWriter ww(s);
173  for (StringPiece tok : WordScanner(text)) {
174    bool matched = false;
175    for (const Pattern& pat : pats) {
176      if (pat.Match(tok)) {
177        matched = true;
178        break;
179      }
180    }
181    if (!matched)
182      ww.Write(tok);
183  }
184}
185
186void SortFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
187  string list;
188  args[0]->Eval(ev, &list);
189  COLLECT_STATS("func sort time");
190  // TODO(hamaji): Probably we could use a faster string-specific sort
191  // algorithm.
192  vector<StringPiece> toks;
193  WordScanner(list).Split(&toks);
194  stable_sort(toks.begin(), toks.end());
195  WordWriter ww(s);
196  StringPiece prev;
197  for (StringPiece tok : toks) {
198    if (prev != tok) {
199      ww.Write(tok);
200      prev = tok;
201    }
202  }
203}
204
205static int GetNumericValueForFunc(const string& buf) {
206  StringPiece s = TrimLeftSpace(buf);
207  char* end;
208  long n = strtol(s.data(), &end, 10);
209  if (n < 0 || n == LONG_MAX || s.data() + s.size() != end) {
210    return -1;
211  }
212  return n;
213}
214
215void WordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
216  const string&& n_str = args[0]->Eval(ev);
217  int n = GetNumericValueForFunc(n_str);
218  if (n < 0) {
219    ev->Error(StringPrintf(
220        "*** non-numeric first argument to `word' function: '%s'.",
221        n_str.c_str()));
222  }
223  if (n == 0) {
224    ev->Error("*** first argument to `word' function must be greater than 0.");
225  }
226
227  const string&& text = args[1]->Eval(ev);
228  for (StringPiece tok : WordScanner(text)) {
229    n--;
230    if (n == 0) {
231      AppendString(tok, s);
232      break;
233    }
234  }
235}
236
237void WordlistFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
238  const string&& s_str = args[0]->Eval(ev);
239  int si = GetNumericValueForFunc(s_str);
240  if (si < 0) {
241    ev->Error(StringPrintf(
242        "*** non-numeric first argument to `wordlist' function: '%s'.",
243        s_str.c_str()));
244  }
245  if (si == 0) {
246    ev->Error(StringPrintf(
247        "*** invalid first argument to `wordlist' function: %s`",
248        s_str.c_str()));
249  }
250
251  const string&& e_str = args[1]->Eval(ev);
252  int ei = GetNumericValueForFunc(e_str);
253  if (ei < 0) {
254    ev->Error(StringPrintf(
255        "*** non-numeric second argument to `wordlist' function: '%s'.",
256        e_str.c_str()));
257  }
258
259  const string&& text = args[2]->Eval(ev);
260  int i = 0;
261  WordWriter ww(s);
262  for (StringPiece tok : WordScanner(text)) {
263    i++;
264    if (si <= i && i <= ei) {
265      ww.Write(tok);
266    }
267  }
268}
269
270void WordsFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
271  const string&& text = args[0]->Eval(ev);
272  WordScanner ws(text);
273  int n = 0;
274  for (auto iter = ws.begin(); iter != ws.end(); ++iter)
275    n++;
276  char buf[32];
277  sprintf(buf, "%d", n);
278  *s += buf;
279}
280
281void FirstwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
282  const string&& text = args[0]->Eval(ev);
283  for (StringPiece tok : WordScanner(text)) {
284    AppendString(tok, s);
285    return;
286  }
287}
288
289void LastwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
290  const string&& text = args[0]->Eval(ev);
291  StringPiece last;
292  for (StringPiece tok : WordScanner(text)) {
293    last = tok;
294  }
295  AppendString(last, s);
296}
297
298void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
299  const string&& list1 = args[0]->Eval(ev);
300  const string&& list2 = args[1]->Eval(ev);
301  WordScanner ws1(list1);
302  WordScanner ws2(list2);
303  WordWriter ww(s);
304  WordScanner::Iterator iter1, iter2;
305  for (iter1 = ws1.begin(), iter2 = ws2.begin();
306       iter1 != ws1.end() && iter2 != ws2.end();
307       ++iter1, ++iter2) {
308    ww.Write(*iter1);
309    // Use |AppendString| not to append extra ' '.
310    AppendString(*iter2, s);
311  }
312  for (; iter1 != ws1.end(); ++iter1)
313    ww.Write(*iter1);
314  for (; iter2 != ws2.end(); ++iter2)
315    ww.Write(*iter2);
316}
317
318void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
319  const string&& pat = args[0]->Eval(ev);
320  COLLECT_STATS("func wildcard time");
321  // Note GNU make does not delay the execution of $(wildcard) so we
322  // do not need to check avoid_io here.
323  WordWriter ww(s);
324  vector<string>* files;
325  for (StringPiece tok : WordScanner(pat)) {
326    ScopedTerminator st(tok);
327    Glob(tok.data(), &files);
328    for (const string& file : *files) {
329      ww.Write(file);
330    }
331  }
332}
333
334void DirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
335  const string&& text = args[0]->Eval(ev);
336  WordWriter ww(s);
337  for (StringPiece tok : WordScanner(text)) {
338    ww.Write(Dirname(tok));
339    s->push_back('/');
340  }
341}
342
343void NotdirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
344  const string&& text = args[0]->Eval(ev);
345  WordWriter ww(s);
346  for (StringPiece tok : WordScanner(text)) {
347    if (tok == "/") {
348      ww.Write(StringPiece(""));
349    } else {
350      ww.Write(Basename(tok));
351    }
352  }
353}
354
355void SuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
356  const string&& text = args[0]->Eval(ev);
357  WordWriter ww(s);
358  for (StringPiece tok : WordScanner(text)) {
359    StringPiece suf = GetExt(tok);
360    if (!suf.empty())
361      ww.Write(suf);
362  }
363}
364
365void BasenameFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
366  const string&& text = args[0]->Eval(ev);
367  WordWriter ww(s);
368  for (StringPiece tok : WordScanner(text)) {
369    ww.Write(StripExt(tok));
370  }
371}
372
373void AddsuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
374  const string&& suf = args[0]->Eval(ev);
375  const string&& text = args[1]->Eval(ev);
376  WordWriter ww(s);
377  for (StringPiece tok : WordScanner(text)) {
378    ww.Write(tok);
379    *s += suf;
380  }
381}
382
383void AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
384  const string&& pre = args[0]->Eval(ev);
385  const string&& text = args[1]->Eval(ev);
386  WordWriter ww(s);
387  for (StringPiece tok : WordScanner(text)) {
388    ww.Write(pre);
389    AppendString(tok, s);
390  }
391}
392
393void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
394  const string&& text = args[0]->Eval(ev);
395  if (ev->avoid_io()) {
396    *s += "$(";
397    string kati_binary;
398    GetExecutablePath(&kati_binary);
399    *s += kati_binary;
400    *s += " --realpath ";
401    *s += text;
402    *s += " 2> /dev/null)";
403    return;
404  }
405
406  WordWriter ww(s);
407  for (StringPiece tok : WordScanner(text)) {
408    ScopedTerminator st(tok);
409    char buf[PATH_MAX];
410    if (realpath(tok.data(), buf))
411      ww.Write(buf);
412  }
413}
414
415void AbspathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
416  const string&& text = args[0]->Eval(ev);
417  WordWriter ww(s);
418  string buf;
419  for (StringPiece tok : WordScanner(text)) {
420    AbsPath(tok, &buf);
421    ww.Write(buf);
422  }
423}
424
425void IfFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
426  const string&& cond = args[0]->Eval(ev);
427  if (cond.empty()) {
428    if (args.size() > 2)
429      args[2]->Eval(ev, s);
430  } else {
431    args[1]->Eval(ev, s);
432  }
433}
434
435void AndFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
436  string cond;
437  for (Value* a : args) {
438    cond = a->Eval(ev);
439    if (cond.empty())
440      return;
441  }
442  if (!cond.empty()) {
443    *s += cond;
444  }
445}
446
447void OrFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
448  for (Value* a : args) {
449    const string&& cond = a->Eval(ev);
450    if (!cond.empty()) {
451      *s += cond;
452      return;
453    }
454  }
455}
456
457void ValueFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
458  const string&& var_name = args[0]->Eval(ev);
459  Var* var = ev->LookupVar(Intern(var_name));
460  AppendString(var->String().as_string(), s);
461}
462
463void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) {
464  // TODO: eval leaks everything... for now.
465  //const string text = args[0]->Eval(ev);
466  string* text = new string;
467  args[0]->Eval(ev, text);
468  if (ev->avoid_io()) {
469    KATI_WARN_LOC(ev->loc(), "*warning*: $(eval) in a recipe is not recommended: %s",
470                  text->c_str());
471  }
472  vector<Stmt*> stmts;
473  Parse(*text, ev->loc(), &stmts);
474  for (Stmt* stmt : stmts) {
475    LOG("%s", stmt->DebugString().c_str());
476    stmt->Eval(ev);
477    //delete stmt;
478  }
479}
480
481//#define TEST_FIND_EMULATOR
482
483// A hack for Android build. We need to evaluate things like $((3+4))
484// when we emit ninja file, because the result of such expressions
485// will be passed to other make functions.
486// TODO: Maybe we should introduce a helper binary which evaluate
487// make expressions at ninja-time.
488static bool HasNoIoInShellScript(const string& cmd) {
489  if (cmd.empty())
490    return true;
491  if (HasPrefix(cmd, "echo $((") && cmd[cmd.size()-1] == ')')
492    return true;
493  return false;
494}
495
496static void ShellFuncImpl(const string& shell, const string& shellflag,
497                          const string& cmd, string* s, FindCommand** fc) {
498  LOG("ShellFunc: %s", cmd.c_str());
499
500#ifdef TEST_FIND_EMULATOR
501  bool need_check = false;
502  string out2;
503#endif
504  if (FindEmulator::Get()) {
505    *fc = new FindCommand();
506    if ((*fc)->Parse(cmd)) {
507#ifdef TEST_FIND_EMULATOR
508      if (FindEmulator::Get()->HandleFind(cmd, **fc, &out2)) {
509        need_check = true;
510      }
511#else
512      if (FindEmulator::Get()->HandleFind(cmd, **fc, s)) {
513        return;
514      }
515#endif
516    }
517    delete *fc;
518    *fc = NULL;
519  }
520
521  COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
522  RunCommand(shell, shellflag, cmd, RedirectStderr::NONE, s);
523  FormatForCommandSubstitution(s);
524
525#ifdef TEST_FIND_EMULATOR
526  if (need_check) {
527    if (*s != out2) {
528      ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
529            cmd.c_str(), s->c_str(), out2.c_str());
530    }
531  }
532#endif
533}
534
535static vector<CommandResult*> g_command_results;
536
537bool ShouldStoreCommandResult(StringPiece cmd) {
538  // We really just want to ignore this one, or remove BUILD_DATETIME from
539  // Android completely
540  if (cmd == "date +%s")
541    return false;
542
543  Pattern pat(g_flags.ignore_dirty_pattern);
544  Pattern nopat(g_flags.no_ignore_dirty_pattern);
545  for (StringPiece tok : WordScanner(cmd)) {
546    if (pat.Match(tok) && !nopat.Match(tok)) {
547      return false;
548    }
549  }
550
551  return true;
552}
553
554void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
555  string cmd = args[0]->Eval(ev);
556  if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
557    if (ev->eval_depth() > 1) {
558      ERROR_LOC(ev->loc(), "kati doesn't support passing results of $(shell) "
559                "to other make constructs: %s",
560                cmd.c_str());
561    }
562    StripShellComment(&cmd);
563    *s += "$(";
564    *s += cmd;
565    *s += ")";
566    return;
567  }
568
569  const string&& shell = ev->GetShell();
570  const string&& shellflag = ev->GetShellFlag();
571
572  string out;
573  FindCommand* fc = NULL;
574  ShellFuncImpl(shell, shellflag, cmd, &out, &fc);
575  if (ShouldStoreCommandResult(cmd)) {
576    CommandResult* cr = new CommandResult();
577    cr->op = (fc == NULL) ? CommandOp::SHELL : CommandOp::FIND,
578    cr->shell = shell;
579    cr->shellflag = shellflag;
580    cr->cmd = cmd;
581    cr->find.reset(fc);
582    cr->result = out;
583    g_command_results.push_back(cr);
584  }
585  *s += out;
586}
587
588void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
589  static const Symbol tmpvar_names[] = {
590    Intern("0"), Intern("1"),  Intern("2"), Intern("3"), Intern("4"),
591    Intern("5"), Intern("6"),  Intern("7"), Intern("8"), Intern("9")
592  };
593
594  const string&& func_name_buf = args[0]->Eval(ev);
595  const StringPiece func_name = TrimSpace(func_name_buf);
596  Var* func = ev->LookupVar(Intern(func_name));
597  if (!func->IsDefined()) {
598    KATI_WARN_LOC(ev->loc(), "*warning*: undefined user function: %s",
599                  func_name.as_string().c_str());
600  }
601  vector<unique_ptr<SimpleVar>> av;
602  for (size_t i = 1; i < args.size(); i++) {
603    unique_ptr<SimpleVar> s(
604        new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC));
605    av.push_back(move(s));
606  }
607  vector<unique_ptr<ScopedGlobalVar>> sv;
608  for (size_t i = 1; ; i++) {
609    string s;
610    Symbol tmpvar_name_sym(Symbol::IsUninitialized{});
611    if (i < sizeof(tmpvar_names)/sizeof(tmpvar_names[0])) {
612      tmpvar_name_sym = tmpvar_names[i];
613    } else {
614      s = StringPrintf("%d", i);
615      tmpvar_name_sym = Intern(s);
616    }
617    if (i < args.size()) {
618      sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get()));
619    } else {
620      // We need to blank further automatic vars
621      Var *v = ev->LookupVar(tmpvar_name_sym);
622      if (!v->IsDefined()) break;
623      if (v->Origin() != VarOrigin::AUTOMATIC) break;
624
625      av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC));
626      sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i-1].get()));
627    }
628  }
629
630  ev->DecrementEvalDepth();
631  func->Eval(ev, s);
632  ev->IncrementEvalDepth();
633}
634
635void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
636  const string&& varname = args[0]->Eval(ev);
637  const string&& list = args[1]->Eval(ev);
638  ev->DecrementEvalDepth();
639  WordWriter ww(s);
640  for (StringPiece tok : WordScanner(list)) {
641    unique_ptr<SimpleVar> v(new SimpleVar(
642        tok.as_string(), VarOrigin::AUTOMATIC));
643    ScopedGlobalVar sv(Intern(varname), v.get());
644    ww.MaybeAddWhitespace();
645    args[2]->Eval(ev, s);
646  }
647  ev->IncrementEvalDepth();
648}
649
650void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
651  const string&& var_name = args[0]->Eval(ev);
652  Var* var = ev->LookupVar(Intern(var_name));
653  *s += GetOriginStr(var->Origin());
654}
655
656void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
657  const string&& var_name = args[0]->Eval(ev);
658  Var* var = ev->LookupVar(Intern(var_name));
659  *s += var->Flavor();
660}
661
662void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
663  const string&& a = args[0]->Eval(ev);
664  if (ev->avoid_io()) {
665    ev->add_delayed_output_command(StringPrintf("echo -e \"%s\"", EchoEscape(a).c_str()));
666    return;
667  }
668  printf("%s\n", a.c_str());
669  fflush(stdout);
670}
671
672void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) {
673  const string&& a = args[0]->Eval(ev);
674  if (ev->avoid_io()) {
675    ev->add_delayed_output_command(
676        StringPrintf("echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str()));
677    return;
678  }
679  WARN_LOC(ev->loc(), "%s", a.c_str());
680}
681
682void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
683  const string&& a = args[0]->Eval(ev);
684  if (ev->avoid_io()) {
685    ev->add_delayed_output_command(
686        StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false",
687                     LOCF(ev->loc()), EchoEscape(a).c_str()));
688    return;
689  }
690  ev->Error(StringPrintf("*** %s.", a.c_str()));
691}
692
693static void FileReadFunc(Evaluator* ev, const string& filename, string* s) {
694  int fd = open(filename.c_str(), O_RDONLY);
695  if (fd < 0) {
696    if (errno == ENOENT) {
697      if (ShouldStoreCommandResult(filename)) {
698        CommandResult* cr = new CommandResult();
699        cr->op = CommandOp::READ_MISSING;
700        cr->cmd = filename;
701        g_command_results.push_back(cr);
702      }
703      return;
704    } else {
705      ev->Error("*** open failed.");
706    }
707  }
708
709  struct stat st;
710  if (fstat(fd, &st) < 0) {
711    ev->Error("*** fstat failed.");
712  }
713
714  size_t len = st.st_size;
715  string out;
716  out.resize(len);
717  ssize_t r = HANDLE_EINTR(read(fd, &out[0], len));
718  if (r != static_cast<ssize_t>(len)) {
719    ev->Error("*** read failed.");
720  }
721
722  if (close(fd) < 0) {
723    ev->Error("*** close failed.");
724  }
725
726  if (out.back() == '\n') {
727    out.pop_back();
728  }
729
730  if (ShouldStoreCommandResult(filename)) {
731    CommandResult* cr = new CommandResult();
732    cr->op = CommandOp::READ;
733    cr->cmd = filename;
734    g_command_results.push_back(cr);
735  }
736  *s += out;
737}
738
739static void FileWriteFunc(Evaluator* ev, const string& filename, bool append, string text) {
740  FILE* f = fopen(filename.c_str(), append ? "ab" : "wb");
741  if (f == NULL) {
742    ev->Error("*** fopen failed.");
743  }
744
745  if (fwrite(&text[0], text.size(), 1, f) != 1) {
746    ev->Error("*** fwrite failed.");
747  }
748
749  if (fclose(f) != 0) {
750    ev->Error("*** fclose failed.");
751  }
752
753  if (ShouldStoreCommandResult(filename)) {
754    CommandResult* cr = new CommandResult();
755    cr->op = CommandOp::WRITE;
756    cr->cmd = filename;
757    cr->result = text;
758    g_command_results.push_back(cr);
759  }
760}
761
762void FileFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
763  if (ev->avoid_io()) {
764    ev->Error("*** $(file ...) is not supported in rules.");
765  }
766
767  string arg = args[0]->Eval(ev);
768  StringPiece filename = TrimSpace(arg);
769
770  if (filename.size() <= 1) {
771    ev->Error("*** Missing filename");
772  }
773
774  if (filename[0] == '<') {
775    filename = TrimLeftSpace(filename.substr(1));
776    if (!filename.size()) {
777      ev->Error("*** Missing filename");
778    }
779    if (args.size() > 1) {
780      ev->Error("*** invalid argument");
781    }
782
783    FileReadFunc(ev, filename.as_string(), s);
784  } else if (filename[0] == '>') {
785    bool append = false;
786    if (filename[1] == '>') {
787      append = true;
788      filename = filename.substr(2);
789    } else {
790      filename = filename.substr(1);
791    }
792    filename = TrimLeftSpace(filename);
793    if (!filename.size()) {
794      ev->Error("*** Missing filename");
795    }
796
797    string text;
798    if (args.size() > 1) {
799      text = args[1]->Eval(ev);
800      if (text.size() == 0 || text.back() != '\n') {
801        text.push_back('\n');
802      }
803    }
804
805    FileWriteFunc(ev, filename.as_string(), append, text);
806  } else {
807    ev->Error(StringPrintf("*** Invalid file operation: %s.  Stop.", filename.as_string().c_str()));
808  }
809}
810
811FuncInfo g_func_infos[] = {
812  { "patsubst", &PatsubstFunc, 3, 3, false, false },
813  { "strip", &StripFunc, 1, 1, false, false },
814  { "subst", &SubstFunc, 3, 3, false, false },
815  { "findstring", &FindstringFunc, 2, 2, false, false },
816  { "filter", &FilterFunc, 2, 2, false, false },
817  { "filter-out", &FilterOutFunc, 2, 2, false, false },
818  { "sort", &SortFunc, 1, 1, false, false },
819  { "word", &WordFunc, 2, 2, false, false },
820  { "wordlist", &WordlistFunc, 3, 3, false, false },
821  { "words", &WordsFunc, 1, 1, false, false },
822  { "firstword", &FirstwordFunc, 1, 1, false, false },
823  { "lastword", &LastwordFunc, 1, 1, false, false },
824
825  { "join", &JoinFunc, 2, 2, false, false },
826  { "wildcard", &WildcardFunc, 1, 1, false, false },
827  { "dir", &DirFunc, 1, 1, false, false },
828  { "notdir", &NotdirFunc, 1, 1, false, false },
829  { "suffix", &SuffixFunc, 1, 1, false, false },
830  { "basename", &BasenameFunc, 1, 1, false, false },
831  { "addsuffix", &AddsuffixFunc, 2, 2, false, false },
832  { "addprefix", &AddprefixFunc, 2, 2, false, false },
833  { "realpath", &RealpathFunc, 1, 1, false, false },
834  { "abspath", &AbspathFunc, 1, 1, false, false },
835
836  { "if", &IfFunc, 3, 2, false, true },
837  { "and", &AndFunc, 0, 0, true, false },
838  { "or", &OrFunc, 0, 0, true, false },
839
840  { "value", &ValueFunc, 1, 1, false, false },
841  { "eval", &EvalFunc, 1, 1, false, false },
842  { "shell", &ShellFunc, 1, 1, false, false },
843  { "call", &CallFunc, 0, 0, false, false },
844  { "foreach", &ForeachFunc, 3, 3, false, false },
845
846  { "origin", &OriginFunc, 1, 1, false, false },
847  { "flavor", &FlavorFunc, 1, 1, false, false },
848
849  { "info", &InfoFunc, 1, 1, false, false },
850  { "warning", &WarningFunc, 1, 1, false, false },
851  { "error", &ErrorFunc, 1, 1, false, false },
852
853  { "file", &FileFunc, 2, 1, false, false },
854};
855
856unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
857
858}  // namespace
859
860void InitFuncTable() {
861  g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
862  for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
863    FuncInfo* fi = &g_func_infos[i];
864    bool ok = g_func_info_map->emplace(fi->name, fi).second;
865    CHECK(ok);
866  }
867}
868
869void QuitFuncTable() {
870  delete g_func_info_map;
871}
872
873FuncInfo* GetFuncInfo(StringPiece name) {
874  auto found = g_func_info_map->find(name);
875  if (found == g_func_info_map->end())
876    return NULL;
877  return found->second;
878}
879
880const vector<CommandResult*>& GetShellCommandResults() {
881  return g_command_results;
882}
883