1#include "internal.h"
2
3#include <ctype.h>
4#include <glob.h>
5#include <stddef.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10/* implementations of gnu make builtin functions*/
11
12static char *trimspacesdup(const char *s);
13static char *shellquote(const char *s);
14
15static int
16matchword(const char *patterns, const char *word, size_t nword)
17{
18 size_t p0, p1;
19
20 for (p0 = 0; patterns[p0];) {
21 const char *pat;
22 char *cooked;
23 size_t npat, i, n, pct, pre, suf;
24 int haspct;
25
26 while (patterns[p0] && isspace((unsigned char)patterns[p0]))
27 p0++;
28 if (!patterns[p0])
29 break;
30 p1 = p0;
31 while (patterns[p1] && !isspace((unsigned char)patterns[p1]))
32 p1++;
33 pat = patterns + p0;
34 npat = p1 - p0;
35 cooked = xmalloc(npat + 1);
36 n = 0;
37 pct = 0;
38 haspct = 0;
39 for (i = 0; i < npat; i++) {
40 if (pat[i] == '\\' && i + 1 < npat && (pat[i + 1] == '\\' || pat[i + 1] == '%')) {
41 cooked[n++] = pat[++i];
42 continue;
43 }
44 if (pat[i] == '%' && !haspct) {
45 pct = n;
46 haspct = 1;
47 continue;
48 }
49 cooked[n++] = pat[i];
50 }
51 cooked[n] = 0;
52 if (!haspct) {
53 if (n == nword && memcmp(cooked, word, n) == 0) {
54 free(cooked);
55 return 1;
56 }
57 free(cooked);
58 p0 = p1;
59 continue;
60 }
61 pre = pct;
62 suf = n - pre;
63 if (nword >= pre + suf &&
64 memcmp(cooked, word, pre) == 0 &&
65 memcmp(cooked + pre, word + nword - suf, suf) == 0) {
66 free(cooked);
67 return 1;
68 }
69 free(cooked);
70 p0 = p1;
71 }
72 return 0;
73}
74
75static char *
76shellquote(const char *s)
77{
78 size_t i, len, cap;
79 char *out;
80
81 cap = strlen(s) * 4 + 3;
82 out = xmalloc(cap);
83 len = 0;
84 out[len++] = '\'';
85 for (i = 0; s[i]; i++) {
86 if (s[i] == '\'') {
87 memcpy(out + len, "'\\''", 4);
88 len += 4;
89 } else {
90 out[len++] = s[i];
91 }
92 }
93 out[len++] = '\'';
94 out[len] = 0;
95 return out;
96}
97
98char *
99fnwildcard(const char *patterns)
100{
101 char *out;
102 size_t len, cap;
103 const char *p;
104
105 cap = 64;
106 len = 0;
107 out = xmalloc(cap);
108 out[0] = 0;
109
110 p = patterns;
111 while (*p) {
112 glob_t g;
113 size_t i;
114 const char *start;
115 char *pat;
116 int rc;
117
118 while (*p && isspace((unsigned char)*p))
119 p++;
120 if (!*p)
121 break;
122 start = p;
123 while (*p && !isspace((unsigned char)*p))
124 p++;
125 pat = xstrndup(start, (size_t)(p - start));
126
127 memset(&g, 0, sizeof(g));
128 rc = glob(pat, 0, 0, &g);
129 free(pat);
130 if (rc == 0) {
131 for (i = 0; i < g.gl_pathc; i++) {
132 size_t plen, need;
133
134 plen = strlen(g.gl_pathv[i]);
135 need = len + plen + 2;
136 if (need > cap) {
137 while (cap < need)
138 cap *= 2;
139 out = xrealloc(out, cap);
140 }
141 if (len)
142 out[len++] = ' ';
143 memcpy(out + len, g.gl_pathv[i], plen);
144 len += plen;
145 out[len] = 0;
146 }
147 }
148 globfree(&g);
149 }
150 return out;
151}
152
153char *
154fnshell(const char *cmd)
155{
156 FILE *fp;
157 char buf[4096];
158 char *out;
159 size_t len, cap, nread, i, j;
160
161 fp = popen(cmd, "r");
162 if (!fp)
163 return xstrdup("");
164
165 cap = 64;
166 len = 0;
167 out = xmalloc(cap);
168 out[0] = 0;
169
170 while ((nread = fread(buf, 1, sizeof(buf), fp)) > 0) {
171 if (len + nread + 1 > cap) {
172 while (cap < len + nread + 1)
173 cap *= 2;
174 out = xrealloc(out, cap);
175 }
176 memcpy(out + len, buf, nread);
177 len += nread;
178 }
179 out[len] = 0;
180 pclose(fp);
181
182 while (len > 0 && (out[len - 1] == '\n' || out[len - 1] == '\r'))
183 len--;
184 for (i = 0, j = 0; i < len; i++) {
185 unsigned char ch;
186
187 ch = (unsigned char)out[i];
188 if (ch == '\n' || ch == '\r') {
189 out[j++] = ' ';
190 continue;
191 }
192 out[j++] = out[i];
193 }
194 out[j] = 0;
195 return out;
196}
197
198char *
199fnfilter(const char *patterns, const char *text)
200{
201 char *out;
202 size_t cap, len, i, j;
203
204 cap = strlen(text) + 1;
205 if (cap < 64)
206 cap = 64;
207 len = 0;
208 out = xmalloc(cap);
209 out[0] = 0;
210
211 for (i = 0; text[i];) {
212 int keep;
213
214 while (text[i] && isspace((unsigned char)text[i]))
215 i++;
216 if (!text[i])
217 break;
218 j = i;
219 while (text[j] && !isspace((unsigned char)text[j]))
220 j++;
221
222 keep = matchword(patterns, text + i, j - i);
223
224 if (keep) {
225 size_t wn, need;
226
227 wn = j - i;
228 need = len + wn + 2;
229 if (need > cap) {
230 while (cap < need)
231 cap *= 2;
232 out = xrealloc(out, cap);
233 }
234 if (len)
235 out[len++] = ' ';
236 memcpy(out + len, text + i, wn);
237 len += wn;
238 out[len] = 0;
239 }
240 i = j;
241 }
242
243 return out;
244}
245
246char *
247fnfilterout(const char *patterns, const char *text)
248{
249 char *out;
250 size_t cap, len, i, j;
251
252 cap = strlen(text) + 1;
253 if (cap < 64)
254 cap = 64;
255 len = 0;
256 out = xmalloc(cap);
257 out[0] = 0;
258
259 for (i = 0; text[i];) {
260 size_t wn, need;
261
262 while (text[i] && isspace((unsigned char)text[i]))
263 i++;
264 if (!text[i])
265 break;
266 j = i;
267 while (text[j] && !isspace((unsigned char)text[j]))
268 j++;
269 if (matchword(patterns, text + i, j - i)) {
270 i = j;
271 continue;
272 }
273 wn = j - i;
274 need = len + wn + 2;
275 if (need > cap) {
276 while (cap < need)
277 cap *= 2;
278 out = xrealloc(out, cap);
279 }
280 if (len)
281 out[len++] = ' ';
282 memcpy(out + len, text + i, wn);
283 len += wn;
284 out[len] = 0;
285 i = j;
286 }
287
288 return out;
289}
290
291char *
292fnfindstring(const char *find, const char *in)
293{
294 return strstr(in, find) ? xstrdup(find) : xstrdup("");
295}
296
297char *
298fnaddprefix(const char *prefix, const char *names)
299{
300 char *out;
301 size_t cap, len, i, j, nprefix;
302
303 nprefix = strlen(prefix);
304 cap = strlen(names) + nprefix + 1;
305 if (cap < 64)
306 cap = 64;
307 len = 0;
308 out = xmalloc(cap);
309 out[0] = 0;
310
311 for (i = 0; names[i];) {
312 size_t wn, need;
313
314 while (names[i] && isspace((unsigned char)names[i]))
315 i++;
316 if (!names[i])
317 break;
318 j = i;
319 while (names[j] && !isspace((unsigned char)names[j]))
320 j++;
321 wn = j - i;
322 need = len + nprefix + wn + 2;
323 if (need > cap) {
324 while (cap < need)
325 cap *= 2;
326 out = xrealloc(out, cap);
327 }
328 if (len)
329 out[len++] = ' ';
330 memcpy(out + len, prefix, nprefix);
331 len += nprefix;
332 memcpy(out + len, names + i, wn);
333 len += wn;
334 out[len] = 0;
335 i = j;
336 }
337
338 return out;
339}
340
341char *
342fnaddsuffix(const char *suffix, const char *names)
343{
344 char *out;
345 size_t cap, len, i, j, nsuffix;
346
347 nsuffix = strlen(suffix);
348 cap = strlen(names) + nsuffix + 1;
349 if (cap < 64)
350 cap = 64;
351 len = 0;
352 out = xmalloc(cap);
353 out[0] = 0;
354
355 for (i = 0; names[i];) {
356 size_t wn, need;
357
358 while (names[i] && isspace((unsigned char)names[i]))
359 i++;
360 if (!names[i])
361 break;
362 j = i;
363 while (names[j] && !isspace((unsigned char)names[j]))
364 j++;
365 wn = j - i;
366 need = len + wn + nsuffix + 2;
367 if (need > cap) {
368 while (cap < need)
369 cap *= 2;
370 out = xrealloc(out, cap);
371 }
372 if (len)
373 out[len++] = ' ';
374 memcpy(out + len, names + i, wn);
375 len += wn;
376 memcpy(out + len, suffix, nsuffix);
377 len += nsuffix;
378 out[len] = 0;
379 i = j;
380 }
381
382 return out;
383}
384
385char *
386fnjoin(const char *list1, const char *list2)
387{
388 char *out;
389 size_t i1, j1, i2, j2, len, cap;
390
391 cap = strlen(list1) + strlen(list2) + 1;
392 if (cap < 64)
393 cap = 64;
394 len = 0;
395 out = xmalloc(cap);
396 out[0] = 0;
397
398 i1 = 0;
399 i2 = 0;
400 for (;;) {
401 size_t w1, w2, need;
402
403 while (list1[i1] && isspace((unsigned char)list1[i1]))
404 i1++;
405 while (list2[i2] && isspace((unsigned char)list2[i2]))
406 i2++;
407 if (!list1[i1] && !list2[i2])
408 break;
409
410 j1 = i1;
411 while (list1[j1] && !isspace((unsigned char)list1[j1]))
412 j1++;
413 j2 = i2;
414 while (list2[j2] && !isspace((unsigned char)list2[j2]))
415 j2++;
416 w1 = j1 - i1;
417 w2 = j2 - i2;
418
419 need = len + w1 + w2 + 2;
420 if (need > cap) {
421 while (cap < need)
422 cap *= 2;
423 out = xrealloc(out, cap);
424 }
425 if (len)
426 out[len++] = ' ';
427 if (w1) {
428 memcpy(out + len, list1 + i1, w1);
429 len += w1;
430 }
431 if (w2) {
432 memcpy(out + len, list2 + i2, w2);
433 len += w2;
434 }
435 out[len] = 0;
436
437 i1 = j1;
438 i2 = j2;
439 }
440
441 return out;
442}
443
444char *
445fnstrip(const char *text)
446{
447 char *out;
448 size_t i, j, len, cap;
449 int need_space;
450
451 cap = strlen(text) + 1;
452 if (cap < 16)
453 cap = 16;
454 out = xmalloc(cap);
455 len = 0;
456 need_space = 0;
457
458 for (i = 0; text[i];) {
459 while (text[i] && isspace((unsigned char)text[i]))
460 i++;
461 if (!text[i])
462 break;
463 if (need_space)
464 out[len++] = ' ';
465 j = i;
466 while (text[j] && !isspace((unsigned char)text[j]))
467 j++;
468 if (len + (j - i) + 1 > cap) {
469 while (cap < len + (j - i) + 1)
470 cap *= 2;
471 out = xrealloc(out, cap);
472 }
473 memcpy(out + len, text + i, j - i);
474 len += j - i;
475 need_space = 1;
476 i = j;
477 }
478 out[len] = 0;
479 return out;
480}
481
482static int
483cmpstr(const void *a, const void *b)
484{
485 const char *const *sa;
486 const char *const *sb;
487
488 sa = a;
489 sb = b;
490 return strcmp(*sa, *sb);
491}
492
493char *
494fnsort(const char *text)
495{
496 char **words;
497 char *out;
498 size_t i, j, n, cap, len;
499
500 words = 0;
501 n = 0;
502 for (i = 0; text[i];) {
503 size_t start;
504
505 while (text[i] && isspace((unsigned char)text[i]))
506 i++;
507 if (!text[i])
508 break;
509 start = i;
510 while (text[i] && !isspace((unsigned char)text[i]))
511 i++;
512 words = xrealloc(words, (n + 1) * sizeof(words[0]));
513 words[n++] = xstrndup(text + start, i - start);
514 }
515
516 if (n == 0)
517 return xstrdup("");
518
519 qsort(words, n, sizeof(words[0]), cmpstr);
520
521 cap = strlen(text) + 1;
522 if (cap < 64)
523 cap = 64;
524 len = 0;
525 out = xmalloc(cap);
526 out[0] = 0;
527 for (i = 0; i < n; i++) {
528 size_t wn, need;
529
530 if (i > 0 && strcmp(words[i - 1], words[i]) == 0)
531 continue;
532 wn = strlen(words[i]);
533 need = len + wn + 2;
534 if (need > cap) {
535 while (cap < need)
536 cap *= 2;
537 out = xrealloc(out, cap);
538 }
539 if (len)
540 out[len++] = ' ';
541 memcpy(out + len, words[i], wn);
542 len += wn;
543 out[len] = 0;
544 }
545
546 for (j = 0; j < n; j++)
547 free(words[j]);
548 free(words);
549 return out;
550}
551
552char *
553fninfo(struct EvalCtx *ctx, const char *args)
554{
555 char *text;
556
557 text = expandstr(ctx, args);
558 if (ctx->avoid_io && ctx->side_effects) {
559 char *cmd, *quoted;
560
561 /* this is kinda a hack, but it gets more tests to pass:
562 * we tag delayed $(info) output so the test wrapper can distinguish
563 synthetic info side effects from real normal echo-having recipes. */
564 quoted = shellquote(text);
565 cmd = xmalloc(strlen("printf '%s\\n' # __shin_info__") + strlen(quoted) + 1);
566 sprintf(cmd, "printf '%%s\\n' %s # __shin_info__", quoted);
567 free(quoted);
568 addstr(ctx->side_effects, cmd);
569 free(cmd);
570 } else {
571 fputs(text, stdout);
572 fputc('\n', stdout);
573 }
574 free(text);
575 return xstrdup("");
576}
577
578char *
579fnorigin(struct EvalCtx *ctx, const char *args)
580{
581 char *name, *trimmed;
582 struct Var *v;
583 const char *origin;
584
585 name = expandstr(ctx, args);
586 trimmed = trimspacesdup(name);
587 free(name);
588
589 if (trimmed[0] == '@' && trimmed[1] == 0) {
590 free(trimmed);
591 return xstrdup("automatic");
592 }
593 if (trimmed[0] == '<' && trimmed[1] == 0) {
594 free(trimmed);
595 return xstrdup("automatic");
596 }
597 if (trimmed[0] == '^' && trimmed[1] == 0) {
598 free(trimmed);
599 return xstrdup("automatic");
600 }
601 if (trimmed[0] == '+' && trimmed[1] == 0) {
602 free(trimmed);
603 return xstrdup("automatic");
604 }
605 if (trimmed[0] == '?' && trimmed[1] == 0) {
606 free(trimmed);
607 return xstrdup("automatic");
608 }
609 if (trimmed[0] == '*' && trimmed[1] == 0) {
610 free(trimmed);
611 return xstrdup("automatic");
612 }
613 if (trimmed[0] == '%' && trimmed[1] == 0) {
614 free(trimmed);
615 return xstrdup("automatic");
616 }
617
618 v = findvar(ctx->env, trimmed);
619 if (!v) {
620 free(trimmed);
621 return xstrdup("undefined");
622 }
623
624 switch (v->origin) {
625 case ORIGIN_DEFAULT:
626 origin = "default";
627 break;
628 case ORIGIN_ENV:
629 origin = "environment";
630 break;
631 case ORIGIN_FILE:
632 origin = "file";
633 break;
634 case ORIGIN_ENV_OVERRIDE:
635 origin = "environment override";
636 break;
637 case ORIGIN_COMMAND:
638 origin = "command line";
639 break;
640 case ORIGIN_OVERRIDE:
641 origin = "override";
642 break;
643 default:
644 origin = "undefined";
645 break;
646 }
647 free(trimmed);
648 return xstrdup(origin);
649}
650
651char *
652fnvalue(struct EvalCtx *ctx, const char *args)
653{
654 char *name, *trimmed;
655 struct Var *v;
656
657 name = expandstr(ctx, args);
658 trimmed = trimspacesdup(name);
659 free(name);
660 v = findvar(ctx->env, trimmed);
661 free(trimmed);
662 if (!v)
663 return xstrdup("");
664 return xstrdup(v->val);
665}
666
667char *
668fnnotdir(const char *names)
669{
670 size_t i, j, k, len, cap, need, wn;
671 const char *slash;
672 char *out;
673
674 cap = strlen(names) + 1;
675 if (cap < 16)
676 cap = 16;
677 len = 0;
678 out = xmalloc(cap);
679 out[0] = 0;
680 for (i = 0; names[i];) {
681 while (names[i] && isspace((unsigned char)names[i]))
682 i++;
683 if (!names[i])
684 break;
685 j = i;
686 while (names[j] && !isspace((unsigned char)names[j]))
687 j++;
688 slash = 0;
689 for (k = i; k < j; k++) {
690 if (names[k] == '/')
691 slash = names + k;
692 }
693 wn = slash ? (size_t)(names + j - slash - 1) : j - i;
694 need = len + wn + 2;
695 if (need > cap) {
696 while (cap < need)
697 cap *= 2;
698 out = xrealloc(out, cap);
699 }
700 if (len)
701 out[len++] = ' ';
702 memcpy(out + len, slash ? slash + 1 : names + i, wn);
703 len += wn;
704 out[len] = 0;
705 i = j;
706 }
707 return out;
708}
709
710char *
711fndir(const char *names)
712{
713 size_t i, j, k, len, cap, need, dlen;
714 const char *slash;
715 char *out;
716
717 cap = strlen(names) + 1;
718 if (cap < 16)
719 cap = 16;
720 len = 0;
721 out = xmalloc(cap);
722 out[0] = 0;
723 for (i = 0; names[i];) {
724 while (names[i] && isspace((unsigned char)names[i]))
725 i++;
726 if (!names[i])
727 break;
728 j = i;
729 while (names[j] && !isspace((unsigned char)names[j]))
730 j++;
731 slash = 0;
732 for (k = i; k < j; k++) {
733 if (names[k] == '/')
734 slash = names + k;
735 }
736 dlen = slash ? (size_t)(slash - (names + i)) + 1 : 2;
737 need = len + dlen + 2;
738 if (need > cap) {
739 while (cap < need)
740 cap *= 2;
741 out = xrealloc(out, cap);
742 }
743 if (len)
744 out[len++] = ' ';
745 if (slash) {
746 memcpy(out + len, names + i, dlen);
747 } else {
748 memcpy(out + len, "./", 2);
749 }
750 len += dlen;
751 out[len] = 0;
752 i = j;
753 }
754 return out;
755}
756
757char *
758fnbasename(const char *names)
759{
760 size_t i, j, k, len, cap, need, wn;
761 const char *fnstart, *dot;
762 char *out;
763
764 cap = strlen(names) + 1;
765 if (cap < 16)
766 cap = 16;
767 len = 0;
768 out = xmalloc(cap);
769 out[0] = 0;
770 for (i = 0; names[i];) {
771 while (names[i] && isspace((unsigned char)names[i]))
772 i++;
773 if (!names[i])
774 break;
775 j = i;
776 while (names[j] && !isspace((unsigned char)names[j]))
777 j++;
778 wn = j - i;
779 fnstart = names + i;
780 dot = 0;
781 for (k = i; k < j; k++) {
782 if (names[k] == '/') {
783 fnstart = names + k + 1;
784 dot = 0;
785 } else if (names[k] == '.' && names + k > fnstart)
786 dot = names + k;
787 }
788 if (dot)
789 wn = (size_t)(dot - (names + i));
790 need = len + wn + 2;
791 if (need > cap) {
792 while (cap < need)
793 cap *= 2;
794 out = xrealloc(out, cap);
795 }
796 if (len)
797 out[len++] = ' ';
798 memcpy(out + len, names + i, wn);
799 len += wn;
800 out[len] = 0;
801 i = j;
802 }
803 return out;
804}
805
806char *
807fnsubst(const char *from, const char *to, const char *text)
808{
809 size_t nfrom, nto, i, len, cap, need;
810 char *out;
811
812 nfrom = strlen(from);
813 nto = strlen(to);
814 if (!nfrom)
815 return xstrdup(text);
816 cap = strlen(text) + 1;
817 if (cap < 16)
818 cap = 16;
819 len = 0;
820 out = xmalloc(cap);
821 out[0] = 0;
822 for (i = 0; text[i];) {
823 if (strncmp(text + i, from, nfrom) == 0) {
824 need = len + nto + 1;
825 if (need > cap) {
826 while (cap < need)
827 cap *= 2;
828 out = xrealloc(out, cap);
829 }
830 memcpy(out + len, to, nto);
831 len += nto;
832 i += nfrom;
833 } else {
834 need = len + 2;
835 if (need > cap) {
836 cap *= 2;
837 out = xrealloc(out, cap);
838 }
839 out[len++] = text[i++];
840 }
841 }
842 out[len] = 0;
843 return out;
844}
845
846static char *
847patsubstword(const char *word, size_t wn, const char *pattern, const char *replacement)
848{
849 size_t npat, nrep, pre, suf, stem, rpre, rsuf;
850 const char *pct, *rpct;
851 char *out;
852
853 npat = strlen(pattern);
854 nrep = strlen(replacement);
855 pct = strchr(pattern, '%');
856 if (!pct) {
857 if (wn == npat && memcmp(word, pattern, wn) == 0)
858 return xstrdup(replacement);
859 return xstrndup(word, wn);
860 }
861 pre = (size_t)(pct - pattern);
862 suf = npat - pre - 1;
863 if (wn < pre + suf)
864 return xstrndup(word, wn);
865 if (memcmp(word, pattern, pre) != 0 || memcmp(word + wn - suf, pct + 1, suf) != 0)
866 return xstrndup(word, wn);
867 stem = wn - pre - suf;
868 rpct = strchr(replacement, '%');
869 if (!rpct)
870 return xstrdup(replacement);
871 rpre = (size_t)(rpct - replacement);
872 rsuf = nrep - rpre - 1;
873 out = xmalloc(rpre + stem + rsuf + 1);
874 memcpy(out, replacement, rpre);
875 memcpy(out + rpre, word + pre, stem);
876 memcpy(out + rpre + stem, rpct + 1, rsuf);
877 out[rpre + stem + rsuf] = 0;
878 return out;
879}
880
881char *
882fnpatsubst(const char *pattern, const char *replacement, const char *text)
883{
884 size_t i, j, wlen, len, cap, need;
885 char *w, *out;
886
887 cap = strlen(text) + 1;
888 if (cap < 16)
889 cap = 16;
890 len = 0;
891 out = xmalloc(cap);
892 out[0] = 0;
893 for (i = 0; text[i];) {
894 while (text[i] && isspace((unsigned char)text[i]))
895 i++;
896 if (!text[i])
897 break;
898 j = i;
899 while (text[j] && !isspace((unsigned char)text[j]))
900 j++;
901 w = patsubstword(text + i, j - i, pattern, replacement);
902 wlen = strlen(w);
903 need = len + wlen + 2;
904 if (need > cap) {
905 while (cap < need)
906 cap *= 2;
907 out = xrealloc(out, cap);
908 }
909 if (len)
910 out[len++] = ' ';
911 memcpy(out + len, w, wlen);
912 len += wlen;
913 out[len] = 0;
914 free(w);
915 i = j;
916 }
917 return out;
918}
919
920char *
921fnif(const char *cond, const char *then, const char *otherwise)
922{
923 const char *p;
924
925 for (p = cond; *p && isspace((unsigned char)*p); p++)
926 ;
927 return xstrdup(*p ? then : otherwise);
928}
929
930char *
931fnwords(const char *text)
932{
933 size_t n;
934 char buf[32];
935
936 n = 0;
937 while (*text) {
938 while (*text && isspace((unsigned char)*text))
939 text++;
940 if (!*text)
941 break;
942 n++;
943 while (*text && !isspace((unsigned char)*text))
944 text++;
945 }
946 snprintf(buf, sizeof(buf), "%zu", n);
947 return xstrdup(buf);
948}
949
950char *
951fnword(const char *n, const char *list)
952{
953 size_t idx, i, wstart;
954 char *end;
955
956 while (isspace((unsigned char)*n))
957 n++;
958 if (!*n)
959 return xstrdup("");
960 idx = (size_t)strtoul(n, &end, 10);
961 if (end == n || idx == 0)
962 return xstrdup("");
963
964 i = 0;
965 while (list[i]) {
966 while (list[i] && isspace((unsigned char)list[i]))
967 i++;
968 if (!list[i])
969 break;
970 wstart = i;
971 while (list[i] && !isspace((unsigned char)list[i]))
972 i++;
973 if (--idx == 0)
974 return xstrndup(list + wstart, i - wstart);
975 }
976 return xstrdup("");
977}
978
979char *
980fnwordlist(const char *s, const char *e, const char *list)
981{
982 size_t start, end, n, i, wstart, wlen, len, cap, need;
983 char *out, *ep;
984
985 while (isspace((unsigned char)*s))
986 s++;
987 while (isspace((unsigned char)*e))
988 e++;
989 if (!*s || !*e)
990 return xstrdup("");
991 start = (size_t)strtoul(s, &ep, 10);
992 if (ep == s || start == 0)
993 return xstrdup("");
994 end = (size_t)strtoul(e, &ep, 10);
995 if (ep == e)
996 return xstrdup("");
997 if (start > end)
998 return xstrdup("");
999
1000 cap = strlen(list) + 1;
1001 if (cap < 16)
1002 cap = 16;
1003 len = 0;
1004 out = xmalloc(cap);
1005 out[0] = 0;
1006
1007 n = 0;
1008 i = 0;
1009 while (list[i]) {
1010 while (list[i] && isspace((unsigned char)list[i]))
1011 i++;
1012 if (!list[i])
1013 break;
1014 wstart = i;
1015 while (list[i] && !isspace((unsigned char)list[i]))
1016 i++;
1017 wlen = i - wstart;
1018 n++;
1019 if (n < start)
1020 continue;
1021 if (n > end)
1022 break;
1023 need = len + wlen + 2;
1024 if (need > cap) {
1025 while (cap < need)
1026 cap *= 2;
1027 out = xrealloc(out, cap);
1028 }
1029 if (len)
1030 out[len++] = ' ';
1031 memcpy(out + len, list + wstart, wlen);
1032 len += wlen;
1033 out[len] = 0;
1034 }
1035 return out;
1036}
1037
1038char *
1039fnfirstword(const char *list)
1040{
1041 size_t i, j;
1042
1043 for (i = 0; list[i] && isspace((unsigned char)list[i]); i++)
1044 ;
1045 if (!list[i])
1046 return xstrdup("");
1047 for (j = i; list[j] && !isspace((unsigned char)list[j]); j++)
1048 ;
1049 return xstrndup(list + i, j - i);
1050}
1051
1052char *
1053fnlastword(const char *list)
1054{
1055 const char *last;
1056 size_t lastlen;
1057 const char *p;
1058
1059 last = 0;
1060 lastlen = 0;
1061 p = list;
1062 while (*p) {
1063 while (*p && isspace((unsigned char)*p))
1064 p++;
1065 if (!*p)
1066 break;
1067 last = p;
1068 while (*p && !isspace((unsigned char)*p))
1069 p++;
1070 lastlen = (size_t)(p - last);
1071 }
1072 return last ? xstrndup(last, lastlen) : xstrdup("");
1073}
1074
1075static char **
1076splitargsraw(const char *s, size_t *out_n)
1077{
1078 size_t i, start, depth, argc, n;
1079 char **argv;
1080
1081 while (*s && isspace((unsigned char)*s))
1082 s++;
1083 n = strlen(s);
1084 argv = 0;
1085 argc = 0;
1086 start = 0;
1087 depth = 0;
1088 for (i = 0; i < n; i++) {
1089 if (s[i] == '$' && i + 1 < n && (s[i + 1] == '(' || s[i + 1] == '{')) {
1090 depth++;
1091 i++;
1092 continue;
1093 }
1094 if ((s[i] == ')' || s[i] == '}') && depth > 0) {
1095 depth--;
1096 continue;
1097 }
1098 if (s[i] != ',' || depth != 0)
1099 continue;
1100 argv = xrealloc(argv, (argc + 1) * sizeof(argv[0]));
1101 argv[argc++] = xstrndup(s + start, i - start);
1102 start = i + 1;
1103 }
1104 argv = xrealloc(argv, (argc + 1) * sizeof(argv[0]));
1105 argv[argc++] = xstrndup(s + start, n - start);
1106 *out_n = argc;
1107 return argv;
1108}
1109
1110static void
1111freeargsraw(char **argv, size_t argc)
1112{
1113 size_t i;
1114
1115 for (i = 0; i < argc; i++)
1116 free(argv[i]);
1117 free(argv);
1118}
1119
1120static char *
1121trimspacesdup(const char *s)
1122{
1123 size_t i, j;
1124
1125 for (i = 0; s[i] && isspace((unsigned char)s[i]); i++)
1126 ;
1127 for (j = strlen(s); j > i && isspace((unsigned char)s[j - 1]); j--)
1128 ;
1129 return xstrndup(s + i, j - i);
1130}
1131
1132char *
1133fnrealpath(const char *names)
1134{
1135 size_t i, j, cap, len, need, rplen;
1136 char *out, *rp, *w;
1137
1138 cap = strlen(names) + 1;
1139 if (cap < 16)
1140 cap = 16;
1141 len = 0;
1142 out = xmalloc(cap);
1143 out[0] = 0;
1144 for (i = 0; names[i];) {
1145 while (names[i] && isspace((unsigned char)names[i]))
1146 i++;
1147 if (!names[i])
1148 break;
1149 j = i;
1150 while (names[j] && !isspace((unsigned char)names[j]))
1151 j++;
1152 w = xstrndup(names + i, j - i);
1153 rp = realpath(w, NULL);
1154 free(w);
1155 rplen = rp ? strlen(rp) : 0;
1156 need = len + rplen + 2;
1157 if (need > cap) {
1158 while (cap < need)
1159 cap *= 2;
1160 out = xrealloc(out, cap);
1161 }
1162 if (rp) {
1163 if (len)
1164 out[len++] = ' ';
1165 memcpy(out + len, rp, rplen);
1166 free(rp);
1167 len += rplen;
1168 out[len] = 0;
1169 }
1170 i = j;
1171 }
1172 return out;
1173}
1174
1175static char *
1176normabspath(const char *path)
1177{
1178 char *cwd, *jp, *np;
1179
1180 if (*path == '/') {
1181 return normpath(path);
1182 }
1183 cwd = getcwddup();
1184 jp = joinpath(cwd, path);
1185 np = normpath(jp);
1186 free(cwd);
1187 free(jp);
1188 return np;
1189}
1190
1191char *
1192fnabspath(const char *names)
1193{
1194 size_t i, j, cap, len, need, nplen;
1195 char *out, *np, *w;
1196
1197 cap = strlen(names) + 1;
1198 if (cap < 16)
1199 cap = 16;
1200 len = 0;
1201 out = xmalloc(cap);
1202 out[0] = 0;
1203 for (i = 0; names[i];) {
1204 while (names[i] && isspace((unsigned char)names[i]))
1205 i++;
1206 if (!names[i])
1207 break;
1208 j = i;
1209 while (names[j] && !isspace((unsigned char)names[j]))
1210 j++;
1211 w = xstrndup(names + i, j - i);
1212 np = normabspath(w);
1213 free(w);
1214 nplen = strlen(np);
1215 need = len + nplen + 2;
1216 if (need > cap) {
1217 while (cap < need)
1218 cap *= 2;
1219 out = xrealloc(out, cap);
1220 }
1221 if (len)
1222 out[len++] = ' ';
1223 memcpy(out + len, np, nplen);
1224 free(np);
1225 len += nplen;
1226 out[len] = 0;
1227 i = j;
1228 }
1229 return out;
1230}
1231
1232char *
1233fnor(struct EvalCtx *ctx, const char *args)
1234{
1235 char **raw;
1236 char *val;
1237 size_t argc, i;
1238
1239 raw = splitargsraw(args, &argc);
1240 for (i = 0; i < argc; i++) {
1241 val = expandstr(ctx, raw[i]);
1242 if (val[0]) {
1243 freeargsraw(raw, argc);
1244 return val;
1245 }
1246 free(val);
1247 }
1248 freeargsraw(raw, argc);
1249 return xstrdup("");
1250}
1251
1252char *
1253fncall(struct EvalCtx *ctx, const char *args)
1254{
1255 char **raw, **exp;
1256 char *name, *val;
1257 size_t argc, i;
1258 struct Var *v;
1259 struct CallFrame frame;
1260
1261 raw = splitargsraw(args, &argc);
1262 if (argc == 0) {
1263 freeargsraw(raw, argc);
1264 return xstrdup("");
1265 }
1266
1267 name = expandstr(ctx, raw[0]);
1268 exp = xrealloc(0, argc * sizeof(exp[0]));
1269 exp[0] = xstrdup(name);
1270 for (i = 1; i < argc; i++)
1271 exp[i] = expandstr(ctx, raw[i]);
1272 freeargsraw(raw, argc);
1273
1274 memset(&frame, 0, sizeof(frame));
1275 frame.args = exp;
1276 frame.nargs = argc;
1277 frame.next = ctx->call;
1278 ctx->call = &frame;
1279
1280 v = findvar(ctx->env, name);
1281 if (v) {
1282 char *body;
1283
1284 /* The function body may use $(eval ...) to replace its own variable,
1285 * so we make a private copy before expanding */
1286 body = xstrdup(v->val);
1287 val = expandstr(ctx, body);
1288 free(body);
1289 } else {
1290 size_t need, pos;
1291 char *inv;
1292
1293 need = 2 + strlen(name) + 2;
1294 for (i = 1; i < argc; i++)
1295 need += strlen(exp[i]) + 1;
1296 inv = xmalloc(need);
1297 pos = 0;
1298 inv[pos++] = '$';
1299 inv[pos++] = '(';
1300 memcpy(inv + pos, name, strlen(name));
1301 pos += strlen(name);
1302 if (argc > 1)
1303 inv[pos++] = ' ';
1304 for (i = 1; i < argc; i++) {
1305 size_t len = strlen(exp[i]);
1306
1307 memcpy(inv + pos, exp[i], len);
1308 pos += len;
1309 if (i + 1 < argc)
1310 inv[pos++] = ',';
1311 }
1312 inv[pos++] = ')';
1313 inv[pos] = 0;
1314 val = expandstr(ctx, inv);
1315 free(inv);
1316 }
1317 ctx->call = frame.next;
1318
1319 for (i = 0; i < argc; i++)
1320 free(exp[i]);
1321 free(exp);
1322 free(name);
1323 return val;
1324}
1325
1326char *
1327fnforeach(struct EvalCtx *ctx, const char *args)
1328{
1329 char **raw;
1330 char *name_raw, *name, *list, *out;
1331 char *saved_val;
1332 const char *iname;
1333 struct Var *saved;
1334 size_t argc, i, j, len, cap;
1335 int saved_simple;
1336 int saved_exported;
1337 enum Origin saved_origin;
1338 int had_saved;
1339
1340 raw = splitargsraw(args, &argc);
1341 if (argc < 3) {
1342 freeargsraw(raw, argc);
1343 return xstrdup("");
1344 }
1345
1346 name_raw = expandstr(ctx, raw[0]);
1347 name = trimspacesdup(name_raw);
1348 free(name_raw);
1349 list = expandstr(ctx, raw[1]);
1350 iname = intern(name);
1351
1352 saved = findvar(ctx->env, name);
1353 had_saved = saved != 0;
1354 if (had_saved) {
1355 saved_val = xstrdup(saved->val);
1356 saved_simple = saved->simple;
1357 saved_origin = saved->origin;
1358 saved_exported = saved->exported;
1359 } else {
1360 saved_val = 0;
1361 saved_simple = 0;
1362 saved_origin = ORIGIN_FILE;
1363 saved_exported = 0;
1364 }
1365
1366 cap = strlen(list) + 1;
1367 if (cap < 16)
1368 cap = 16;
1369 len = 0;
1370 out = xmalloc(cap);
1371 out[0] = 0;
1372
1373 for (i = 0; list[i];) {
1374 char *word, *exp;
1375 size_t wn, need;
1376
1377 while (list[i] && isspace((unsigned char)list[i]))
1378 i++;
1379 if (!list[i])
1380 break;
1381 j = i;
1382 while (list[j] && !isspace((unsigned char)list[j]))
1383 j++;
1384 word = xstrndup(list + i, j - i);
1385 envsetvar(ctx->env, name, word, 1, ORIGIN_OVERRIDE, 0);
1386 exp = expandstr(ctx, raw[2]);
1387 wn = strlen(exp);
1388 need = len + wn + 2;
1389 if (need > cap) {
1390 while (cap < need)
1391 cap *= 2;
1392 out = xrealloc(out, cap);
1393 }
1394 if (len)
1395 out[len++] = ' ';
1396 memcpy(out + len, exp, wn);
1397 len += wn;
1398 out[len] = 0;
1399 free(exp);
1400 i = j;
1401 }
1402
1403 if (had_saved) {
1404 free(saved->val);
1405 saved->val = saved_val;
1406 saved->simple = saved_simple;
1407 saved->origin = saved_origin;
1408 saved->exported = saved_exported;
1409 } else {
1410 envdelvar(ctx->env, iname);
1411 }
1412
1413 free(list);
1414 free(name);
1415 freeargsraw(raw, argc);
1416 return out;
1417}
1418
1419char *
1420fneval(struct EvalCtx *ctx, const char *args)
1421{
1422 char *exp;
1423
1424 while (*args && isspace((unsigned char)*args))
1425 args++;
1426 exp = expandstr(ctx, args);
1427 if (evalsnippet(ctx, "*eval*", exp) < 0)
1428 ctx->errors++;
1429 free(exp);
1430 return xstrdup("");
1431}