private static string Reduce (string path, bool compact_escaped)
{
// quick out, allocation-free, for a common case
if (path == "/")
return path;
StringBuilder res = new StringBuilder();
if (compact_escaped) {
// replace '\', %5C ('\') and %2f ('/') into '/'
// other escaped values seems to survive this step
for (int i=0; i < path.Length; i++) {
char c = path [i];
switch (c) {
case '\\':
res.Append ('/');
break;
case '%':
if (i < path.Length - 2) {
char c1 = path [i + 1];
char c2 = Char.ToUpper (path [i + 2]);
if (((c1 == '2') && (c2 == 'F')) || ((c1 == '5') && (c2 == 'C'))) {
res.Append ('/');
i += 2;
} else {
res.Append (c);
}
} else {
res.Append (c);
}
break;
default:
res.Append (c);
break;
}
}
path = res.ToString ();
} else {
path = path.Replace ('\\', '/');
}
ArrayList result = new ArrayList ();
bool begin = true;
for (int startpos = 0; startpos < path.Length; ) {
int endpos = path.IndexOf('/', startpos);
if (endpos == -1) endpos = path.Length;
string current = path.Substring (startpos, endpos-startpos);
startpos = endpos + 1;
if ((begin && current.Length == 0) || current == "." )
continue;
begin = false;
if (current == "..") {
int resultCount = result.Count;
#if NET_2_0
// in 2.0 profile, skip leading ".." parts
if (resultCount == 0) {
continue;
}
result.RemoveAt (resultCount - 1);
continue;
#else
// in 1.x profile, retain leading ".." parts, and only reduce
// URI is previous part is not ".."
if (resultCount > 0) {
if ((string) result[resultCount - 1] != "..") {
result.RemoveAt (resultCount - 1);
continue;
}
}
#endif
}
result.Add (current);
}
if (result.Count == 0)
return "/";
res.Length = 0;
if (path [0] == '/')
res.Append ('/');
bool first = true;
foreach (string part in result) {
if (first) {
first = false;
} else {
res.Append ('/');
}
res.Append(part);
}
if (path.EndsWith ("/"))
res.Append ('/');
return res.ToString();
}