1#include "ninqu.h"
2
3#include <ctype.h>
4#include <fnmatch.h>
5#include <glob.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/stat.h>
10#include <unistd.h>
11
12long
13mtime_of(const char *path)
14{
15 struct stat st;
16 if (stat(path, &st) != 0)
17 return -1;
18 return (long)st.st_mtime;
19}
20
21int
22file_exists(const char *path)
23{
24 return mtime_of(path) >= 0;
25}
26
27int
28file_contains(const char *path, const char *needle)
29{
30 FILE *fp;
31 char buf[8192];
32 size_t n, needlen = strlen(needle), taillen = 0;
33 char tail[256];
34 int found = 0;
35
36 if (!needle[0])
37 return 1;
38 fp = fopen(path, "r");
39 if (!fp)
40 return 0;
41 tail[0] = '\0';
42 while (!found && (n = fread(buf, 1, sizeof buf - 1, fp)) > 0) {
43 char joined[8192 + 256];
44 buf[n] = '\0';
45 snprintf(joined, sizeof joined, "%s%s", tail, buf);
46 if (strstr(joined, needle))
47 found = 1;
48 taillen = strlen(buf);
49 if (taillen > needlen)
50 taillen = needlen - 1;
51 snprintf(tail, sizeof tail, "%s", buf + strlen(buf) - taillen);
52 }
53 fclose(fp);
54 return found;
55}
56
57/* recurse_dir lacks a userdata slot, use statics for one call */
58static const char *rg_suffix;
59static struct StrList *rg_out;
60
61static void
62rg_visit(const char *path)
63{
64 struct stat st;
65
66 if (lstat(path, &st) != 0)
67 return;
68 if (S_ISDIR(st.st_mode)) {
69 recurse_dir(path, rg_visit);
70 return;
71 }
72 if (S_ISREG(st.st_mode) && fnmatch(rg_suffix, base_of(path), 0) == 0)
73 sl_push(rg_out, path);
74}
75
76static void
77recurse_glob(const char *dir, const char *suffix, struct StrList *out)
78{
79 rg_suffix = suffix;
80 rg_out = out;
81 recurse_dir(dir, rg_visit);
82}
83
84/* double-star-slash recurses, anything else goes to glob(3) */
85void
86glob_expand(const char *pattern, struct StrList *out)
87{
88 const char *rec = strstr(pattern, "**/");
89 if (rec) {
90 char prefix[1024], suffix[1024];
91 size_t plen = (size_t)(rec - pattern);
92 if (plen >= sizeof prefix)
93 plen = sizeof prefix - 1;
94 memcpy(prefix, pattern, plen);
95 prefix[plen] = '\0';
96 if (plen > 0 && prefix[plen - 1] == '/')
97 prefix[plen - 1] = '\0';
98 strlcpy(suffix, rec + 3, sizeof suffix);
99 recurse_glob(prefix[0] ? prefix : ".", suffix, out);
100 } else {
101 glob_t g;
102 size_t i;
103 if (glob(pattern, 0, NULL, &g) == 0)
104 for (i = 0; i < g.gl_pathc; i++)
105 sl_push(out, g.gl_pathv[i]);
106 globfree(&g);
107 }
108}
109
110void
111glob_all(struct Rule *r, struct StrList *out)
112{
113 int i;
114 for (i = 0; i < r->globs.n; i++) {
115 char *pat = kv_expand(r->globs.v[i]);
116 glob_expand(pat, out);
117 free(pat);
118 }
119}
120
121int
122is_wildcard_pattern(const char *pattern)
123{
124 return strpbrk(pattern, "*?[") != NULL;
125}
126
127/* true if every glob on r is a literal path */
128int
129rule_is_literal(struct Rule *r)
130{
131 int i;
132 if (r->globs.n == 0)
133 return 0;
134 for (i = 0; i < r->globs.n; i++)
135 if (is_wildcard_pattern(r->globs.v[i]))
136 return 0;
137 return 1;
138}
139
140/* broad wildcard steps aside for literal paths so overrides need no (skip) */
141int
142path_claimed_elsewhere(const char *path, struct Rule *self)
143{
144 int i, j;
145 for (i = 0; i < nrules; i++) {
146 struct Rule *other = &rules[i];
147 if (other == self || !rule_is_literal(other))
148 continue;
149 for (j = 0; j < other->globs.n; j++) {
150 char *exp = kv_expand(other->globs.v[j]);
151 int hit = strcmp(exp, path) == 0;
152 free(exp);
153 if (hit)
154 return 1;
155 }
156 }
157 return 0;
158}
159
160void
161splitext(const char *base, char *stem, size_t stemsz, char *ext, size_t extsz)
162{
163 const char *dot = strrchr(base, '.');
164 if (dot && dot != base) {
165 size_t sl = (size_t)(dot - base);
166 if (sl >= stemsz)
167 sl = stemsz - 1;
168 memcpy(stem, base, sl);
169 stem[sl] = '\0';
170 strlcpy(ext, dot, extsz);
171 } else {
172 strlcpy(stem, base, stemsz);
173 ext[0] = '\0';
174 }
175}
176
177/* normalize so a gate keying off BASESTEM matches genconfig output */
178void
179to_ident(const char *s, char *out, size_t outsz)
180{
181 size_t i;
182 for (i = 0; s[i] && i + 1 < outsz; i++)
183 out[i] = (s[i] == '-' || s[i] == '.') ? '_' : s[i];
184 out[i] = '\0';
185}