function collapsePath(path: UTF8String): UTF8String;
const
delimiters = ['
/', '
\'];
var
afterLastDelim: integer;
writePos: integer;
readPos: integer;
skip: integer;
state: (sNormal, sAfterDelim, sAfterDot, sAfter2Dots);
begin
state := sAfterDelim;
writePos := length(path);
afterLastDelim := writePos;
skip := 0;
for readPos := length(path)
downto 1
do
begin
if path[readPos]
in delimiters
then
begin
if (state = sNormal)
or (state = sAfterDelim)
then
begin
if skip = 0
then
begin
path[writePos] := path[readPos];
dec(writePos);
afterLastDelim := writePos;
end else
begin
writePos := afterLastDelim;
dec(skip);
end;
end else // not normal
begin
writePos := afterLastDelim;
if state = sAfter2Dots
then inc(skip);
end;
state := sAfterDelim;
end else // no delimiter
begin
path[writePos] := path[readPos];
dec(writePos);
if path[readPos] = '
.'
then
begin
if state = sAfterDelim
then state := sAfterDot
else if state = sAfterDot
then state := sAfter2Dots
else state := sNormal;
// third dot
end else state := sNormal;
end;
end;
if (state = sNormal)
or (state = sAfterDelim)
then
begin
if skip > 0
then
begin
writePos := afterLastDelim;
dec(skip);
end;
end else writePos := afterLastDelim;
if skip > 0
then
begin
path[writePos-1] := '
.';
path[writePos] := '
.';
dec(writePos,2);
dec(skip);
end;
while skip > 0
do
begin
path[writePos-2] := '
.';
path[writePos-1] := '
.';
path[writePos] := '
\';
dec(writePos,3);
dec(skip);
end;
result := copy(path, writePos+1, length(path));
end;