xref: /plan9/sys/src/libc/port/cleanname.c (revision 853458f38e7eb3a48cfa3a36aefdb799375e398a)
1 #include <u.h>
2 #include <libc.h>
3 
4 /*
5  * In place, rewrite name to compress multiple /, eliminate ., and process ..
6  */
7 #define SEP(x)	((x)=='/' || (x) == 0)
8 char*
cleanname(char * name)9 cleanname(char *name)
10 {
11 	char *s;	/* source of copy */
12 	char *d;	/* destination of copy */
13 	char *d0;	/* start of path afer the root name */
14 	Rune r;
15 	int rooted;
16 
17 	if(name[0] == 0)
18 		return strcpy(name, ".");
19 	rooted = 0;
20 	d0 = name;
21 	if(d0[0] == '#'){
22 		if(d0[1] == 0)
23 			return d0;
24 		d0  += 1 + chartorune(&r, d0+1); /* ignore slash: #/ */
25 		while(!SEP(*d0))
26 			d0 += chartorune(&r, d0);
27 		if(d0 == 0)
28 			return name;
29 		d0++;	/* keep / after #<name> */
30 		rooted = 1;
31 	}else if(d0[0] == '/'){
32 		rooted = 1;
33 		d0++;
34 	}
35 
36 	s = d0;
37 	if(rooted){
38 		/* skip extra '/' at root name */
39 		for(; *s == '/'; s++)
40 			;
41 	}
42 	/* remove dup slashes */
43 	for(d = d0; *s != 0; s++){
44 		*d++ = *s;
45 		if(*s == '/')
46 			while(s[1] == '/')
47 				s++;
48 	}
49 	*d = 0;
50 
51 	d = d0;
52 	s = d0;
53 	while(*s != 0){
54 		if(s[0] == '.' && SEP(s[1])){
55 			if(s[1] == 0)
56 				break;
57 			s+= 2;
58 			continue;
59 		}
60 		if(s[0] == '.' && s[1] == '.' && SEP(s[2])){
61 			if(d == d0){
62 				if(rooted){
63 					/* /../x -> /x */
64 					if(s[2] == 0)
65 						break;
66 					s += 3;
67 					continue;
68 				}else{
69 					/* ../x -> ../x; and never collect ../ */
70 					d0 += 3;
71 				}
72 			}
73 			if(d > d0){
74 				/* a/../x -> x */
75 				assert(d-2 >= d0 && d[-1] == '/');
76 				for(d -= 2; d > d0 && d[-1] != '/'; d--)
77 						;
78 				if(s[2] == 0)
79 					break;
80 				s += 3;
81 				continue;
82 			}
83 		}
84 		while(!SEP(*s))
85 			*d++ = *s++;
86 		if(*s == 0)
87 			break;
88 
89 		*d++ = *s++;
90 	}
91 	*d = 0;
92 	if(d-1 > name && d[-1] == '/')	/* thanks to #/ */
93 		*--d = 0;
94 	if(name[0] == 0)
95 		strcpy(name, ".");
96 	return name;
97 }
98