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