func.cc revision cbb801c461562b7319f86a4889f5349e27484b8b
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  const string&& list = args[0]->Eval(ev);
186  vector<StringPiece> toks;
187  WordScanner(list).Split(&toks);
188  sort(toks.begin(), toks.end());
189  WordWriter ww(s);
190  StringPiece prev;
191  for (StringPiece tok : toks) {
192    if (prev != tok) {
193      ww.Write(tok);
194      prev = tok;
195    }
196  }
197}
198
199static int GetNumericValueForFunc(const string& buf) {
200  StringPiece s = TrimLeftSpace(buf);
201  char* end;
202  long n = strtol(s.data(), &end, 10);
203  if (n < 0 || n == LONG_MAX || s.data() + s.size() != end) {
204    return -1;
205  }
206  return n;
207}
208
209void WordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
210  const string&& n_str = args[0]->Eval(ev);
211  int n = GetNumericValueForFunc(n_str);
212  if (n < 0) {
213    ev->Error(StringPrintf(
214        "*** non-numeric first argument to `word' function: '%s'.",
215        n_str.c_str()));
216  }
217  if (n == 0) {
218    ev->Error("*** first argument to `word' function must be greater than 0.");
219  }
220
221  const string&& text = args[1]->Eval(ev);
222  for (StringPiece tok : WordScanner(text)) {
223    n--;
224    if (n == 0) {
225      AppendString(tok, s);
226      break;
227    }
228  }
229}
230
231void WordlistFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
232  const string&& s_str = args[0]->Eval(ev);
233  int si = GetNumericValueForFunc(s_str);
234  if (si < 0) {
235    ev->Error(StringPrintf(
236        "*** non-numeric first argument to `wordlist' function: '%s'.",
237        s_str.c_str()));
238  }
239  if (si == 0) {
240    ev->Error(StringPrintf(
241        "*** invalid first argument to `wordlist' function: %s`",
242        s_str.c_str()));
243  }
244
245  const string&& e_str = args[1]->Eval(ev);
246  int ei = GetNumericValueForFunc(e_str);
247  if (ei < 0) {
248    ev->Error(StringPrintf(
249        "*** non-numeric second argument to `wordlist' function: '%s'.",
250        e_str.c_str()));
251  }
252
253  const string&& text = args[2]->Eval(ev);
254  int i = 0;
255  WordWriter ww(s);
256  for (StringPiece tok : WordScanner(text)) {
257    i++;
258    if (si <= i && i <= ei) {
259      ww.Write(tok);
260    }
261  }
262}
263
264void WordsFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
265  const string&& text = args[0]->Eval(ev);
266  WordScanner ws(text);
267  int n = 0;
268  for (auto iter = ws.begin(); iter != ws.end(); ++iter)
269    n++;
270  char buf[32];
271  sprintf(buf, "%d", n);
272  *s += buf;
273}
274
275void FirstwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
276  const string&& text = args[0]->Eval(ev);
277  for (StringPiece tok : WordScanner(text)) {
278    AppendString(tok, s);
279    return;
280  }
281}
282
283void LastwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
284  const string&& text = args[0]->Eval(ev);
285  StringPiece last;
286  for (StringPiece tok : WordScanner(text)) {
287    last = tok;
288  }
289  AppendString(last, s);
290}
291
292void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
293  const string&& list1 = args[0]->Eval(ev);
294  const string&& list2 = args[1]->Eval(ev);
295  WordScanner ws1(list1);
296  WordScanner ws2(list2);
297  WordWriter ww(s);
298  for (WordScanner::Iterator iter1 = ws1.begin(), iter2 = ws2.begin();
299       iter1 != ws1.end() && iter2 != ws2.end();
300       ++iter1, ++iter2) {
301    ww.Write(*iter1);
302    // Use |AppendString| not to append extra ' '.
303    AppendString(*iter2, s);
304  }
305}
306
307void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
308  const string&& pat = args[0]->Eval(ev);
309  COLLECT_STATS("func wildcard time");
310  // Note GNU make does not delay the execution of $(wildcard) so we
311  // do not need to check avoid_io here.
312  WordWriter ww(s);
313  vector<string>* files;
314  for (StringPiece tok : WordScanner(pat)) {
315    ScopedTerminator st(tok);
316    Glob(tok.data(), &files);
317    sort(files->begin(), files->end());
318    for (const string& file : *files) {
319      ww.Write(file);
320    }
321  }
322}
323
324void DirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
325  const string&& text = args[0]->Eval(ev);
326  WordWriter ww(s);
327  for (StringPiece tok : WordScanner(text)) {
328    ww.Write(Dirname(tok));
329    s->push_back('/');
330  }
331}
332
333void NotdirFunc(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    if (tok == "/") {
338      ww.Write(StringPiece(""));
339    } else {
340      ww.Write(Basename(tok));
341    }
342  }
343}
344
345void SuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
346  const string&& text = args[0]->Eval(ev);
347  WordWriter ww(s);
348  for (StringPiece tok : WordScanner(text)) {
349    StringPiece suf = GetExt(tok);
350    if (!suf.empty())
351      ww.Write(suf);
352  }
353}
354
355void BasenameFunc(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    ww.Write(StripExt(tok));
360  }
361}
362
363void AddsuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
364  const string&& suf = args[0]->Eval(ev);
365  const string&& text = args[1]->Eval(ev);
366  WordWriter ww(s);
367  for (StringPiece tok : WordScanner(text)) {
368    ww.Write(tok);
369    *s += suf;
370  }
371}
372
373void AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
374  const string&& pre = 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(pre);
379    AppendString(tok, s);
380  }
381}
382
383void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
384  const string&& text = args[0]->Eval(ev);
385  if (ev->avoid_io()) {
386    *s += "$(";
387    string kati_binary;
388    GetExecutablePath(&kati_binary);
389    *s += kati_binary;
390    *s += " --realpath ";
391    *s += text;
392    *s += " 2> /dev/null)";
393    return;
394  }
395
396  WordWriter ww(s);
397  for (StringPiece tok : WordScanner(text)) {
398    ScopedTerminator st(tok);
399    char buf[PATH_MAX];
400    if (realpath(tok.data(), buf))
401      ww.Write(buf);
402  }
403}
404
405void AbspathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
406  const string&& text = args[0]->Eval(ev);
407  WordWriter ww(s);
408  string buf;
409  for (StringPiece tok : WordScanner(text)) {
410    AbsPath(tok, &buf);
411    ww.Write(buf);
412  }
413}
414
415void IfFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
416  const string&& cond = args[0]->Eval(ev);
417  if (cond.empty()) {
418    if (args.size() > 2)
419      args[2]->Eval(ev, s);
420  } else {
421    args[1]->Eval(ev, s);
422  }
423}
424
425void AndFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
426  string cond;
427  for (Value* a : args) {
428    cond = a->Eval(ev);
429    if (cond.empty())
430      return;
431  }
432  if (!cond.empty()) {
433    *s += cond;
434  }
435}
436
437void OrFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
438  for (Value* a : args) {
439    const string&& cond = a->Eval(ev);
440    if (!cond.empty()) {
441      *s += cond;
442      return;
443    }
444  }
445}
446
447void ValueFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
448  const string&& var_name = args[0]->Eval(ev);
449  Var* var = ev->LookupVar(Intern(var_name));
450  AppendString(var->String().as_string(), s);
451}
452
453void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) {
454  // TODO: eval leaks everything... for now.
455  //const string text = args[0]->Eval(ev);
456  string* text = new string;
457  args[0]->Eval(ev, text);
458  if ((*text)[0] == '#') {
459    delete text;
460    return;
461  }
462  if (ev->avoid_io()) {
463    KATI_WARN("%s:%d: *warning*: $(eval) in a recipe is not recommended: %s",
464              LOCF(ev->loc()), text->c_str());
465  }
466  vector<Stmt*> stmts;
467  Parse(*text, ev->loc(), &stmts);
468  for (Stmt* stmt : stmts) {
469    LOG("%s", stmt->DebugString().c_str());
470    stmt->Eval(ev);
471    //delete stmt;
472  }
473}
474
475//#define TEST_FIND_EMULATOR
476
477// A hack for Android build. We need to evaluate things like $((3+4))
478// when we emit ninja file, because the result of such expressions
479// will be passed to other make functions.
480// TODO: Maybe we should introduce a helper binary which evaluate
481// make expressions at ninja-time.
482static bool HasNoIoInShellScript(const string& cmd) {
483  if (cmd.empty())
484    return true;
485  if (HasPrefix(cmd, "echo $((") && cmd[cmd.size()-1] == ')')
486    return true;
487  return false;
488}
489
490static void ShellFuncImpl(const string& shell, const string& cmd,
491                          string* s, FindCommand** fc) {
492  LOG("ShellFunc: %s", cmd.c_str());
493
494#ifdef TEST_FIND_EMULATOR
495  bool need_check = false;
496  string out2;
497#endif
498  if (FindEmulator::Get()) {
499    *fc = new FindCommand();
500    if ((*fc)->Parse(cmd)) {
501#ifdef TEST_FIND_EMULATOR
502      if (FindEmulator::Get()->HandleFind(cmd, **fc, &out2)) {
503        need_check = true;
504      }
505#else
506      if (FindEmulator::Get()->HandleFind(cmd, **fc, s)) {
507        return;
508      }
509#endif
510    }
511    delete *fc;
512    *fc = NULL;
513  }
514
515  COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
516  RunCommand(shell, cmd, RedirectStderr::NONE, s);
517  FormatForCommandSubstitution(s);
518
519#ifdef TEST_FIND_EMULATOR
520  if (need_check) {
521    if (*s != out2) {
522      ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
523            cmd.c_str(), s->c_str(), out2.c_str());
524    }
525  }
526#endif
527}
528
529static vector<CommandResult*> g_command_results;
530
531bool ShouldStoreCommandResult(StringPiece cmd) {
532  if (HasWord(cmd, "date") || HasWord(cmd, "echo"))
533    return false;
534
535  Pattern pat(g_flags.ignore_dirty_pattern);
536  Pattern nopat(g_flags.no_ignore_dirty_pattern);
537  for (StringPiece tok : WordScanner(cmd)) {
538    if (pat.Match(tok) && !nopat.Match(tok)) {
539      return false;
540    }
541  }
542
543  return true;
544}
545
546void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
547  string cmd = args[0]->Eval(ev);
548  if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
549    if (ev->eval_depth() > 1) {
550      ERROR("%s:%d: kati doesn't support passing results of $(shell) "
551            "to other make constructs: %s",
552            LOCF(ev->loc()), cmd.c_str());
553    }
554    StripShellComment(&cmd);
555    *s += "$(";
556    *s += cmd;
557    *s += ")";
558    return;
559  }
560
561  const string&& shell = ev->EvalVar(kShellSym);
562
563  string out;
564  FindCommand* fc = NULL;
565  ShellFuncImpl(shell, cmd, &out, &fc);
566  if (ShouldStoreCommandResult(cmd)) {
567    CommandResult* cr = new CommandResult();
568    cr->cmd = cmd;
569    cr->find.reset(fc);
570    cr->result = out;
571    g_command_results.push_back(cr);
572  }
573  *s += out;
574}
575
576void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
577  static const string tmpvar_names[] = {
578    "0", "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9"
579  };
580
581  const string&& func_name = args[0]->Eval(ev);
582  Var* func = ev->LookupVar(Intern(func_name));
583  if (!func->IsDefined()) {
584    KATI_WARN("%s:%d: *warning*: undefined user function: %s",
585              ev->loc(), func_name.c_str());
586  }
587  vector<unique_ptr<SimpleVar>> av;
588  for (size_t i = 1; i < args.size(); i++) {
589    unique_ptr<SimpleVar> s(
590        new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC));
591    av.push_back(move(s));
592  }
593  vector<unique_ptr<ScopedVar>> sv;
594  for (size_t i = 1; ; i++) {
595    string s;
596    StringPiece tmpvar_name;
597    if (i < sizeof(tmpvar_names)/sizeof(tmpvar_names[0])) {
598      tmpvar_name = tmpvar_names[i];
599    } else {
600      s = StringPrintf("%d", i);
601      tmpvar_name = s;
602    }
603    if (i < args.size()) {
604      sv.emplace_back(new ScopedVar(ev->mutable_vars(),
605                                    Intern(tmpvar_name), av[i-1].get()));
606    } else {
607      // We need to blank further automatic vars
608      Var *v = ev->LookupVar(Intern(tmpvar_name));
609      if (!v->IsDefined()) break;
610      if (v->Origin() != VarOrigin::AUTOMATIC) break;
611
612      av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC));
613      sv.emplace_back(new ScopedVar(ev->mutable_vars(),
614                                    Intern(tmpvar_name), av[i-1].get()));
615    }
616  }
617
618  ev->DecrementEvalDepth();
619  func->Eval(ev, s);
620  ev->IncrementEvalDepth();
621}
622
623void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
624  const string&& varname = args[0]->Eval(ev);
625  const string&& list = args[1]->Eval(ev);
626  ev->DecrementEvalDepth();
627  WordWriter ww(s);
628  for (StringPiece tok : WordScanner(list)) {
629    unique_ptr<SimpleVar> v(new SimpleVar(
630        tok.as_string(), VarOrigin::AUTOMATIC));
631    ScopedVar sv(ev->mutable_vars(), Intern(varname), v.get());
632    ww.MaybeAddWhitespace();
633    args[2]->Eval(ev, s);
634  }
635  ev->IncrementEvalDepth();
636}
637
638void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
639  const string&& var_name = args[0]->Eval(ev);
640  Var* var = ev->LookupVar(Intern(var_name));
641  *s += GetOriginStr(var->Origin());
642}
643
644void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
645  const string&& var_name = args[0]->Eval(ev);
646  Var* var = ev->LookupVar(Intern(var_name));
647  *s += var->Flavor();
648}
649
650void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
651  const string&& a = args[0]->Eval(ev);
652  if (ev->avoid_io()) {
653    ev->add_delayed_output_command(StringPrintf("echo -e \"%s\"", EchoEscape(a).c_str()));
654    return;
655  }
656  printf("%s\n", a.c_str());
657  fflush(stdout);
658}
659
660void WarningFunc(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(
664        StringPrintf("echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str()));
665    return;
666  }
667  printf("%s:%d: %s\n", LOCF(ev->loc()), a.c_str());
668  fflush(stdout);
669}
670
671void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
672  const string&& a = args[0]->Eval(ev);
673  if (ev->avoid_io()) {
674    ev->add_delayed_output_command(
675        StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false",
676                     LOCF(ev->loc()), EchoEscape(a).c_str()));
677    return;
678  }
679  ev->Error(StringPrintf("*** %s.", a.c_str()));
680}
681
682FuncInfo g_func_infos[] = {
683  { "patsubst", &PatsubstFunc, 3, 3, false, false },
684  { "strip", &StripFunc, 1, 1, false, false },
685  { "subst", &SubstFunc, 3, 3, false, false },
686  { "findstring", &FindstringFunc, 2, 2, false, false },
687  { "filter", &FilterFunc, 2, 2, false, false },
688  { "filter-out", &FilterOutFunc, 2, 2, false, false },
689  { "sort", &SortFunc, 1, 1, false, false },
690  { "word", &WordFunc, 2, 2, false, false },
691  { "wordlist", &WordlistFunc, 3, 3, false, false },
692  { "words", &WordsFunc, 1, 1, false, false },
693  { "firstword", &FirstwordFunc, 1, 1, false, false },
694  { "lastword", &LastwordFunc, 1, 1, false, false },
695
696  { "join", &JoinFunc, 2, 2, false, false },
697  { "wildcard", &WildcardFunc, 1, 1, false, false },
698  { "dir", &DirFunc, 1, 1, false, false },
699  { "notdir", &NotdirFunc, 1, 1, false, false },
700  { "suffix", &SuffixFunc, 1, 1, false, false },
701  { "basename", &BasenameFunc, 1, 1, false, false },
702  { "addsuffix", &AddsuffixFunc, 2, 2, false, false },
703  { "addprefix", &AddprefixFunc, 2, 2, false, false },
704  { "realpath", &RealpathFunc, 1, 1, false, false },
705  { "abspath", &AbspathFunc, 1, 1, false, false },
706
707  { "if", &IfFunc, 3, 2, false, true },
708  { "and", &AndFunc, 0, 0, true, false },
709  { "or", &OrFunc, 0, 0, true, false },
710
711  { "value", &ValueFunc, 1, 1, false, false },
712  { "eval", &EvalFunc, 1, 1, false, false },
713  { "shell", &ShellFunc, 1, 1, false, false },
714  { "call", &CallFunc, 0, 0, false, false },
715  { "foreach", &ForeachFunc, 3, 3, false, false },
716
717  { "origin", &OriginFunc, 1, 1, false, false },
718  { "flavor", &FlavorFunc, 1, 1, false, false },
719
720  { "info", &InfoFunc, 1, 1, false, false },
721  { "warning", &WarningFunc, 1, 1, false, false },
722  { "error", &ErrorFunc, 1, 1, false, false },
723};
724
725unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
726
727}  // namespace
728
729void InitFuncTable() {
730  g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
731  for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
732    FuncInfo* fi = &g_func_infos[i];
733    bool ok = g_func_info_map->emplace(fi->name, fi).second;
734    CHECK(ok);
735  }
736}
737
738void QuitFuncTable() {
739  delete g_func_info_map;
740}
741
742FuncInfo* GetFuncInfo(StringPiece name) {
743  auto found = g_func_info_map->find(name);
744  if (found == g_func_info_map->end())
745    return NULL;
746  return found->second;
747}
748
749const vector<CommandResult*>& GetShellCommandResults() {
750  return g_command_results;
751}
752