diff options
Diffstat (limited to 'tools/perf/util/path.c')
-rw-r--r-- | tools/perf/util/path.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/tools/perf/util/path.c b/tools/perf/util/path.c new file mode 100644 index 000000000000..a501a40dd2cb --- /dev/null +++ b/tools/perf/util/path.c | |||
@@ -0,0 +1,353 @@ | |||
1 | /* | ||
2 | * I'm tired of doing "vsnprintf()" etc just to open a | ||
3 | * file, so here's a "return static buffer with printf" | ||
4 | * interface for paths. | ||
5 | * | ||
6 | * It's obviously not thread-safe. Sue me. But it's quite | ||
7 | * useful for doing things like | ||
8 | * | ||
9 | * f = open(mkpath("%s/%s.perf", base, name), O_RDONLY); | ||
10 | * | ||
11 | * which is what it's designed for. | ||
12 | */ | ||
13 | #include "cache.h" | ||
14 | |||
15 | static char bad_path[] = "/bad-path/"; | ||
16 | /* | ||
17 | * Two hacks: | ||
18 | */ | ||
19 | |||
20 | static char *get_perf_dir(void) | ||
21 | { | ||
22 | return "."; | ||
23 | } | ||
24 | |||
25 | size_t strlcpy(char *dest, const char *src, size_t size) | ||
26 | { | ||
27 | size_t ret = strlen(src); | ||
28 | |||
29 | if (size) { | ||
30 | size_t len = (ret >= size) ? size - 1 : ret; | ||
31 | memcpy(dest, src, len); | ||
32 | dest[len] = '\0'; | ||
33 | } | ||
34 | return ret; | ||
35 | } | ||
36 | |||
37 | |||
38 | static char *get_pathname(void) | ||
39 | { | ||
40 | static char pathname_array[4][PATH_MAX]; | ||
41 | static int index; | ||
42 | return pathname_array[3 & ++index]; | ||
43 | } | ||
44 | |||
45 | static char *cleanup_path(char *path) | ||
46 | { | ||
47 | /* Clean it up */ | ||
48 | if (!memcmp(path, "./", 2)) { | ||
49 | path += 2; | ||
50 | while (*path == '/') | ||
51 | path++; | ||
52 | } | ||
53 | return path; | ||
54 | } | ||
55 | |||
56 | char *mksnpath(char *buf, size_t n, const char *fmt, ...) | ||
57 | { | ||
58 | va_list args; | ||
59 | unsigned len; | ||
60 | |||
61 | va_start(args, fmt); | ||
62 | len = vsnprintf(buf, n, fmt, args); | ||
63 | va_end(args); | ||
64 | if (len >= n) { | ||
65 | strlcpy(buf, bad_path, n); | ||
66 | return buf; | ||
67 | } | ||
68 | return cleanup_path(buf); | ||
69 | } | ||
70 | |||
71 | static char *perf_vsnpath(char *buf, size_t n, const char *fmt, va_list args) | ||
72 | { | ||
73 | const char *perf_dir = get_perf_dir(); | ||
74 | size_t len; | ||
75 | |||
76 | len = strlen(perf_dir); | ||
77 | if (n < len + 1) | ||
78 | goto bad; | ||
79 | memcpy(buf, perf_dir, len); | ||
80 | if (len && !is_dir_sep(perf_dir[len-1])) | ||
81 | buf[len++] = '/'; | ||
82 | len += vsnprintf(buf + len, n - len, fmt, args); | ||
83 | if (len >= n) | ||
84 | goto bad; | ||
85 | return cleanup_path(buf); | ||
86 | bad: | ||
87 | strlcpy(buf, bad_path, n); | ||
88 | return buf; | ||
89 | } | ||
90 | |||
91 | char *perf_snpath(char *buf, size_t n, const char *fmt, ...) | ||
92 | { | ||
93 | va_list args; | ||
94 | va_start(args, fmt); | ||
95 | (void)perf_vsnpath(buf, n, fmt, args); | ||
96 | va_end(args); | ||
97 | return buf; | ||
98 | } | ||
99 | |||
100 | char *perf_pathdup(const char *fmt, ...) | ||
101 | { | ||
102 | char path[PATH_MAX]; | ||
103 | va_list args; | ||
104 | va_start(args, fmt); | ||
105 | (void)perf_vsnpath(path, sizeof(path), fmt, args); | ||
106 | va_end(args); | ||
107 | return xstrdup(path); | ||
108 | } | ||
109 | |||
110 | char *mkpath(const char *fmt, ...) | ||
111 | { | ||
112 | va_list args; | ||
113 | unsigned len; | ||
114 | char *pathname = get_pathname(); | ||
115 | |||
116 | va_start(args, fmt); | ||
117 | len = vsnprintf(pathname, PATH_MAX, fmt, args); | ||
118 | va_end(args); | ||
119 | if (len >= PATH_MAX) | ||
120 | return bad_path; | ||
121 | return cleanup_path(pathname); | ||
122 | } | ||
123 | |||
124 | char *perf_path(const char *fmt, ...) | ||
125 | { | ||
126 | const char *perf_dir = get_perf_dir(); | ||
127 | char *pathname = get_pathname(); | ||
128 | va_list args; | ||
129 | unsigned len; | ||
130 | |||
131 | len = strlen(perf_dir); | ||
132 | if (len > PATH_MAX-100) | ||
133 | return bad_path; | ||
134 | memcpy(pathname, perf_dir, len); | ||
135 | if (len && perf_dir[len-1] != '/') | ||
136 | pathname[len++] = '/'; | ||
137 | va_start(args, fmt); | ||
138 | len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); | ||
139 | va_end(args); | ||
140 | if (len >= PATH_MAX) | ||
141 | return bad_path; | ||
142 | return cleanup_path(pathname); | ||
143 | } | ||
144 | |||
145 | |||
146 | /* perf_mkstemp() - create tmp file honoring TMPDIR variable */ | ||
147 | int perf_mkstemp(char *path, size_t len, const char *template) | ||
148 | { | ||
149 | const char *tmp; | ||
150 | size_t n; | ||
151 | |||
152 | tmp = getenv("TMPDIR"); | ||
153 | if (!tmp) | ||
154 | tmp = "/tmp"; | ||
155 | n = snprintf(path, len, "%s/%s", tmp, template); | ||
156 | if (len <= n) { | ||
157 | errno = ENAMETOOLONG; | ||
158 | return -1; | ||
159 | } | ||
160 | return mkstemp(path); | ||
161 | } | ||
162 | |||
163 | |||
164 | const char *make_relative_path(const char *abs, const char *base) | ||
165 | { | ||
166 | static char buf[PATH_MAX + 1]; | ||
167 | int baselen; | ||
168 | if (!base) | ||
169 | return abs; | ||
170 | baselen = strlen(base); | ||
171 | if (prefixcmp(abs, base)) | ||
172 | return abs; | ||
173 | if (abs[baselen] == '/') | ||
174 | baselen++; | ||
175 | else if (base[baselen - 1] != '/') | ||
176 | return abs; | ||
177 | strcpy(buf, abs + baselen); | ||
178 | return buf; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * It is okay if dst == src, but they should not overlap otherwise. | ||
183 | * | ||
184 | * Performs the following normalizations on src, storing the result in dst: | ||
185 | * - Ensures that components are separated by '/' (Windows only) | ||
186 | * - Squashes sequences of '/'. | ||
187 | * - Removes "." components. | ||
188 | * - Removes ".." components, and the components the precede them. | ||
189 | * Returns failure (non-zero) if a ".." component appears as first path | ||
190 | * component anytime during the normalization. Otherwise, returns success (0). | ||
191 | * | ||
192 | * Note that this function is purely textual. It does not follow symlinks, | ||
193 | * verify the existence of the path, or make any system calls. | ||
194 | */ | ||
195 | int normalize_path_copy(char *dst, const char *src) | ||
196 | { | ||
197 | char *dst0; | ||
198 | |||
199 | if (has_dos_drive_prefix(src)) { | ||
200 | *dst++ = *src++; | ||
201 | *dst++ = *src++; | ||
202 | } | ||
203 | dst0 = dst; | ||
204 | |||
205 | if (is_dir_sep(*src)) { | ||
206 | *dst++ = '/'; | ||
207 | while (is_dir_sep(*src)) | ||
208 | src++; | ||
209 | } | ||
210 | |||
211 | for (;;) { | ||
212 | char c = *src; | ||
213 | |||
214 | /* | ||
215 | * A path component that begins with . could be | ||
216 | * special: | ||
217 | * (1) "." and ends -- ignore and terminate. | ||
218 | * (2) "./" -- ignore them, eat slash and continue. | ||
219 | * (3) ".." and ends -- strip one and terminate. | ||
220 | * (4) "../" -- strip one, eat slash and continue. | ||
221 | */ | ||
222 | if (c == '.') { | ||
223 | if (!src[1]) { | ||
224 | /* (1) */ | ||
225 | src++; | ||
226 | } else if (is_dir_sep(src[1])) { | ||
227 | /* (2) */ | ||
228 | src += 2; | ||
229 | while (is_dir_sep(*src)) | ||
230 | src++; | ||
231 | continue; | ||
232 | } else if (src[1] == '.') { | ||
233 | if (!src[2]) { | ||
234 | /* (3) */ | ||
235 | src += 2; | ||
236 | goto up_one; | ||
237 | } else if (is_dir_sep(src[2])) { | ||
238 | /* (4) */ | ||
239 | src += 3; | ||
240 | while (is_dir_sep(*src)) | ||
241 | src++; | ||
242 | goto up_one; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* copy up to the next '/', and eat all '/' */ | ||
248 | while ((c = *src++) != '\0' && !is_dir_sep(c)) | ||
249 | *dst++ = c; | ||
250 | if (is_dir_sep(c)) { | ||
251 | *dst++ = '/'; | ||
252 | while (is_dir_sep(c)) | ||
253 | c = *src++; | ||
254 | src--; | ||
255 | } else if (!c) | ||
256 | break; | ||
257 | continue; | ||
258 | |||
259 | up_one: | ||
260 | /* | ||
261 | * dst0..dst is prefix portion, and dst[-1] is '/'; | ||
262 | * go up one level. | ||
263 | */ | ||
264 | dst--; /* go to trailing '/' */ | ||
265 | if (dst <= dst0) | ||
266 | return -1; | ||
267 | /* Windows: dst[-1] cannot be backslash anymore */ | ||
268 | while (dst0 < dst && dst[-1] != '/') | ||
269 | dst--; | ||
270 | } | ||
271 | *dst = '\0'; | ||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * path = Canonical absolute path | ||
277 | * prefix_list = Colon-separated list of absolute paths | ||
278 | * | ||
279 | * Determines, for each path in prefix_list, whether the "prefix" really | ||
280 | * is an ancestor directory of path. Returns the length of the longest | ||
281 | * ancestor directory, excluding any trailing slashes, or -1 if no prefix | ||
282 | * is an ancestor. (Note that this means 0 is returned if prefix_list is | ||
283 | * "/".) "/foo" is not considered an ancestor of "/foobar". Directories | ||
284 | * are not considered to be their own ancestors. path must be in a | ||
285 | * canonical form: empty components, or "." or ".." components are not | ||
286 | * allowed. prefix_list may be null, which is like "". | ||
287 | */ | ||
288 | int longest_ancestor_length(const char *path, const char *prefix_list) | ||
289 | { | ||
290 | char buf[PATH_MAX+1]; | ||
291 | const char *ceil, *colon; | ||
292 | int len, max_len = -1; | ||
293 | |||
294 | if (prefix_list == NULL || !strcmp(path, "/")) | ||
295 | return -1; | ||
296 | |||
297 | for (colon = ceil = prefix_list; *colon; ceil = colon+1) { | ||
298 | for (colon = ceil; *colon && *colon != PATH_SEP; colon++); | ||
299 | len = colon - ceil; | ||
300 | if (len == 0 || len > PATH_MAX || !is_absolute_path(ceil)) | ||
301 | continue; | ||
302 | strlcpy(buf, ceil, len+1); | ||
303 | if (normalize_path_copy(buf, buf) < 0) | ||
304 | continue; | ||
305 | len = strlen(buf); | ||
306 | if (len > 0 && buf[len-1] == '/') | ||
307 | buf[--len] = '\0'; | ||
308 | |||
309 | if (!strncmp(path, buf, len) && | ||
310 | path[len] == '/' && | ||
311 | len > max_len) { | ||
312 | max_len = len; | ||
313 | } | ||
314 | } | ||
315 | |||
316 | return max_len; | ||
317 | } | ||
318 | |||
319 | /* strip arbitrary amount of directory separators at end of path */ | ||
320 | static inline int chomp_trailing_dir_sep(const char *path, int len) | ||
321 | { | ||
322 | while (len && is_dir_sep(path[len - 1])) | ||
323 | len--; | ||
324 | return len; | ||
325 | } | ||
326 | |||
327 | /* | ||
328 | * If path ends with suffix (complete path components), returns the | ||
329 | * part before suffix (sans trailing directory separators). | ||
330 | * Otherwise returns NULL. | ||
331 | */ | ||
332 | char *strip_path_suffix(const char *path, const char *suffix) | ||
333 | { | ||
334 | int path_len = strlen(path), suffix_len = strlen(suffix); | ||
335 | |||
336 | while (suffix_len) { | ||
337 | if (!path_len) | ||
338 | return NULL; | ||
339 | |||
340 | if (is_dir_sep(path[path_len - 1])) { | ||
341 | if (!is_dir_sep(suffix[suffix_len - 1])) | ||
342 | return NULL; | ||
343 | path_len = chomp_trailing_dir_sep(path, path_len); | ||
344 | suffix_len = chomp_trailing_dir_sep(suffix, suffix_len); | ||
345 | } | ||
346 | else if (path[--path_len] != suffix[--suffix_len]) | ||
347 | return NULL; | ||
348 | } | ||
349 | |||
350 | if (path_len && !is_dir_sep(path[path_len - 1])) | ||
351 | return NULL; | ||
352 | return xstrndup(path, chomp_trailing_dir_sep(path, path_len)); | ||
353 | } | ||