func.cc revision 644d6b9b5e0ffa14e38e7c2212e1a686b248c1cd
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 (ev->avoid_io()) {
459    KATI_WARN("%s:%d: *warning*: $(eval) in a recipe is not recommended: %s",
460              LOCF(ev->loc()), text->c_str());
461  }
462  vector<Stmt*> stmts;
463  Parse(*text, ev->loc(), &stmts);
464  for (Stmt* stmt : stmts) {
465    LOG("%s", stmt->DebugString().c_str());
466    stmt->Eval(ev);
467    //delete stmt;
468  }
469}
470
471//#define TEST_FIND_EMULATOR
472
473// A hack for Android build. We need to evaluate things like $((3+4))
474// when we emit ninja file, because the result of such expressions
475// will be passed to other make functions.
476// TODO: Maybe we should introduce a helper binary which evaluate
477// make expressions at ninja-time.
478static bool HasNoIoInShellScript(const string& cmd) {
479  if (cmd.empty())
480    return true;
481  if (HasPrefix(cmd, "echo $((") && cmd[cmd.size()-1] == ')')
482    return true;
483  return false;
484}
485
486static void ShellFuncImpl(const string& shell, const string& cmd,
487                          string* s, FindCommand** fc) {
488  LOG("ShellFunc: %s", cmd.c_str());
489
490#ifdef TEST_FIND_EMULATOR
491  bool need_check = false;
492  string out2;
493#endif
494  if (FindEmulator::Get()) {
495    *fc = new FindCommand();
496    if ((*fc)->Parse(cmd)) {
497#ifdef TEST_FIND_EMULATOR
498      if (FindEmulator::Get()->HandleFind(cmd, **fc, &out2)) {
499        need_check = true;
500      }
501#else
502      if (FindEmulator::Get()->HandleFind(cmd, **fc, s)) {
503        return;
504      }
505#endif
506    }
507    delete *fc;
508    *fc = NULL;
509  }
510
511  COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
512  RunCommand(shell, cmd, RedirectStderr::NONE, s);
513  FormatForCommandSubstitution(s);
514
515#ifdef TEST_FIND_EMULATOR
516  if (need_check) {
517    if (*s != out2) {
518      ERROR("FindEmulator is broken: %s\n%s\nvs\n%s",
519            cmd.c_str(), s->c_str(), out2.c_str());
520    }
521  }
522#endif
523}
524
525static vector<CommandResult*> g_command_results;
526
527bool ShouldStoreCommandResult(StringPiece cmd) {
528  if (HasWord(cmd, "date") || HasWord(cmd, "echo"))
529    return false;
530
531  Pattern pat(g_flags.ignore_dirty_pattern);
532  Pattern nopat(g_flags.no_ignore_dirty_pattern);
533  for (StringPiece tok : WordScanner(cmd)) {
534    if (pat.Match(tok) && !nopat.Match(tok)) {
535      return false;
536    }
537  }
538
539  return true;
540}
541
542void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
543  string cmd = args[0]->Eval(ev);
544  if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
545    StripShellComment(&cmd);
546    *s += "$(";
547    *s += cmd;
548    *s += ")";
549    return;
550  }
551
552  const string&& shell = ev->EvalVar(kShellSym);
553
554  string out;
555  FindCommand* fc = NULL;
556  ShellFuncImpl(shell, cmd, &out, &fc);
557  if (ShouldStoreCommandResult(cmd)) {
558    CommandResult* cr = new CommandResult();
559    cr->cmd = cmd;
560    cr->find.reset(fc);
561    cr->result = out;
562    g_command_results.push_back(cr);
563  }
564  *s += out;
565}
566
567void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
568  static const string tmpvar_names[] = {
569    "0", "1",  "2",  "3",  "4",  "5",  "6",  "7",  "8",  "9"
570  };
571
572  const string&& func_name = args[0]->Eval(ev);
573  Var* func = ev->LookupVar(Intern(func_name));
574  vector<unique_ptr<SimpleVar>> av;
575  for (size_t i = 1; i < args.size(); i++) {
576    unique_ptr<SimpleVar> s(
577        new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC));
578    av.push_back(move(s));
579  }
580  vector<unique_ptr<ScopedVar>> sv;
581  for (size_t i = 1; ; i++) {
582    string s;
583    StringPiece tmpvar_name;
584    if (i < sizeof(tmpvar_names)/sizeof(tmpvar_names[0])) {
585      tmpvar_name = tmpvar_names[i];
586    } else {
587      s = StringPrintf("%d", i);
588      tmpvar_name = s;
589    }
590    if (i < args.size()) {
591      sv.emplace_back(new ScopedVar(ev->mutable_vars(),
592                                    Intern(tmpvar_name), av[i-1].get()));
593    } else {
594      // We need to blank further automatic vars
595      Var *v = ev->LookupVar(Intern(tmpvar_name));
596      if (!v->IsDefined()) break;
597      if (v->Origin() != VarOrigin::AUTOMATIC) break;
598
599      av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC));
600      sv.emplace_back(new ScopedVar(ev->mutable_vars(),
601                                    Intern(tmpvar_name), av[i-1].get()));
602    }
603  }
604  func->Eval(ev, s);
605}
606
607void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
608  const string&& varname = args[0]->Eval(ev);
609  const string&& list = args[1]->Eval(ev);
610  WordWriter ww(s);
611  for (StringPiece tok : WordScanner(list)) {
612    unique_ptr<SimpleVar> v(new SimpleVar(
613        tok.as_string(), VarOrigin::AUTOMATIC));
614    ScopedVar sv(ev->mutable_vars(), Intern(varname), v.get());
615    ww.MaybeAddWhitespace();
616    args[2]->Eval(ev, s);
617  }
618}
619
620void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
621  const string&& var_name = args[0]->Eval(ev);
622  Var* var = ev->LookupVar(Intern(var_name));
623  *s += GetOriginStr(var->Origin());
624}
625
626void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
627  const string&& var_name = args[0]->Eval(ev);
628  Var* var = ev->LookupVar(Intern(var_name));
629  *s += var->Flavor();
630}
631
632void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
633  const string&& a = args[0]->Eval(ev);
634  if (ev->avoid_io()) {
635    ev->add_delayed_output_command(StringPrintf("echo -e \"%s\"", EchoEscape(a).c_str()));
636    return;
637  }
638  printf("%s\n", a.c_str());
639  fflush(stdout);
640}
641
642void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) {
643  const string&& a = args[0]->Eval(ev);
644  if (ev->avoid_io()) {
645    ev->add_delayed_output_command(
646        StringPrintf("echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str()));
647    return;
648  }
649  printf("%s:%d: %s\n", LOCF(ev->loc()), a.c_str());
650  fflush(stdout);
651}
652
653void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
654  const string&& a = args[0]->Eval(ev);
655  if (ev->avoid_io()) {
656    ev->add_delayed_output_command(
657        StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false",
658                     LOCF(ev->loc()), EchoEscape(a).c_str()));
659    return;
660  }
661  ev->Error(StringPrintf("*** %s.", a.c_str()));
662}
663
664FuncInfo g_func_infos[] = {
665  { "patsubst", &PatsubstFunc, 3, 3, false, false },
666  { "strip", &StripFunc, 1, 1, false, false },
667  { "subst", &SubstFunc, 3, 3, false, false },
668  { "findstring", &FindstringFunc, 2, 2, false, false },
669  { "filter", &FilterFunc, 2, 2, false, false },
670  { "filter-out", &FilterOutFunc, 2, 2, false, false },
671  { "sort", &SortFunc, 1, 1, false, false },
672  { "word", &WordFunc, 2, 2, false, false },
673  { "wordlist", &WordlistFunc, 3, 3, false, false },
674  { "words", &WordsFunc, 1, 1, false, false },
675  { "firstword", &FirstwordFunc, 1, 1, false, false },
676  { "lastword", &LastwordFunc, 1, 1, false, false },
677
678  { "join", &JoinFunc, 2, 2, false, false },
679  { "wildcard", &WildcardFunc, 1, 1, false, false },
680  { "dir", &DirFunc, 1, 1, false, false },
681  { "notdir", &NotdirFunc, 1, 1, false, false },
682  { "suffix", &SuffixFunc, 1, 1, false, false },
683  { "basename", &BasenameFunc, 1, 1, false, false },
684  { "addsuffix", &AddsuffixFunc, 2, 2, false, false },
685  { "addprefix", &AddprefixFunc, 2, 2, false, false },
686  { "realpath", &RealpathFunc, 1, 1, false, false },
687  { "abspath", &AbspathFunc, 1, 1, false, false },
688
689  { "if", &IfFunc, 3, 2, false, true },
690  { "and", &AndFunc, 0, 0, true, false },
691  { "or", &OrFunc, 0, 0, true, false },
692
693  { "value", &ValueFunc, 1, 1, false, false },
694  { "eval", &EvalFunc, 1, 1, false, false },
695  { "shell", &ShellFunc, 1, 1, false, false },
696  { "call", &CallFunc, 0, 0, false, false },
697  { "foreach", &ForeachFunc, 3, 3, false, false },
698
699  { "origin", &OriginFunc, 1, 1, false, false },
700  { "flavor", &FlavorFunc, 1, 1, false, false },
701
702  { "info", &InfoFunc, 1, 1, false, false },
703  { "warning", &WarningFunc, 1, 1, false, false },
704  { "error", &ErrorFunc, 1, 1, false, false },
705};
706
707unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
708
709}  // namespace
710
711void InitFuncTable() {
712  g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
713  for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
714    FuncInfo* fi = &g_func_infos[i];
715    bool ok = g_func_info_map->emplace(fi->name, fi).second;
716    CHECK(ok);
717  }
718}
719
720void QuitFuncTable() {
721  delete g_func_info_map;
722}
723
724FuncInfo* GetFuncInfo(StringPiece name) {
725  auto found = g_func_info_map->find(name);
726  if (found == g_func_info_map->end())
727    return NULL;
728  return found->second;
729}
730
731const vector<CommandResult*>& GetShellCommandResults() {
732  return g_command_results;
733}
734