• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
StripShellComment(string * cmd)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 #if defined(__has_cpp_attribute) && __has_cpp_attribute(clang::fallthrough)
68         [[clang::fallthrough]];
69 #endif
70 
71       case '\'':
72       case '"':
73       case '`':
74         if (quote) {
75           if (quote == *in)
76             quote = 0;
77         } else if (!prev_backslash) {
78           quote = *in;
79         }
80         res += *in;
81         break;
82 
83       case '\\':
84         res += '\\';
85         break;
86 
87       default:
88         res += *in;
89     }
90 
91     if (*in == '\\') {
92       prev_backslash = !prev_backslash;
93     } else {
94       prev_backslash = false;
95     }
96 
97     prev_char = *in;
98   }
99   cmd->swap(res);
100 }
101 
PatsubstFunc(const vector<Value * > & args,Evaluator * ev,string * s)102 void PatsubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
103   const string&& pat_str = args[0]->Eval(ev);
104   const string&& repl = args[1]->Eval(ev);
105   const string&& str = args[2]->Eval(ev);
106   WordWriter ww(s);
107   Pattern pat(pat_str);
108   for (StringPiece tok : WordScanner(str)) {
109     ww.MaybeAddWhitespace();
110     pat.AppendSubst(tok, repl, s);
111   }
112 }
113 
StripFunc(const vector<Value * > & args,Evaluator * ev,string * s)114 void StripFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
115   const string&& str = args[0]->Eval(ev);
116   WordWriter ww(s);
117   for (StringPiece tok : WordScanner(str)) {
118     ww.Write(tok);
119   }
120 }
121 
SubstFunc(const vector<Value * > & args,Evaluator * ev,string * s)122 void SubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
123   const string&& pat = args[0]->Eval(ev);
124   const string&& repl = args[1]->Eval(ev);
125   const string&& str = args[2]->Eval(ev);
126   if (pat.empty()) {
127     *s += str;
128     *s += repl;
129     return;
130   }
131   size_t index = 0;
132   while (index < str.size()) {
133     size_t found = str.find(pat, index);
134     if (found == string::npos)
135       break;
136     AppendString(StringPiece(str).substr(index, found - index), s);
137     AppendString(repl, s);
138     index = found + pat.size();
139   }
140   AppendString(StringPiece(str).substr(index), s);
141 }
142 
FindstringFunc(const vector<Value * > & args,Evaluator * ev,string * s)143 void FindstringFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
144   const string&& find = args[0]->Eval(ev);
145   const string&& in = args[1]->Eval(ev);
146   if (in.find(find) != string::npos)
147     AppendString(find, s);
148 }
149 
FilterFunc(const vector<Value * > & args,Evaluator * ev,string * s)150 void FilterFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
151   const string&& pat_buf = args[0]->Eval(ev);
152   const string&& text = args[1]->Eval(ev);
153   vector<Pattern> pats;
154   for (StringPiece pat : WordScanner(pat_buf)) {
155     pats.push_back(Pattern(pat));
156   }
157   WordWriter ww(s);
158   for (StringPiece tok : WordScanner(text)) {
159     for (const Pattern& pat : pats) {
160       if (pat.Match(tok)) {
161         ww.Write(tok);
162         break;
163       }
164     }
165   }
166 }
167 
FilterOutFunc(const vector<Value * > & args,Evaluator * ev,string * s)168 void FilterOutFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
169   const string&& pat_buf = args[0]->Eval(ev);
170   const string&& text = args[1]->Eval(ev);
171   vector<Pattern> pats;
172   for (StringPiece pat : WordScanner(pat_buf)) {
173     pats.push_back(Pattern(pat));
174   }
175   WordWriter ww(s);
176   for (StringPiece tok : WordScanner(text)) {
177     bool matched = false;
178     for (const Pattern& pat : pats) {
179       if (pat.Match(tok)) {
180         matched = true;
181         break;
182       }
183     }
184     if (!matched)
185       ww.Write(tok);
186   }
187 }
188 
SortFunc(const vector<Value * > & args,Evaluator * ev,string * s)189 void SortFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
190   string list;
191   args[0]->Eval(ev, &list);
192   COLLECT_STATS("func sort time");
193   // TODO(hamaji): Probably we could use a faster string-specific sort
194   // algorithm.
195   vector<StringPiece> toks;
196   WordScanner(list).Split(&toks);
197   stable_sort(toks.begin(), toks.end());
198   WordWriter ww(s);
199   StringPiece prev;
200   for (StringPiece tok : toks) {
201     if (prev != tok) {
202       ww.Write(tok);
203       prev = tok;
204     }
205   }
206 }
207 
GetNumericValueForFunc(const string & buf)208 static int GetNumericValueForFunc(const string& buf) {
209   StringPiece s = TrimLeftSpace(buf);
210   char* end;
211   long n = strtol(s.data(), &end, 10);
212   if (n < 0 || n == LONG_MAX || s.data() + s.size() != end) {
213     return -1;
214   }
215   return n;
216 }
217 
WordFunc(const vector<Value * > & args,Evaluator * ev,string * s)218 void WordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
219   const string&& n_str = args[0]->Eval(ev);
220   int n = GetNumericValueForFunc(n_str);
221   if (n < 0) {
222     ev->Error(
223         StringPrintf("*** non-numeric first argument to `word' function: '%s'.",
224                      n_str.c_str()));
225   }
226   if (n == 0) {
227     ev->Error("*** first argument to `word' function must be greater than 0.");
228   }
229 
230   const string&& text = args[1]->Eval(ev);
231   for (StringPiece tok : WordScanner(text)) {
232     n--;
233     if (n == 0) {
234       AppendString(tok, s);
235       break;
236     }
237   }
238 }
239 
WordlistFunc(const vector<Value * > & args,Evaluator * ev,string * s)240 void WordlistFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
241   const string&& s_str = args[0]->Eval(ev);
242   int si = GetNumericValueForFunc(s_str);
243   if (si < 0) {
244     ev->Error(StringPrintf(
245         "*** non-numeric first argument to `wordlist' function: '%s'.",
246         s_str.c_str()));
247   }
248   if (si == 0) {
249     ev->Error(
250         StringPrintf("*** invalid first argument to `wordlist' function: %s`",
251                      s_str.c_str()));
252   }
253 
254   const string&& e_str = args[1]->Eval(ev);
255   int ei = GetNumericValueForFunc(e_str);
256   if (ei < 0) {
257     ev->Error(StringPrintf(
258         "*** non-numeric second argument to `wordlist' function: '%s'.",
259         e_str.c_str()));
260   }
261 
262   const string&& text = args[2]->Eval(ev);
263   int i = 0;
264   WordWriter ww(s);
265   for (StringPiece tok : WordScanner(text)) {
266     i++;
267     if (si <= i && i <= ei) {
268       ww.Write(tok);
269     }
270   }
271 }
272 
WordsFunc(const vector<Value * > & args,Evaluator * ev,string * s)273 void WordsFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
274   const string&& text = args[0]->Eval(ev);
275   WordScanner ws(text);
276   int n = 0;
277   for (auto iter = ws.begin(); iter != ws.end(); ++iter)
278     n++;
279   char buf[32];
280   sprintf(buf, "%d", n);
281   *s += buf;
282 }
283 
FirstwordFunc(const vector<Value * > & args,Evaluator * ev,string * s)284 void FirstwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
285   const string&& text = args[0]->Eval(ev);
286   for (StringPiece tok : WordScanner(text)) {
287     AppendString(tok, s);
288     return;
289   }
290 }
291 
LastwordFunc(const vector<Value * > & args,Evaluator * ev,string * s)292 void LastwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
293   const string&& text = args[0]->Eval(ev);
294   StringPiece last;
295   for (StringPiece tok : WordScanner(text)) {
296     last = tok;
297   }
298   AppendString(last, s);
299 }
300 
JoinFunc(const vector<Value * > & args,Evaluator * ev,string * s)301 void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
302   const string&& list1 = args[0]->Eval(ev);
303   const string&& list2 = args[1]->Eval(ev);
304   WordScanner ws1(list1);
305   WordScanner ws2(list2);
306   WordWriter ww(s);
307   WordScanner::Iterator iter1, iter2;
308   for (iter1 = ws1.begin(), iter2 = ws2.begin();
309        iter1 != ws1.end() && iter2 != ws2.end(); ++iter1, ++iter2) {
310     ww.Write(*iter1);
311     // Use |AppendString| not to append extra ' '.
312     AppendString(*iter2, s);
313   }
314   for (; iter1 != ws1.end(); ++iter1)
315     ww.Write(*iter1);
316   for (; iter2 != ws2.end(); ++iter2)
317     ww.Write(*iter2);
318 }
319 
WildcardFunc(const vector<Value * > & args,Evaluator * ev,string * s)320 void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
321   const string&& pat = args[0]->Eval(ev);
322   COLLECT_STATS("func wildcard time");
323   // Note GNU make does not delay the execution of $(wildcard) so we
324   // do not need to check avoid_io here.
325   WordWriter ww(s);
326   vector<string>* files;
327   for (StringPiece tok : WordScanner(pat)) {
328     ScopedTerminator st(tok);
329     Glob(tok.data(), &files);
330     for (const string& file : *files) {
331       ww.Write(file);
332     }
333   }
334 }
335 
DirFunc(const vector<Value * > & args,Evaluator * ev,string * s)336 void DirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
337   const string&& text = args[0]->Eval(ev);
338   WordWriter ww(s);
339   for (StringPiece tok : WordScanner(text)) {
340     ww.Write(Dirname(tok));
341     s->push_back('/');
342   }
343 }
344 
NotdirFunc(const vector<Value * > & args,Evaluator * ev,string * s)345 void NotdirFunc(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     if (tok == "/") {
350       ww.Write(StringPiece(""));
351     } else {
352       ww.Write(Basename(tok));
353     }
354   }
355 }
356 
SuffixFunc(const vector<Value * > & args,Evaluator * ev,string * s)357 void SuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
358   const string&& text = args[0]->Eval(ev);
359   WordWriter ww(s);
360   for (StringPiece tok : WordScanner(text)) {
361     StringPiece suf = GetExt(tok);
362     if (!suf.empty())
363       ww.Write(suf);
364   }
365 }
366 
BasenameFunc(const vector<Value * > & args,Evaluator * ev,string * s)367 void BasenameFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
368   const string&& text = args[0]->Eval(ev);
369   WordWriter ww(s);
370   for (StringPiece tok : WordScanner(text)) {
371     ww.Write(StripExt(tok));
372   }
373 }
374 
AddsuffixFunc(const vector<Value * > & args,Evaluator * ev,string * s)375 void AddsuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
376   const string&& suf = args[0]->Eval(ev);
377   const string&& text = args[1]->Eval(ev);
378   WordWriter ww(s);
379   for (StringPiece tok : WordScanner(text)) {
380     ww.Write(tok);
381     *s += suf;
382   }
383 }
384 
AddprefixFunc(const vector<Value * > & args,Evaluator * ev,string * s)385 void AddprefixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
386   const string&& pre = args[0]->Eval(ev);
387   const string&& text = args[1]->Eval(ev);
388   WordWriter ww(s);
389   for (StringPiece tok : WordScanner(text)) {
390     ww.Write(pre);
391     AppendString(tok, s);
392   }
393 }
394 
RealpathFunc(const vector<Value * > & args,Evaluator * ev,string * s)395 void RealpathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
396   const string&& text = args[0]->Eval(ev);
397   if (ev->avoid_io()) {
398     *s += "$(";
399     string kati_binary;
400     GetExecutablePath(&kati_binary);
401     *s += kati_binary;
402     *s += " --realpath ";
403     *s += text;
404     *s += " 2> /dev/null)";
405     return;
406   }
407 
408   WordWriter ww(s);
409   for (StringPiece tok : WordScanner(text)) {
410     ScopedTerminator st(tok);
411     char buf[PATH_MAX];
412     if (realpath(tok.data(), buf))
413       ww.Write(buf);
414   }
415 }
416 
AbspathFunc(const vector<Value * > & args,Evaluator * ev,string * s)417 void AbspathFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
418   const string&& text = args[0]->Eval(ev);
419   WordWriter ww(s);
420   string buf;
421   for (StringPiece tok : WordScanner(text)) {
422     AbsPath(tok, &buf);
423     ww.Write(buf);
424   }
425 }
426 
IfFunc(const vector<Value * > & args,Evaluator * ev,string * s)427 void IfFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
428   const string&& cond = args[0]->Eval(ev);
429   if (cond.empty()) {
430     if (args.size() > 2)
431       args[2]->Eval(ev, s);
432   } else {
433     args[1]->Eval(ev, s);
434   }
435 }
436 
AndFunc(const vector<Value * > & args,Evaluator * ev,string * s)437 void AndFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
438   string cond;
439   for (Value* a : args) {
440     cond = a->Eval(ev);
441     if (cond.empty())
442       return;
443   }
444   if (!cond.empty()) {
445     *s += cond;
446   }
447 }
448 
OrFunc(const vector<Value * > & args,Evaluator * ev,string * s)449 void OrFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
450   for (Value* a : args) {
451     const string&& cond = a->Eval(ev);
452     if (!cond.empty()) {
453       *s += cond;
454       return;
455     }
456   }
457 }
458 
ValueFunc(const vector<Value * > & args,Evaluator * ev,string * s)459 void ValueFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
460   const string&& var_name = args[0]->Eval(ev);
461   Var* var = ev->LookupVar(Intern(var_name));
462   AppendString(var->String().as_string(), s);
463 }
464 
EvalFunc(const vector<Value * > & args,Evaluator * ev,string *)465 void EvalFunc(const vector<Value*>& args, Evaluator* ev, string*) {
466   // TODO: eval leaks everything... for now.
467   // const string text = args[0]->Eval(ev);
468   ev->CheckStack();
469   string* text = new string;
470   args[0]->Eval(ev, text);
471   if (ev->avoid_io()) {
472     KATI_WARN_LOC(ev->loc(),
473                   "*warning*: $(eval) in a recipe is not recommended: %s",
474                   text->c_str());
475   }
476   vector<Stmt*> stmts;
477   Parse(*text, ev->loc(), &stmts);
478   for (Stmt* stmt : stmts) {
479     LOG("%s", stmt->DebugString().c_str());
480     stmt->Eval(ev);
481     // delete stmt;
482   }
483 }
484 
485 //#define TEST_FIND_EMULATOR
486 
487 // A hack for Android build. We need to evaluate things like $((3+4))
488 // when we emit ninja file, because the result of such expressions
489 // will be passed to other make functions.
490 // TODO: Maybe we should introduce a helper binary which evaluate
491 // make expressions at ninja-time.
HasNoIoInShellScript(const string & cmd)492 static bool HasNoIoInShellScript(const string& cmd) {
493   if (cmd.empty())
494     return true;
495   if (HasPrefix(cmd, "echo $((") && cmd[cmd.size() - 1] == ')')
496     return true;
497   return false;
498 }
499 
ShellFuncImpl(const string & shell,const string & shellflag,const string & cmd,const Loc & loc,string * s,FindCommand ** fc)500 static void ShellFuncImpl(const string& shell,
501                           const string& shellflag,
502                           const string& cmd,
503                           const Loc& loc,
504                           string* s,
505                           FindCommand** fc) {
506   LOG("ShellFunc: %s", cmd.c_str());
507 
508 #ifdef TEST_FIND_EMULATOR
509   bool need_check = false;
510   string out2;
511 #endif
512   if (FindEmulator::Get()) {
513     *fc = new FindCommand();
514     if ((*fc)->Parse(cmd)) {
515 #ifdef TEST_FIND_EMULATOR
516       if (FindEmulator::Get()->HandleFind(cmd, **fc, loc, &out2)) {
517         need_check = true;
518       }
519 #else
520       if (FindEmulator::Get()->HandleFind(cmd, **fc, loc, s)) {
521         return;
522       }
523 #endif
524     }
525     delete *fc;
526     *fc = NULL;
527   }
528 
529   COLLECT_STATS_WITH_SLOW_REPORT("func shell time", cmd.c_str());
530   RunCommand(shell, shellflag, cmd, RedirectStderr::NONE, s);
531   FormatForCommandSubstitution(s);
532 
533 #ifdef TEST_FIND_EMULATOR
534   if (need_check) {
535     if (*s != out2) {
536       ERROR("FindEmulator is broken: %s\n%s\nvs\n%s", cmd.c_str(), s->c_str(),
537             out2.c_str());
538     }
539   }
540 #endif
541 }
542 
543 static vector<CommandResult*> g_command_results;
544 
ShouldStoreCommandResult(StringPiece cmd)545 bool ShouldStoreCommandResult(StringPiece cmd) {
546   // We really just want to ignore this one, or remove BUILD_DATETIME from
547   // Android completely
548   if (cmd == "date +%s")
549     return false;
550 
551   Pattern pat(g_flags.ignore_dirty_pattern);
552   Pattern nopat(g_flags.no_ignore_dirty_pattern);
553   for (StringPiece tok : WordScanner(cmd)) {
554     if (pat.Match(tok) && !nopat.Match(tok)) {
555       return false;
556     }
557   }
558 
559   return true;
560 }
561 
ShellFunc(const vector<Value * > & args,Evaluator * ev,string * s)562 void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
563   string cmd = args[0]->Eval(ev);
564   if (ev->avoid_io() && !HasNoIoInShellScript(cmd)) {
565     if (ev->eval_depth() > 1) {
566       ERROR_LOC(ev->loc(),
567                 "kati doesn't support passing results of $(shell) "
568                 "to other make constructs: %s",
569                 cmd.c_str());
570     }
571     StripShellComment(&cmd);
572     *s += "$(";
573     *s += cmd;
574     *s += ")";
575     return;
576   }
577 
578   const string&& shell = ev->GetShell();
579   const string&& shellflag = ev->GetShellFlag();
580 
581   string out;
582   FindCommand* fc = NULL;
583   ShellFuncImpl(shell, shellflag, cmd, ev->loc(), &out, &fc);
584   if (ShouldStoreCommandResult(cmd)) {
585     CommandResult* cr = new CommandResult();
586     cr->op = (fc == NULL) ? CommandOp::SHELL : CommandOp::FIND,
587     cr->shell = shell;
588     cr->shellflag = shellflag;
589     cr->cmd = cmd;
590     cr->find.reset(fc);
591     cr->result = out;
592     g_command_results.push_back(cr);
593   }
594   *s += out;
595 }
596 
CallFunc(const vector<Value * > & args,Evaluator * ev,string * s)597 void CallFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
598   static const Symbol tmpvar_names[] = {
599       Intern("0"), Intern("1"), Intern("2"), Intern("3"), Intern("4"),
600       Intern("5"), Intern("6"), Intern("7"), Intern("8"), Intern("9")};
601 
602   ev->CheckStack();
603   const string&& func_name_buf = args[0]->Eval(ev);
604   Symbol func_sym = Intern(TrimSpace(func_name_buf));
605   Var* func = ev->LookupVar(func_sym);
606   func->Used(ev, func_sym);
607   if (!func->IsDefined()) {
608     KATI_WARN_LOC(ev->loc(), "*warning*: undefined user function: %s",
609                   func_sym.c_str());
610   }
611   vector<unique_ptr<SimpleVar>> av;
612   for (size_t i = 1; i < args.size(); i++) {
613     unique_ptr<SimpleVar> s(
614         new SimpleVar(args[i]->Eval(ev), VarOrigin::AUTOMATIC));
615     av.push_back(move(s));
616   }
617   vector<unique_ptr<ScopedGlobalVar>> sv;
618   for (size_t i = 1;; i++) {
619     string s;
620     Symbol tmpvar_name_sym;
621     if (i < sizeof(tmpvar_names) / sizeof(tmpvar_names[0])) {
622       tmpvar_name_sym = tmpvar_names[i];
623     } else {
624       s = StringPrintf("%d", i);
625       tmpvar_name_sym = Intern(s);
626     }
627     if (i < args.size()) {
628       sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i - 1].get()));
629     } else {
630       // We need to blank further automatic vars
631       Var* v = ev->LookupVar(tmpvar_name_sym);
632       if (!v->IsDefined())
633         break;
634       if (v->Origin() != VarOrigin::AUTOMATIC)
635         break;
636 
637       av.emplace_back(new SimpleVar("", VarOrigin::AUTOMATIC));
638       sv.emplace_back(new ScopedGlobalVar(tmpvar_name_sym, av[i - 1].get()));
639     }
640   }
641 
642   ev->DecrementEvalDepth();
643   func->Eval(ev, s);
644   ev->IncrementEvalDepth();
645 }
646 
ForeachFunc(const vector<Value * > & args,Evaluator * ev,string * s)647 void ForeachFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
648   const string&& varname = args[0]->Eval(ev);
649   const string&& list = args[1]->Eval(ev);
650   ev->DecrementEvalDepth();
651   WordWriter ww(s);
652   for (StringPiece tok : WordScanner(list)) {
653     unique_ptr<SimpleVar> v(
654         new SimpleVar(tok.as_string(), VarOrigin::AUTOMATIC));
655     ScopedGlobalVar sv(Intern(varname), v.get());
656     ww.MaybeAddWhitespace();
657     args[2]->Eval(ev, s);
658   }
659   ev->IncrementEvalDepth();
660 }
661 
OriginFunc(const vector<Value * > & args,Evaluator * ev,string * s)662 void OriginFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
663   const string&& var_name = args[0]->Eval(ev);
664   Var* var = ev->LookupVar(Intern(var_name));
665   *s += GetOriginStr(var->Origin());
666 }
667 
FlavorFunc(const vector<Value * > & args,Evaluator * ev,string * s)668 void FlavorFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
669   const string&& var_name = args[0]->Eval(ev);
670   Var* var = ev->LookupVar(Intern(var_name));
671   *s += var->Flavor();
672 }
673 
InfoFunc(const vector<Value * > & args,Evaluator * ev,string *)674 void InfoFunc(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\"", EchoEscape(a).c_str()));
679     return;
680   }
681   printf("%s\n", a.c_str());
682   fflush(stdout);
683 }
684 
WarningFunc(const vector<Value * > & args,Evaluator * ev,string *)685 void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) {
686   const string&& a = args[0]->Eval(ev);
687   if (ev->avoid_io()) {
688     ev->add_delayed_output_command(StringPrintf(
689         "echo -e \"%s:%d: %s\" 2>&1", LOCF(ev->loc()), EchoEscape(a).c_str()));
690     return;
691   }
692   WARN_LOC(ev->loc(), "%s", a.c_str());
693 }
694 
ErrorFunc(const vector<Value * > & args,Evaluator * ev,string *)695 void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
696   const string&& a = args[0]->Eval(ev);
697   if (ev->avoid_io()) {
698     ev->add_delayed_output_command(
699         StringPrintf("echo -e \"%s:%d: *** %s.\" 2>&1 && false",
700                      LOCF(ev->loc()), EchoEscape(a).c_str()));
701     return;
702   }
703   ev->Error(StringPrintf("*** %s.", a.c_str()));
704 }
705 
FileReadFunc(Evaluator * ev,const string & filename,string * s)706 static void FileReadFunc(Evaluator* ev, const string& filename, string* s) {
707   int fd = open(filename.c_str(), O_RDONLY);
708   if (fd < 0) {
709     if (errno == ENOENT) {
710       if (ShouldStoreCommandResult(filename)) {
711         CommandResult* cr = new CommandResult();
712         cr->op = CommandOp::READ_MISSING;
713         cr->cmd = filename;
714         g_command_results.push_back(cr);
715       }
716       return;
717     } else {
718       ev->Error("*** open failed.");
719     }
720   }
721 
722   struct stat st;
723   if (fstat(fd, &st) < 0) {
724     ev->Error("*** fstat failed.");
725   }
726 
727   size_t len = st.st_size;
728   string out;
729   out.resize(len);
730   ssize_t r = HANDLE_EINTR(read(fd, &out[0], len));
731   if (r != static_cast<ssize_t>(len)) {
732     ev->Error("*** read failed.");
733   }
734 
735   if (close(fd) < 0) {
736     ev->Error("*** close failed.");
737   }
738 
739   if (out.back() == '\n') {
740     out.pop_back();
741   }
742 
743   if (ShouldStoreCommandResult(filename)) {
744     CommandResult* cr = new CommandResult();
745     cr->op = CommandOp::READ;
746     cr->cmd = filename;
747     g_command_results.push_back(cr);
748   }
749   *s += out;
750 }
751 
FileWriteFunc(Evaluator * ev,const string & filename,bool append,string text)752 static void FileWriteFunc(Evaluator* ev,
753                           const string& filename,
754                           bool append,
755                           string text) {
756   FILE* f = fopen(filename.c_str(), append ? "ab" : "wb");
757   if (f == NULL) {
758     ev->Error("*** fopen failed.");
759   }
760 
761   if (fwrite(&text[0], text.size(), 1, f) != 1) {
762     ev->Error("*** fwrite failed.");
763   }
764 
765   if (fclose(f) != 0) {
766     ev->Error("*** fclose failed.");
767   }
768 
769   if (ShouldStoreCommandResult(filename)) {
770     CommandResult* cr = new CommandResult();
771     cr->op = CommandOp::WRITE;
772     cr->cmd = filename;
773     cr->result = text;
774     g_command_results.push_back(cr);
775   }
776 }
777 
FileFunc(const vector<Value * > & args,Evaluator * ev,string * s)778 void FileFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
779   if (ev->avoid_io()) {
780     ev->Error("*** $(file ...) is not supported in rules.");
781   }
782 
783   string arg = args[0]->Eval(ev);
784   StringPiece filename = TrimSpace(arg);
785 
786   if (filename.size() <= 1) {
787     ev->Error("*** Missing filename");
788   }
789 
790   if (filename[0] == '<') {
791     filename = TrimLeftSpace(filename.substr(1));
792     if (!filename.size()) {
793       ev->Error("*** Missing filename");
794     }
795     if (args.size() > 1) {
796       ev->Error("*** invalid argument");
797     }
798 
799     FileReadFunc(ev, filename.as_string(), s);
800   } else if (filename[0] == '>') {
801     bool append = false;
802     if (filename[1] == '>') {
803       append = true;
804       filename = filename.substr(2);
805     } else {
806       filename = filename.substr(1);
807     }
808     filename = TrimLeftSpace(filename);
809     if (!filename.size()) {
810       ev->Error("*** Missing filename");
811     }
812 
813     string text;
814     if (args.size() > 1) {
815       text = args[1]->Eval(ev);
816       if (text.size() == 0 || text.back() != '\n') {
817         text.push_back('\n');
818       }
819     }
820 
821     FileWriteFunc(ev, filename.as_string(), append, text);
822   } else {
823     ev->Error(StringPrintf("*** Invalid file operation: %s.  Stop.",
824                            filename.as_string().c_str()));
825   }
826 }
827 
DeprecatedVarFunc(const vector<Value * > & args,Evaluator * ev,string *)828 void DeprecatedVarFunc(const vector<Value*>& args, Evaluator* ev, string*) {
829   string vars_str = args[0]->Eval(ev);
830   string msg;
831 
832   if (args.size() == 2) {
833     msg = ". " + args[1]->Eval(ev);
834   }
835 
836   if (ev->avoid_io()) {
837     ev->Error("*** $(KATI_deprecated_var ...) is not supported in rules.");
838   }
839 
840   for (StringPiece var : WordScanner(vars_str)) {
841     Symbol sym = Intern(var);
842     Var* v = ev->PeekVar(sym);
843     if (!v->IsDefined()) {
844       v = new SimpleVar(VarOrigin::FILE);
845       sym.SetGlobalVar(v, false, nullptr);
846     }
847 
848     if (v->Deprecated()) {
849       ev->Error(
850           StringPrintf("*** Cannot call KATI_deprecated_var on already "
851                        "deprecated variable: %s.",
852                        sym.c_str()));
853     } else if (v->Obsolete()) {
854       ev->Error(
855           StringPrintf("*** Cannot call KATI_deprecated_var on already "
856                        "obsolete variable: %s.",
857                        sym.c_str()));
858     }
859 
860     v->SetDeprecated(msg);
861   }
862 }
863 
ObsoleteVarFunc(const vector<Value * > & args,Evaluator * ev,string *)864 void ObsoleteVarFunc(const vector<Value*>& args, Evaluator* ev, string*) {
865   string vars_str = args[0]->Eval(ev);
866   string msg;
867 
868   if (args.size() == 2) {
869     msg = ". " + args[1]->Eval(ev);
870   }
871 
872   if (ev->avoid_io()) {
873     ev->Error("*** $(KATI_obsolete_var ...) is not supported in rules.");
874   }
875 
876   for (StringPiece var : WordScanner(vars_str)) {
877     Symbol sym = Intern(var);
878     Var* v = ev->PeekVar(sym);
879     if (!v->IsDefined()) {
880       v = new SimpleVar(VarOrigin::FILE);
881       sym.SetGlobalVar(v, false, nullptr);
882     }
883 
884     if (v->Deprecated()) {
885       ev->Error(
886           StringPrintf("*** Cannot call KATI_obsolete_var on already "
887                        "deprecated variable: %s.",
888                        sym.c_str()));
889     } else if (v->Obsolete()) {
890       ev->Error(StringPrintf(
891           "*** Cannot call KATI_obsolete_var on already obsolete variable: %s.",
892           sym.c_str()));
893     }
894 
895     v->SetObsolete(msg);
896   }
897 }
898 
DeprecateExportFunc(const vector<Value * > & args,Evaluator * ev,string *)899 void DeprecateExportFunc(const vector<Value*>& args, Evaluator* ev, string*) {
900   string msg = ". " + args[0]->Eval(ev);
901 
902   if (ev->avoid_io()) {
903     ev->Error("*** $(KATI_deprecate_export) is not supported in rules.");
904   }
905 
906   if (ev->ExportObsolete()) {
907     ev->Error("*** Export is already obsolete.");
908   } else if (ev->ExportDeprecated()) {
909     ev->Error("*** Export is already deprecated.");
910   }
911 
912   ev->SetExportDeprecated(msg);
913 }
914 
ObsoleteExportFunc(const vector<Value * > & args,Evaluator * ev,string *)915 void ObsoleteExportFunc(const vector<Value*>& args, Evaluator* ev, string*) {
916   string msg = ". " + args[0]->Eval(ev);
917 
918   if (ev->avoid_io()) {
919     ev->Error("*** $(KATI_obsolete_export) is not supported in rules.");
920   }
921 
922   if (ev->ExportObsolete()) {
923     ev->Error("*** Export is already obsolete.");
924   }
925 
926   ev->SetExportObsolete(msg);
927 }
928 
929 FuncInfo g_func_infos[] = {
930     {"patsubst", &PatsubstFunc, 3, 3, false, false},
931     {"strip", &StripFunc, 1, 1, false, false},
932     {"subst", &SubstFunc, 3, 3, false, false},
933     {"findstring", &FindstringFunc, 2, 2, false, false},
934     {"filter", &FilterFunc, 2, 2, false, false},
935     {"filter-out", &FilterOutFunc, 2, 2, false, false},
936     {"sort", &SortFunc, 1, 1, false, false},
937     {"word", &WordFunc, 2, 2, false, false},
938     {"wordlist", &WordlistFunc, 3, 3, false, false},
939     {"words", &WordsFunc, 1, 1, false, false},
940     {"firstword", &FirstwordFunc, 1, 1, false, false},
941     {"lastword", &LastwordFunc, 1, 1, false, false},
942 
943     {"join", &JoinFunc, 2, 2, false, false},
944     {"wildcard", &WildcardFunc, 1, 1, false, false},
945     {"dir", &DirFunc, 1, 1, false, false},
946     {"notdir", &NotdirFunc, 1, 1, false, false},
947     {"suffix", &SuffixFunc, 1, 1, false, false},
948     {"basename", &BasenameFunc, 1, 1, false, false},
949     {"addsuffix", &AddsuffixFunc, 2, 2, false, false},
950     {"addprefix", &AddprefixFunc, 2, 2, false, false},
951     {"realpath", &RealpathFunc, 1, 1, false, false},
952     {"abspath", &AbspathFunc, 1, 1, false, false},
953 
954     {"if", &IfFunc, 3, 2, false, true},
955     {"and", &AndFunc, 0, 0, true, false},
956     {"or", &OrFunc, 0, 0, true, false},
957 
958     {"value", &ValueFunc, 1, 1, false, false},
959     {"eval", &EvalFunc, 1, 1, false, false},
960     {"shell", &ShellFunc, 1, 1, false, false},
961     {"call", &CallFunc, 0, 0, false, false},
962     {"foreach", &ForeachFunc, 3, 3, false, false},
963 
964     {"origin", &OriginFunc, 1, 1, false, false},
965     {"flavor", &FlavorFunc, 1, 1, false, false},
966 
967     {"info", &InfoFunc, 1, 1, false, false},
968     {"warning", &WarningFunc, 1, 1, false, false},
969     {"error", &ErrorFunc, 1, 1, false, false},
970 
971     {"file", &FileFunc, 2, 1, false, false},
972 
973     /* Kati custom extension functions */
974     {"KATI_deprecated_var", &DeprecatedVarFunc, 2, 1, false, false},
975     {"KATI_obsolete_var", &ObsoleteVarFunc, 2, 1, false, false},
976     {"KATI_deprecate_export", &DeprecateExportFunc, 1, 1, false, false},
977     {"KATI_obsolete_export", &ObsoleteExportFunc, 1, 1, false, false},
978 };
979 
980 unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
981 
982 }  // namespace
983 
InitFuncTable()984 void InitFuncTable() {
985   g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
986   for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
987     FuncInfo* fi = &g_func_infos[i];
988     bool ok = g_func_info_map->emplace(fi->name, fi).second;
989     CHECK(ok);
990   }
991 }
992 
QuitFuncTable()993 void QuitFuncTable() {
994   delete g_func_info_map;
995 }
996 
GetFuncInfo(StringPiece name)997 FuncInfo* GetFuncInfo(StringPiece name) {
998   auto found = g_func_info_map->find(name);
999   if (found == g_func_info_map->end())
1000     return NULL;
1001   return found->second;
1002 }
1003 
GetShellCommandResults()1004 const vector<CommandResult*>& GetShellCommandResults() {
1005   return g_command_results;
1006 }
1007