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