xref: /inferno-os/libkern/cleanname.c (revision d3641b487cf5cdc46e9b537d30eb37736e5c7b1a)
1 #include "lib9.h"
2 
3 /*
4  * In place, rewrite name to compress multiple /, eliminate ., and process ..
5  */
6 #define SEP(x)	((x)=='/' || (x) == 0)
7 char*
8 cleanname(char *name)
9 {
10 	char *p, *q, *dotdot;
11 	int rooted, erasedprefix;
12 
13 	rooted = name[0] == '/';
14 	erasedprefix = 0;
15 
16 	/*
17 	 * invariants:
18 	 *	p points at beginning of path element we're considering.
19 	 *	q points just past the last path element we wrote (no slash).
20 	 *	dotdot points just past the point where .. cannot backtrack
21 	 *		any further (no slash).
22 	 */
23 	p = q = dotdot = name+rooted;
24 	while(*p) {
25 		if(p[0] == '/')	/* null element */
26 			p++;
27 		else if(p[0] == '.' && SEP(p[1])) {
28 			if(p == name)
29 				erasedprefix = 1;
30 			p += 1;	/* don't count the separator in case it is nul */
31 		} else if(p[0] == '.' && p[1] == '.' && SEP(p[2])) {
32 			p += 2;
33 			if(q > dotdot) {	/* can backtrack */
34 				while(--q > dotdot && *q != '/')
35 					;
36 			} else if(!rooted) {	/* /.. is / but ./../ is .. */
37 				if(q != name)
38 					*q++ = '/';
39 				*q++ = '.';
40 				*q++ = '.';
41 				dotdot = q;
42 			}
43 			if(q == name)
44 				erasedprefix = 1;	/* erased entire path via dotdot */
45 		} else {	/* real path element */
46 			if(q != name+rooted)
47 				*q++ = '/';
48 			while((*q = *p) != '/' && *q != 0)
49 				p++, q++;
50 		}
51 	}
52 	if(q == name)	/* empty string is really ``.'' */
53 		*q++ = '.';
54 	*q = '\0';
55 	if(erasedprefix && name[0] == '#'){
56 		/* this was not a #x device path originally - make it not one now */
57 		memmove(name+2, name, strlen(name)+1);
58 		name[0] = '.';
59 		name[1] = '/';
60 	}
61 	return name;
62 }
63