in Sharpmake/PathUtil.cs [199:295]
public static unsafe string SimplifyPathImpl(string path)
{
// First construct a path helper to help with the conversion
char* arrayPtr = stackalloc char[path.Length + 1];
int consecutiveDotsCounter = 0;
bool pathSeparatorWritePending = IsPathSeparator(path[^1]); // Explicitly handle path with a trailing path separator (we want to keep it)
int writePosition = path.Length;
int dotDotCounter = 0;
// Start by the end of the path to easily handle '..' case
for (int index = path.Length - 1; index >= 0; --index)
{
char currentChar = path[index];
if (IsPathSeparator(currentChar))
{
pathSeparatorWritePending = pathSeparatorWritePending
|| (consecutiveDotsCounter == -1 && dotDotCounter == 0); // We want to consider this path separator only if we are not on a '.' or '..'
HandleDotDotCounter(ref consecutiveDotsCounter, ref dotDotCounter, path);
}
else
{
// Count consecutive dots (if there is only dots in the name)
if (currentChar == '.' && consecutiveDotsCounter != -1)
{
++consecutiveDotsCounter;
continue;
}
if (dotDotCounter == 0)
{
// Write pending path separator
if (pathSeparatorWritePending)
{
arrayPtr[writePosition--] = Path.DirectorySeparatorChar;
pathSeparatorWritePending = false;
}
// Write held back '.'
for (int i = 0; i < consecutiveDotsCounter; ++i)
{
arrayPtr[writePosition--] = '.';
}
arrayPtr[writePosition--] = currentChar;
}
// We encountered something else than '.', now we don't care about them until next path separator
consecutiveDotsCounter = -1;
}
}
// Handle additional '..' that was not taken into account nor consummed
HandleDotDotCounter(ref consecutiveDotsCounter, ref dotDotCounter, path);
for (int i = 0; i < dotDotCounter; ++i)
{
if (pathSeparatorWritePending)
arrayPtr[writePosition--] = Path.DirectorySeparatorChar;
arrayPtr[writePosition--] = '.';
arrayPtr[writePosition--] = '.';
pathSeparatorWritePending = true;
}
// Handle rooted path on Unix platforms
if (path[0] == '/')
arrayPtr[writePosition--] = '/';
return new string(arrayPtr, writePosition + 1, path.Length - writePosition);
static bool IsPathSeparator(char c) => c == Path.DirectorySeparatorChar || c == OtherSeparator;
static void HandleDotDotCounter(ref int consecutiveDotsCounter, ref int dotDotCounter, string path)
{
switch (consecutiveDotsCounter)
{
case -1:
// We encountered a real folder name (not made exclusively of dots), and it have been skipped if dotDotCounter was not 0, so we decrement it
if (dotDotCounter > 0)
--dotDotCounter;
break;
case 0:
break;
case 1:
// skip: "./"
break;
case 2:
++dotDotCounter;
break;
default:
throw new ArgumentException($"Invalid path format: '{path}' (folder made of three or more consecutive dots detected)");
}
// We are on a path separator, consecutive dots counter must be reset
consecutiveDotsCounter = 0;
}
}