summaryrefslogtreecommitdiff
path: root/util/cleanname.c
blob: e8a8ed866435b1cece83473e19a7a3bfd20dade9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
 * In place, rewrite name to compress multiple /, eliminate ., and process ..
 */

#define SEP(x)	((x)=='/' || (x) == 0)

char *
cleanname(char *name)
{
	char *p, *q, *dotdot;
	int rooted;

	rooted = name[0] == '/';

	/*
	 * invariants:
	 *	p points at beginning of path element we're considering.
	 *	q points just past the last path element we wrote (no slash).
	 *	dotdot points just past the point where .. cannot backtrack
	 *		any further (no slash).
	 */

	p = q = dotdot = name + rooted;

	while (*p)
	{
		if (p[0] == '/')	/* null element */
			p++;

		else if (p[0] == '.' && SEP(p[1]))
			p += 1;	/* don't count the separator in case it is nul */

		else if (p[0] == '.' && p[1] == '.' && SEP(p[2]))
		{
			p += 2;
			if (q > dotdot) {	/* can backtrack */
				while (--q > dotdot && *q != '/')
					;
			} else if (!rooted) {	/* /.. is / but ./../ is .. */
				if (q != name)
					*q++ = '/';
				*q++ = '.';
				*q++ = '.';
				dotdot = q;
			}
		}

		else {	/* real path element */
			if (q != name + rooted)
				*q++ = '/';
			while ((*q = *p) != '/' && *q != 0)
				p++, q++;
		}
	}

	if (q == name)	/* empty string is really ``.'' */
		*q++ = '.';

	*q = '\0';

	return name;
}