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