func.cc revision 67f9a70b05f54dfa0a4d13c5d2ca16557b8917ad
1#include "func.h"
2
3#include <glob.h>
4#include <limits.h>
5#include <stdio.h>
6#include <stdlib.h>
7
8#include <algorithm>
9#include <iterator>
10#include <unordered_map>
11
12#include "eval.h"
13#include "log.h"
14#include "strutil.h"
15
16namespace {
17
18void PatsubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
19  shared_ptr<string> pat = args[0]->Eval(ev);
20  shared_ptr<string> repl = args[1]->Eval(ev);
21  shared_ptr<string> str = args[2]->Eval(ev);
22  WordWriter ww(s);
23  for (StringPiece tok : WordScanner(*str)) {
24    ww.MaybeAddWhitespace();
25    AppendSubstPattern(tok, *pat, *repl, s);
26  }
27}
28
29void StripFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
30  shared_ptr<string> str = args[0]->Eval(ev);
31  WordWriter ww(s);
32  for (StringPiece tok : WordScanner(*str)) {
33    ww.Write(tok);
34  }
35}
36
37void SubstFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
38  shared_ptr<string> pat = args[0]->Eval(ev);
39  shared_ptr<string> repl = args[1]->Eval(ev);
40  shared_ptr<string> str = args[2]->Eval(ev);
41  size_t index = 0;
42  while (index < str->size()) {
43    size_t found = str->find(*pat, index);
44    if (found == string::npos)
45      break;
46    AppendString(StringPiece(*str).substr(index, found - index), s);
47    AppendString(*repl, s);
48    index = found + pat->size();
49  }
50  AppendString(StringPiece(*str).substr(index), s);
51}
52
53void FindstringFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
54  shared_ptr<string> find = args[0]->Eval(ev);
55  shared_ptr<string> in = args[1]->Eval(ev);
56  if (in->find(*find) != string::npos)
57    AppendString(*find, s);
58}
59
60void FilterFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
61  shared_ptr<string> pat_buf = args[0]->Eval(ev);
62  shared_ptr<string> text = args[1]->Eval(ev);
63  vector<StringPiece> pats;
64  WordScanner(*pat_buf).Split(&pats);
65  WordWriter ww(s);
66  for (StringPiece tok : WordScanner(*text)) {
67    for (StringPiece pat : pats) {
68      if (MatchPattern(tok, pat)) {
69        ww.Write(tok);
70        break;
71      }
72    }
73  }
74}
75
76void FilterOutFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
77  shared_ptr<string> pat_buf = args[0]->Eval(ev);
78  shared_ptr<string> text = args[1]->Eval(ev);
79  vector<StringPiece> pats;
80  WordScanner(*pat_buf).Split(&pats);
81  WordWriter ww(s);
82  for (StringPiece tok : WordScanner(*text)) {
83    bool matched = false;
84    for (StringPiece pat : pats) {
85      if (MatchPattern(tok, pat)) {
86        matched = true;
87        break;
88      }
89    }
90    if (!matched)
91      ww.Write(tok);
92  }
93}
94
95void SortFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
96  shared_ptr<string> list = args[0]->Eval(ev);
97  vector<StringPiece> toks;
98  WordScanner(*list).Split(&toks);
99  sort(toks.begin(), toks.end());
100  WordWriter ww(s);
101  StringPiece prev;
102  for (StringPiece tok : toks) {
103    if (prev != tok) {
104      ww.Write(tok);
105      prev = tok;
106    }
107  }
108}
109
110static int GetNumericValueForFunc(const string& buf) {
111  StringPiece s = TrimLeftSpace(buf);
112  char* end;
113  long n = strtol(s.data(), &end, 10);
114  if (n < 0 || n == LONG_MAX || s.data() + s.size() != end) {
115    return -1;
116  }
117  return n;
118}
119
120void WordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
121  shared_ptr<string> n_str = args[0]->Eval(ev);
122  int n = GetNumericValueForFunc(*n_str);
123  if (n < 0) {
124    ev->Error(StringPrintf(
125        "*** non-numeric first argument to `word' function: '%s'.",
126        n_str->c_str()));
127  }
128  if (n == 0) {
129    ev->Error("*** first argument to `word' function must be greater than 0.");
130  }
131
132  shared_ptr<string> text = args[1]->Eval(ev);
133  for (StringPiece tok : WordScanner(*text)) {
134    n--;
135    if (n == 0) {
136      AppendString(tok, s);
137      break;
138    }
139  }
140}
141
142void WordlistFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
143  shared_ptr<string> s_str = args[0]->Eval(ev);
144  int si = GetNumericValueForFunc(*s_str);
145  if (si < 0) {
146    ev->Error(StringPrintf(
147        "*** non-numeric first argument to `wordlist' function: '%s'.",
148        s_str->c_str()));
149  }
150  if (si == 0) {
151    ev->Error(StringPrintf(
152        "*** invalid first argument to `wordlist' function: %s`",
153        s_str->c_str()));
154  }
155
156  shared_ptr<string> e_str = args[1]->Eval(ev);
157  int ei = GetNumericValueForFunc(*e_str);
158  if (ei < 0) {
159    ev->Error(StringPrintf(
160        "*** non-numeric second argument to `wordlist' function: '%s'.",
161        e_str->c_str()));
162  }
163
164  shared_ptr<string> text = args[2]->Eval(ev);
165  int i = 0;
166  WordWriter ww(s);
167  for (StringPiece tok : WordScanner(*text)) {
168    i++;
169    if (si <= i && i <= ei) {
170      ww.Write(tok);
171    }
172  }
173}
174
175void WordsFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
176  shared_ptr<string> text = args[0]->Eval(ev);
177  WordScanner ws(*text);
178  int n = 0;
179  for (auto iter = ws.begin(); iter != ws.end(); ++iter)
180    n++;
181  char buf[32];
182  sprintf(buf, "%d", n);
183  *s += buf;
184}
185
186void FirstwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
187  shared_ptr<string> text = args[0]->Eval(ev);
188  for (StringPiece tok : WordScanner(*text)) {
189    AppendString(tok, s);
190    return;
191  }
192}
193
194void LastwordFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
195  shared_ptr<string> text = args[0]->Eval(ev);
196  StringPiece last;
197  for (StringPiece tok : WordScanner(*text)) {
198    last = tok;
199  }
200  AppendString(last, s);
201}
202
203void JoinFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
204  shared_ptr<string> list1 = args[0]->Eval(ev);
205  shared_ptr<string> list2 = args[1]->Eval(ev);
206  WordScanner ws1(*list1);
207  WordScanner ws2(*list2);
208  WordWriter ww(s);
209  for (WordScanner::Iterator iter1 = ws1.begin(), iter2 = ws2.begin();
210       iter1 != ws1.end() && iter2 != ws2.end();
211       ++iter1, ++iter2) {
212    ww.Write(*iter1);
213    // Use |AppendString| not to append extra ' '.
214    AppendString(*iter2, s);
215  }
216}
217
218void WildcardFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
219  shared_ptr<string> pat = args[0]->Eval(ev);
220  WordWriter ww(s);
221  for (StringPiece tok : WordScanner(*pat)) {
222    char orig = tok[tok.size()];
223    const_cast<char*>(tok.data())[tok.size()] = '\0';
224
225    // TODO: Make this faster by not always using glob.
226    glob_t gl;
227    glob(tok.data(), GLOB_NOSORT, NULL, &gl);
228    for (size_t i = 0; i < gl.gl_pathc; i++) {
229      ww.Write(gl.gl_pathv[i]);
230    }
231    globfree(&gl);
232
233    const_cast<char*>(tok.data())[tok.size()] = orig;
234  }
235}
236
237void DirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
238  shared_ptr<string> text = args[0]->Eval(ev);
239  WordWriter ww(s);
240  for (StringPiece tok : WordScanner(*text)) {
241    ww.Write(Dirname(tok));
242    if (tok != "/")
243      s->push_back('/');
244  }
245}
246
247void NotdirFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
248  shared_ptr<string> text = args[0]->Eval(ev);
249  WordWriter ww(s);
250  for (StringPiece tok : WordScanner(*text)) {
251    if (tok == "/") {
252      ww.Write(STRING_PIECE(""));
253    } else {
254      ww.Write(Basename(tok));
255    }
256  }
257}
258
259void SuffixFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
260  shared_ptr<string> text = args[0]->Eval(ev);
261  WordWriter ww(s);
262  for (StringPiece tok : WordScanner(*text)) {
263    StringPiece suf = GetExt(tok);
264    if (!suf.empty())
265      ww.Write(suf);
266  }
267}
268
269void BasenameFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
270  shared_ptr<string> text = args[0]->Eval(ev);
271  WordWriter ww(s);
272  for (StringPiece tok : WordScanner(*text)) {
273    ww.Write(StripExt(tok));
274  }
275}
276
277void AddsuffixFunc(const vector<Value*>&, Evaluator*, string*) {
278  printf("TODO(addsuffix)");
279}
280
281void AddprefixFunc(const vector<Value*>&, Evaluator*, string*) {
282  printf("TODO(addprefix)");
283}
284
285void RealpathFunc(const vector<Value*>&, Evaluator*, string*) {
286  printf("TODO(realpath)");
287}
288
289void AbspathFunc(const vector<Value*>&, Evaluator*, string*) {
290  printf("TODO(abspath)");
291}
292
293void IfFunc(const vector<Value*>&, Evaluator*, string*) {
294  printf("TODO(if)");
295}
296
297void AndFunc(const vector<Value*>&, Evaluator*, string*) {
298  printf("TODO(and)");
299}
300
301void OrFunc(const vector<Value*>&, Evaluator*, string*) {
302  printf("TODO(or)");
303}
304
305void ValueFunc(const vector<Value*>&, Evaluator*, string*) {
306  printf("TODO(value)");
307}
308
309void EvalFunc(const vector<Value*>&, Evaluator*, string*) {
310  printf("TODO(eval)");
311}
312
313void ShellFunc(const vector<Value*>& args, Evaluator* ev, string* s) {
314  shared_ptr<string> cmd = args[0]->Eval(ev);
315  LOG("ShellFunc: %s", cmd->c_str());
316  string out;
317  // TODO: Handle $(SHELL).
318  FILE* fp = popen(cmd->c_str(), "r");
319  while (true) {
320    char buf[4096];
321    size_t r = fread(buf, 1, 4096, fp);
322    out.append(buf, buf+r);
323    if (r == 0) {
324      fclose(fp);
325      break;
326    }
327  }
328
329  while (out[out.size()-1] == '\n')
330    out.pop_back();
331  for (size_t i = 0; i < out.size(); i++) {
332    if (out[i] == '\n')
333      out[i] = ' ';
334  }
335  *s += out;
336}
337
338void CallFunc(const vector<Value*>&, Evaluator*, string*) {
339  printf("TODO(call)");
340}
341
342void ForeachFunc(const vector<Value*>&, Evaluator*, string*) {
343  printf("TODO(foreach)");
344}
345
346void OriginFunc(const vector<Value*>&, Evaluator*, string*) {
347  printf("TODO(origin)");
348}
349
350void FlavorFunc(const vector<Value*>&, Evaluator*, string*) {
351  printf("TODO(flavor)");
352}
353
354void InfoFunc(const vector<Value*>& args, Evaluator* ev, string*) {
355  shared_ptr<string> a = args[0]->Eval(ev);
356  printf("%s\n", a->c_str());
357  fflush(stdout);
358}
359
360void WarningFunc(const vector<Value*>& args, Evaluator* ev, string*) {
361  shared_ptr<string> a = args[0]->Eval(ev);
362  printf("%s:%d: %s\n", LOCF(ev->loc()), a->c_str());
363  fflush(stdout);
364}
365
366void ErrorFunc(const vector<Value*>& args, Evaluator* ev, string*) {
367  shared_ptr<string> a = args[0]->Eval(ev);
368  ev->Error(StringPrintf("*** %s.", a->c_str()));
369}
370
371FuncInfo g_func_infos[] = {
372  { "patsubst", &PatsubstFunc, 3 },
373  { "strip", &StripFunc, 1 },
374  { "subst", &SubstFunc, 3 },
375  { "findstring", &FindstringFunc, 2 },
376  { "filter", &FilterFunc, 2 },
377  { "filter-out", &FilterOutFunc, 2 },
378  { "sort", &SortFunc, 1 },
379  { "word", &WordFunc, 2 },
380  { "wordlist", &WordlistFunc, 3 },
381  { "words", &WordsFunc, 1 },
382  { "firstword", &FirstwordFunc, 1 },
383  { "lastword", &LastwordFunc, 1 },
384
385  { "join", &JoinFunc, 2 },
386  { "wildcard", &WildcardFunc, 1 },
387  { "dir", &DirFunc, 1 },
388  { "notdir", &NotdirFunc, 1 },
389  { "suffix", &SuffixFunc, 1 },
390  { "basename", &BasenameFunc, 1 },
391  { "addsuffix", &AddsuffixFunc, 2 },
392  { "addprefix", &AddprefixFunc, 2 },
393  { "realpath", &RealpathFunc, 1 },
394  { "abspath", &AbspathFunc, 1 },
395
396  { "if", &IfFunc, 1 },
397  { "and", &AndFunc, 1 },
398  { "or", &OrFunc, 1 },
399  { "value", &ValueFunc, 1 },
400  { "eval", &EvalFunc, 1 },
401  { "shell", &ShellFunc, 1 },
402  { "call", &CallFunc, 1 },
403  { "foreach", &ForeachFunc, 1 },
404  { "origin", &OriginFunc, 1 },
405  { "flavor", &FlavorFunc, 1 },
406  { "info", &InfoFunc, 1 },
407  { "warning", &WarningFunc, 1 },
408  { "error", &ErrorFunc, 1 },
409};
410
411unordered_map<StringPiece, FuncInfo*>* g_func_info_map;
412
413}  // namespace
414
415void InitFuncTable() {
416  g_func_info_map = new unordered_map<StringPiece, FuncInfo*>;
417  for (size_t i = 0; i < sizeof(g_func_infos) / sizeof(g_func_infos[0]); i++) {
418    FuncInfo* fi = &g_func_infos[i];
419    bool ok = g_func_info_map->insert(make_pair(Intern(fi->name), fi)).second;
420    CHECK(ok);
421  }
422}
423
424void QuitFuncTable() {
425  delete g_func_info_map;
426}
427
428FuncInfo* GetFuncInfo(StringPiece name) {
429  auto found = g_func_info_map->find(name);
430  if (found == g_func_info_map->end())
431    return NULL;
432  return found->second;
433}
434