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