PageRenderTime 15ms CodeModel.GetById 0ms app.highlight 13ms RepoModel.GetById 0ms app.codeStats 0ms

/tools/perf/util/path.c

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