123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 1997-2000 by the Free Pascal development team
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {****************************************************************************
- A platform independent FExpand implementation
- ****************************************************************************}
- {$IFDEF FPC_FEXPAND_VOLUMES}
- {$IFNDEF FPC_FEXPAND_DRIVES}
- (* Volumes are just a special case of drives. *)
- {$DEFINE FPC_FEXPAND_DRIVES}
- {$ENDIF FPC_FEXPAND_DRIVES}
- {$ENDIF FPC_FEXPAND_VOLUMES}
- {$IFDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$IFNDEF FPC_FEXPAND_DRIVES}
- (* If DirectorySeparator at the beginning marks a relative path, *)
- (* an absolute path must always begin with a drive or volume. *)
- {$DEFINE FPC_FEXPAND_DRIVES}
- {$ENDIF FPC_FEXPAND_DRIVES}
- {$IFNDEF FPC_FEXPAND_MULTIPLE_UPDIR}
- (* Traversing multiple levels at once explicitely allowed. *)
- {$DEFINE FPC_FEXPAND_MULTIPLE_UPDIR}
- {$ENDIF FPC_FEXPAND_MULTIPLE_UPDIR}
- (* Helper define used to support common features of FPC_FEXPAND_DIRSEP_IS_* *)
- {$DEFINE FPC_FEXPAND_UPDIR_HELPER}
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$IFDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$IFNDEF FPC_FEXPAND_DRIVES}
- (* If DirectorySeparator at the beginning marks a relative path, *)
- (* an absolute path must always begin with a drive or volume. *)
- {$DEFINE FPC_FEXPAND_DRIVES}
- {$ENDIF FPC_FEXPAND_DRIVES}
- {$IFNDEF FPC_FEXPAND_MULTIPLE_UPDIR}
- (* Traversing multiple levels at once explicitely allowed. *)
- {$DEFINE FPC_FEXPAND_MULTIPLE_UPDIR}
- {$ENDIF FPC_FEXPAND_MULTIPLE_UPDIR}
- (* Helper define used to support common features of FPC_FEXPAND_DIRSEP_IS_* *)
- {$DEFINE FPC_FEXPAND_UPDIR_HELPER}
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- { this code is used both in sysutils and in the dos unit, and the dos
- unit does not have a charinset routine }
- {$if not defined(FPC_FEXPAND_SYSUTILS) and not defined(FPC_FEXPAND_HAS_CHARINSET)}
- {$define FPC_FEXPAND_HAS_CHARINSET}
- type
- TFExpandSysCharSet = set of ansichar;
- Function CharInSet(Ch:AnsiChar;Const CSet : TFExpandSysCharSet) : Boolean; inline;
- begin
- CharInSet:=ch in CSet;
- end;
- Function CharInSet(Ch:WideChar;Const CSet : TFExpandSysCharSet) : Boolean;
- begin
- CharInSet:=(Ch<=#$FF) and (ansichar(byte(ch)) in CSet);
- end;
- {$endif}
- procedure GetDirIO (DriveNr: byte; var Dir: {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}RawByteString{$else}PathStr{$endif});
- (* GetDirIO is supposed to return the root of the given drive *)
- (* in case of an error for compatibility of FExpand with TP/BP. *)
- var
- OldInOutRes: word;
- begin
- OldInOutRes := InOutRes;
- InOutRes := 0;
- GetDir (DriveNr, Dir);
- {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
- { set the same codepage as used for the strings in fexpand itself }
- SetCodePage(Dir,DefaultFileSystemCodePage);
- {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- InOutRes := OldInOutRes;
- end;
- {$IFDEF FPC_FEXPAND_VOLUMES}
- {$IFNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
- procedure GetDirIO (const VolumeName: OpenString; var Dir: {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}RawByteString{$else}PathStr{$endif});
- var
- OldInOutRes: word;
- begin
- OldInOutRes := InOutRes;
- InOutRes := 0;
- GetDir (VolumeName, Dir);
- InOutRes := OldInOutRes;
- end;
- {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
- {$ENDIF FPC_FEXPAND_VOLUMES}
- function FExpand (const Path: PathStr): PathStr;
- (* LFNSupport boolean constant, variable or function must be declared for all
- the platforms, at least locally in the Dos unit implementation part.
- In addition, FPC_FEXPAND_UNC, FPC_FEXPAND_DRIVES, FPC_FEXPAND_GETENV_PCHAR,
- FPC_FEXPAND_TILDE, FPC_FEXPAND_VOLUMES, FPC_FEXPAND_NO_DEFAULT_PATHS,
- FPC_FEXPAND_DRIVESEP_IS_ROOT, FPC_FEXPAND_NO_CURDIR,
- FPC_FEXPAND_NO_DOTS_UPDIR, FPC_FEXPAND_DIRSEP_IS_UPDIR,
- FPC_FEXPAND_DIRSEP_IS_CURDIR and FPC_FEXPAND_MULTIPLE_UPDIR conditionals
- might be defined to specify FExpand behaviour - see end of this file for
- individual descriptions. Finally, FPC_FEXPAND_SYSUTILS allows to reuse
- the same implementation for SysUtils.ExpandFileName.
- *)
- {$IFDEF FPC_FEXPAND_DRIVES}
- var
- PathStart: longint;
- {$ELSE FPC_FEXPAND_DRIVES}
- const
- PathStart = 1;
- {$ENDIF FPC_FEXPAND_DRIVES}
- {$IFDEF FPC_FEXPAND_UNC}
- var
- RootNotNeeded: boolean;
- {$ELSE FPC_FEXPAND_UNC}
- const
- RootNotNeeded = false;
- {$ENDIF FPC_FEXPAND_UNC}
- var S, Pa, Dirs, TmpS: {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}RawByteString{$else}PathStr{$endif};
- I, J: longint;
- begin
- {$IFDEF FPC_FEXPAND_UNC}
- RootNotNeeded := false;
- {$ENDIF FPC_FEXPAND_UNC}
- (* First convert the path to uppercase if appropriate for current platform. *)
- {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
- { for sysutils/rawbytestring, process everything in
- DefaultFileSystemCodePage to prevent risking data loss that may be
- relevant when the file name is used }
- if FileNameCasePreserving then
- Pa := ToSingleByteFileSystemEncodedFileName (Path)
- else
- Pa := UpCase (ToSingleByteFileSystemEncodedFileName (Path));
- {$ELSE FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- if FileNameCasePreserving then
- Pa := Path
- else
- Pa := UpCase (Path);
- {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- { already done before this routine is called from sysutils }
- {$IFNDEF FPC_FEXPAND_SYSUTILS}
- (* Allow both '/' and '\' as directory separators *)
- (* by converting all to the native one. *)
- {$warnings off}
- for I := 1 to Length (Pa) do
- if CharInSet(Pa [I], AllowDirectorySeparators) then
- Pa [I] := DirectorySeparator;
- {$warnings on}
- {$ENDIF not FPC_FEXPAND_SYSUTILS}
- (* PathStart is amount of characters to strip to get beginning *)
- (* of path without volume/drive specification. *)
- {$IFDEF FPC_FEXPAND_DRIVES}
- {$IFDEF FPC_FEXPAND_VOLUMES}
- {$IFDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- PathStart := Pos (DriveSeparator, Pa);
- {$ELSE FPC_FEXPAND_DRIVESEP_IS_ROOT}
- PathStart := Succ (Pos (DriveSeparator, Pa));
- {$ENDIF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- {$ELSE FPC_FEXPAND_VOLUMES}
- PathStart := 3;
- {$ENDIF FPC_FEXPAND_VOLUMES}
- {$ENDIF FPC_FEXPAND_DRIVES}
- (* Expand tilde to home directory if appropriate. *)
- {$IFDEF FPC_FEXPAND_TILDE}
- {Replace ~/ with $HOME/}
- if (Length (Pa) >= 1) and (Pa [1] = '~') and
- ((Pa [2] = DirectorySeparator) or (Length (Pa) = 1)) then
- begin
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- {$IFDEF SYSUTILSUNICODE}
- S := PathStr(GetEnvironmentVariable ('HOME'));
- {$ELSE SYSUTILSUNICODE}
- S := ToSingleByteFileSystemEncodedFileName(GetEnvironmentVariable ('HOME'));
- {$ENDIF SYSUTILSUNICODE}
- {$ELSE FPC_FEXPAND_SYSUTILS}
- {$IFDEF FPC_FEXPAND_GETENV_PCHAR}
- S := StrPas (GetEnv ('HOME'));
- {$ELSE FPC_FEXPAND_GETENV_PCHAR}
- S := GetEnv ('HOME');
- {$ENDIF FPC_FEXPAND_GETENV_PCHAR}
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- if (S = '') or (Length (S) = 1) and (Length (Pa) > 1)
- and (S [1] = DirectorySeparator) then
- Delete (Pa, 1, 1)
- else
- if S [Length (S)] = DirectorySeparator then
- Pa := S + Copy (Pa, 3, Length (Pa) - 2)
- else
- Pa := S + Copy (Pa, 2, Pred (Length (Pa)));
- end;
- {$ENDIF FPC_FEXPAND_TILDE}
- (* Do we have a drive/volume specification? *)
- {$IFDEF FPC_FEXPAND_VOLUMES}
- if PathStart > 1 then
- {$ELSE FPC_FEXPAND_VOLUMES}
- if (Length (Pa) > 1) and CharInSet(Pa [1], ['A'..'Z', 'a'..'z']) and
- (Pa [2] = DriveSeparator) and (DriveSeparator <> DirectorySeparator) then
- {$ENDIF FPC_FEXPAND_VOLUMES}
- begin
- (* We need to know current directory on given *)
- (* volume/drive _if_ such a thing is defined. *)
- {$IFDEF FPC_FEXPAND_DRIVES}
- {$IFNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
- {$IFDEF FPC_FEXPAND_VOLUMES}
- GetDirIO (Copy (Pa, 1, PathStart - 2), S);
- {$ELSE FPC_FEXPAND_VOLUMES}
- { Always uppercase driveletter }
- if CharInSet(Pa [1], ['a'..'z']) then
- Pa [1] := Chr (Ord (Pa [1]) and not ($20));
- GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S);
- {$ENDIF FPC_FEXPAND_VOLUMES}
- (* Do we have more than just drive/volume specification? *)
- if Length (Pa) = Pred (PathStart) then
- (* If not, just use the current directory for that drive/volume. *)
- Pa := S
- else
- (* If yes, find out whether the following path is relative or absolute. *)
- if Pa [PathStart] <> DirectorySeparator then
- {$IFDEF FPC_FEXPAND_VOLUMES}
- if Copy (Pa, 1, PathStart - 2) = Copy (S, 1, PathStart - 2)
- then
- {$ELSE FPC_FEXPAND_VOLUMES}
- if UpCase(Pa [1]) = UpCase(S [1]) then
- {$ENDIF FPC_FEXPAND_VOLUMES}
- begin
- { remove ending slash if it already exists }
- if S [Length (S)] = DirectorySeparator then
- SetLength(S,Length(S)-1);
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- { not "Pa := S + DirectorySeparator + ..." because
- that will convert the result to
- DefaultSystemCodePage in case of RawByteString due
- to DirectorySeparator being an ansichar }
- TmpS := S;
- SetLength(TmpS, Length(TmpS) + 1);
- TmpS[Length(TmpS)] := DirectorySeparator;
- Pa := TmpS +
- Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
- {$ELSE FPC_FEXPAND_SYSUTILS}
- Pa := S + DirectorySeparator +
- Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- end
- else
- begin
- TmpS := DriveSeparator + DirectorySeparator;
- {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
- SetCodePage(TmpS, DefaultFileSystemCodePage, false);
- {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- {$IFDEF FPC_FEXPAND_VOLUMES}
- Pa := Copy (Pa, 1, PathStart - 2) + TmpS +
- Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
- {$ELSE FPC_FEXPAND_VOLUMES}
- { copy() instead of Pa[1] to preserve string code page }
- Pa := Copy (Pa, 1, 1) + TmpS +
- Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
- {$ENDIF FPC_FEXPAND_VOLUMES}
- end
- {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
- end
- else
- {$ELSE FPC_FEXPAND_DRIVES}
- (* If drives are not supported, but a drive *)
- (* was supplied anyway, ignore (remove) it. *)
- Delete (Pa, 1, 2);
- end;
- {Check whether we don't have an absolute path already}
- if (Length (Pa) >= PathStart) and (Pa [PathStart] <> DirectorySeparator) or
- (Length (Pa) < PathStart) then
- {$ENDIF FPC_FEXPAND_DRIVES}
- begin
- (* Get current directory on selected drive/volume. *)
- GetDirIO (0, S);
- {$IFDEF FPC_FEXPAND_VOLUMES}
- {$IFDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- PathStart := Pos (DriveSeparator, S);
- {$ELSE FPC_FEXPAND_DRIVESEP_IS_ROOT}
- PathStart := Succ (Pos (DriveSeparator, S));
- {$ENDIF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- {$ENDIF FPC_FEXPAND_VOLUMES}
- (* Do we have an absolute path without drive or volume? *)
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$IFDEF FPC_FEXPAND_DRIVES}
- if (Length (Pa) > 0)
- {$IFDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- and (Pa [1] = DriveSeparator)
- {$ELSE FPC_FEXPAND_DRIVESEP_IS_ROOT}
- and (Pa [1] = DirectorySeparator)
- {$ENDIF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- then
- begin
- {$IFDEF FPC_FEXPAND_UNC}
- {Do not touch network drive names}
- if (Length (Pa) > 1) and (Pa [2] = DirectorySeparator)
- and LFNSupport then
- begin
- PathStart := 3;
- {Find the start of the string of directories}
- while (PathStart <= Length (Pa)) and
- (Pa [PathStart] <> DirectorySeparator) do
- Inc (PathStart);
- if PathStart > Length (Pa) then
- {We have just a machine name...}
- if Length (Pa) = 2 then
- {...or not even that one}
- PathStart := 2
- else
- begin
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- { no string concatenation to prevent code page
- conversion for RawByteString }
- SetLength(Pa, Length(Pa) + 1);
- Pa[Length(Pa)] := DirectorySeparator
- {$ELSE FPC_FEXPAND_SYSUTILS}
- Pa := Pa + DirectorySeparator;
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- end
- else if PathStart < Length (Pa) then
- {We have a resource name as well}
- begin
- RootNotNeeded := true;
- {Let's continue in searching}
- repeat
- Inc (PathStart);
- until (PathStart > Length (Pa)) or
- (Pa [PathStart] = DirectorySeparator);
- end;
- end
- else
- {$ENDIF FPC_FEXPAND_UNC}
- begin
- {$IFDEF FPC_FEXPAND_VOLUMES}
- I := Pos (DriveSeparator, S);
- {$IFDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$IFDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- if (Pa [1] = DriveSeparator) then
- Delete (Pa, 1, 1);
- {$ENDIF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- Pa := Copy (S, 1, I) + Pa;
- PathStart := I;
- {$ELSE FPC_FEXPAND_DIRSEP_IS_UPDIR}
- TmpS := Copy (S, 1, Pred (I));
- SetLength(TmpS, Length(TmpS) + 1);
- TmpS[Length(TmpS)] := DriveSeparator;
- Pa := TmpS + Pa;
- PathStart := Succ (I);
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$ELSE FPC_FEXPAND_VOLUMES}
- TmpS := S[1] + DriveSeparator;
- {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
- SetCodePage(TmpS, DefaultFileSystemCodePage, false);
- {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- Pa := TmpS + Pa;
- {$ENDIF FPC_FEXPAND_VOLUMES}
- end;
- end
- else
- {$ENDIF FPC_FEXPAND_DRIVES}
- (* We already have a slash if root is the curent directory. *)
- if Length (S) = PathStart then
- Pa := S + Pa
- {$ELSE FPC_FEXPAND_DIRSEP_IS_CURDIR}
- (* More complex with DirectorySeparator as current directory *)
- if (S [Length (S)] = DriveSeparator)
- and (Pa [1] = DirectorySeparator) then
- Pa := S + Copy (Pa, 2, Pred (Length (Pa)))
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- else
- (* We need an ending slash if FExpand was called *)
- (* with an empty string for compatibility, except *)
- (* for platforms where this is invalid. *)
- if Length (Pa) = 0 then
- begin
- Pa := S;
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- { no string concatenation to prevent code page
- conversion for RawByteString }
- SetLength(Pa, Length(Pa) + 1);
- Pa[Length(Pa)] := DirectorySeparator
- {$ELSE FPC_FEXPAND_SYSUTILS}
- Pa := Pa + DirectorySeparator;
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- {$ENDIF not FPC_FEXPAND_DIRSEP_IS_UPDIR}
- end
- else
- {$IFDEF FPC_FEXPAND_UPDIR_HELPER}
- if Pa [1] = DirectorySeparator then
- Pa := S + Pa
- else
- {$ENDIF FPC_FEXPAND_UPDIR_HELPER}
- begin
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- { not "Pa := S + DirectorySeparator + Pa" because
- that will convert the result to
- DefaultSystemCodePage in case of RawByteString due
- to DirectorySeparator being an ansichar. Don't
- always use this code because in case of
- truncation with shortstrings the result will be
- different }
- TmpS := S;
- SetLength(TmpS, Length(TmpS) + 1);
- TmpS[Length(TmpS)] := DirectorySeparator;
- Pa := TmpS + Pa;
- {$ELSE FPC_FEXPAND_SYSUTILS}
- Pa := S + DirectorySeparator + Pa
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- end;
- end;
- {Get string of directories to only process relative references on this one}
- Dirs := Copy (Pa, Succ (PathStart), Length (Pa) - PathStart);
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {Before anything else, remove doubled DirectorySeparator characters
- - technically invalid or at least useless, but ignored by most operating
- systems except for plain DOS.}
- I := Pos (DirectorySeparator + DirectorySeparator, Dirs);
- while I <> 0 do
- begin
- J := Succ (I);
- while (Length (Dirs) > J) and (Dirs [Succ (J)] = DirectorySeparator) do
- Inc (J);
- Delete (Dirs, Succ (I), J - I);
- I := Pos (DirectorySeparator + DirectorySeparator, Dirs);
- end;
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$IFNDEF FPC_FEXPAND_NO_CURDIR}
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {First remove all references to '\.\'}
- I := Pos (DirectorySeparator + '.' + DirectorySeparator, Dirs);
- while I <> 0 do
- begin
- Delete (Dirs, I, 2);
- I := Pos (DirectorySeparator + '.' + DirectorySeparator, Dirs);
- end;
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$ENDIF FPC_FEXPAND_NO_CURDIR}
- {$IFNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
- {$IFDEF FPC_FEXPAND_MULTIPLE_UPDIR}
- {Now replace all references to '\...' with '\..\..'}
- I := Pos (DirectorySeparator + '...', Dirs);
- while I <> 0 do
- begin
- Insert (DirectorySeparator + '.', Dirs, I + 3);
- I := Pos (DirectorySeparator + '...', Dirs);
- end;
- {$ENDIF FPC_FEXPAND_MULTIPLE_UPDIR}
- {Now remove also all references to '\..\' + of course previous dirs..}
- I := Pos (DirectorySeparator + '..' + DirectorySeparator, Dirs);
- while I <> 0 do
- begin
- J := Pred (I);
- while (J > 0) and (Dirs [J] <> DirectorySeparator) do
- Dec (J);
- Delete (Dirs, Succ (J), I - J + 3);
- I := Pos (DirectorySeparator + '..' + DirectorySeparator, Dirs);
- end;
- {$ENDIF FPC_FEXPAND_NO_DOTS_UPDIR}
- {$IFDEF FPC_FEXPAND_UPDIR_HELPER}
- { Now remove all references to '//' or '::' plus previous directories... }
- I := Pos (DirectorySeparator + DirectorySeparator, Dirs);
- while I <> 0 do
- begin
- J := Pred (I);
- while (J > 0) and (Dirs [J] <> DirectorySeparator) do
- Dec (J);
- Delete (Dirs, Succ (J), Succ (I - J));
- I := Pos (DirectorySeparator + DirectorySeparator, Dirs);
- end;
- {$ENDIF FPC_FEXPAND_UPDIR_HELPER}
- {$IFNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
- {Then remove also a reference to '\..' at the end of line
- + the previous directory, of course,...}
- I := Pos (DirectorySeparator + '..', Dirs);
- if (I <> 0) and (I = Length (Dirs) - 2) then
- begin
- J := Pred (I);
- while (J > 0) and (Dirs [J] <> DirectorySeparator) do
- Dec (J);
- if (J = 0) then
- Dirs := ''
- else
- Delete (Dirs, Succ (J), I - J + 2);
- end;
- {$ENDIF FPC_FEXPAND_NO_DOTS_UPDIR}
- {$IFNDEF FPC_FEXPAND_NO_CURDIR}
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {...and also a possible reference to '\.'}
- if (Length (Dirs) = 1) then
- begin
- if (Dirs [1] = '.') then
- {A special case}
- Dirs := ''
- end
- else
- if (Length (Dirs) <> 0) and (Dirs [Length (Dirs)] = '.') and
- (Dirs [Pred (Length (Dirs))] = DirectorySeparator) then
- Delete (Dirs,length(Dirs)-1,2);
- {Finally remove '.\' at the beginning of the string of directories...}
- while (Length (Dirs) >= 2) and (Dirs [1] = '.')
- and (Dirs [2] = DirectorySeparator) do
- Delete (Dirs, 1, 2);
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$ENDIF FPC_FEXPAND_NO_CURDIR}
- {$IFDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- (* Remove possible (invalid) references to '/' at the beginning. *)
- while (Length (Dirs) >= 1) and (Dirs [1] = '/') do
- Delete (Dirs, 1, 1);
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$IFNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
- {...and possible (invalid) references to '..\' as well}
- while (Length (Dirs) >= 3) and (Dirs [1] = '.') and (Dirs [2] = '.') and
- (Dirs [3] = DirectorySeparator) do
- Delete (Dirs, 1, 3);
- {$ENDIF FPC_FEXPAND_NO_DOTS_UPDIR}
- {Two special cases - '.' and '..' alone}
- {$IFNDEF FPC_FEXPAND_NO_CURDIR}
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- if (Length (Dirs) = 1) and (Dirs [1] = '.') then
- Dirs := '';
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_CURDIR}
- {$ENDIF FPC_FEXPAND_NO_CURDIR}
- {$IFNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
- if (Length (Dirs) = 2) and (Dirs [1] = '.') and (Dirs [2] = '.') then
- Dirs := '';
- {$ENDIF FPC_FEXPAND_NO_DOTS_UPDIR}
- {Join the parts back to create the complete path}
- if Length (Dirs) = 0 then
- begin
- Pa := Copy (Pa, 1, PathStart);
- {$IFNDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- if Pa [PathStart] <> DirectorySeparator then
- begin
- {$IFDEF FPC_FEXPAND_SYSUTILS}
- { no string concatenation to prevent code page
- conversion for RawByteString }
- SetLength(Pa, Length(Pa) + 1);
- Pa[Length(Pa)] := DirectorySeparator
- {$ELSE FPC_FEXPAND_SYSUTILS}
- Pa := Pa + DirectorySeparator;
- {$ENDIF FPC_FEXPAND_SYSUTILS}
- end
- {$ENDIF FPC_FEXPAND_DRIVESEP_IS_ROOT}
- end
- else
- Pa := Copy (Pa, 1, PathStart) + Dirs;
- {$IFNDEF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {Remove ending \ if not supplied originally, the original string
- wasn't empty (to stay compatible) and if not really needed}
- if (Pa [Length (Pa)] = DirectorySeparator)
- and ((Length (Pa) > PathStart) or
- {A special case with UNC paths}
- (RootNotNeeded and (Length (Pa) = PathStart)))
- {Reference to current directory at the end should be removed}
- and (Length (Path) <> 0)
- and (Path [Length (Path)] <> DirectorySeparator)
- then
- Delete (PA,length(PA),1);
- {$ENDIF FPC_FEXPAND_DIRSEP_IS_UPDIR}
- {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
- { return result in expected code page }
- SetCodePage(Pa,DefaultRTLFileSystemCodePage);
- {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
- FExpand := Pa;
- end;
- (* Description of individual conditional defines supported for FExpand
- (disregard the used directory separators in examples, constant
- System.DirectorySeparator is used in the real implemenation, of course):
- FPC_FEXPAND_UNC - UNC ("Universal Naming Convention") paths are
- supported (usually used for networking, used in DOS (with
- networking support installed), OS/2, Win32 and at least some
- Netware versions as far as I remember. An example of such a path
- is '\\servername\sharename\some\path'.
- FPC_FEXPAND_DRIVES - drive letters are supported (DOS-like
- environments - DOS, OS/2, Win32). Example is 'C:\TEST'.
- FPC_FEXPAND_GETENV_PCHAR - an implementation of GetEnv returning
- PChar instead of a shortstring is available (Unix) to support
- long values of environment variables.
- FPC_FEXPAND_TILDE - expansion of '~/' to GetEnv('HOME') - Unix.
- Example: '~/some/path'.
- FPC_FEXPAND_VOLUMES - volumes are supported (similar to drives,
- but the name can be longer; used under Netware, Amiga and
- probably MacOS as far as I understand it correctly). Example:
- 'VolumeName:Some:Path' or 'ServerName/Volume:Some\Path'
- (Netware).
- FPC_FEXPAND_NO_DEFAULT_PATHS - Dos keeps information about the
- current directory for every drive. If some platform supports
- drives or volumes, but keeps no track of current directories for
- them (i.e. there's no support for "GetDir(DriveNumber, Dir)" or
- "GetDir(Volume, Dir)", but only for "GetDir (0, Dir)" (i.e. the
- overall current directory), you should define this. Otherwise
- constructs like 'C:Some\Path' refer a path relative to the
- current directory on the C: drive.
- FPC_FEXPAND_DRIVESEP_IS_ROOT - this means that DriveSeparator
- should be used as beginning of the "real" path for a particular
- drive or volume instead of the DirectorySeparator. This would be
- used in case that there is only one character (DriveSeparator)
- delimitting the drive letter or volume name from the remaining
- path _and_ the DriveSeparator marks the root of an absolute path
- in that case. Example - 'Volume:This/Is/Absolute/Path'.
- FPC_FEXPAND_NO_CURDIR - there is no support to refer to current
- directory explicitely (like '.' used under both Unix and DOS-like
- environments).
- FPC_FEXPAND_NO_DOTS_UPDIR - '..' cannot be used to refer to the
- upper directory.
- FPC_FEXPAND_DIRSEP_IS_UPDIR - DirectorySeparator at the beginning of
- a path (or doubled DirectorySeparator inside the path) refer to the
- parent directory, one more DirectorySeparator to parent directory of
- parent directory and so on (Amiga). Please, note that you can decide
- to support both '..' and DirectorySeparator as references to the parent
- directory at the same time for compatibility reasons - however this
- support makes it impossible to use otherwise possibly valid name
- of '..'.
- FPC_FEXPAND_DIRSEP_IS_CURDIR - DirectorySeparator at the beginning of
- a path refers to the current directory (i.e. path beginning with
- DirectorySeparator is always a relative path). Two DirectorySeparator
- characters refer to the parent directory, three refer to parent
- directory of the parent directory and so on (MacOS).
- FPC_FEXPAND_MULTIPLE_UPDIR - grouping of more characters specifying
- upper directory references higher directory levels. Example: '...'
- (Netware).
- FPC_FEXPAND_SYSUTILS allows to reuse the same implementation for
- SysUtils.ExpandFileName by avoiding things specific for unit Dos.
- *)
|