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