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