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