mirror of
				https://gitlab.com/freepascal.org/fpc/source.git
				synced 2025-10-31 08:51:37 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			713 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			713 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| {
 | |
|     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, BasePath: 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.           *)
 | |
| {$push}
 | |
| {$warnings off}
 | |
| 	  for I := 1 to Length (Pa) do
 | |
| 	    if CharInSet(Pa [I], AllowDirectorySeparators)  then
 | |
| 	      Pa [I] := DirectorySeparator;
 | |
| {$pop}
 | |
| {$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
 | |
|                       ((Length (Pa) = 1) or (Pa [2] = DirectorySeparator)) 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 (Length(S)=0) 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 base path *)
 | |
| {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
 | |
|             S := ToSingleByteFileSystemEncodedFileName (BasePath);
 | |
| {$ELSE FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
 | |
|             S := BasePath;
 | |
| {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
 | |
|             if not FileNameCasePreserving then
 | |
|               S := UpCase(S)
 | |
|             else
 | |
|             { Always uppercase driveletter }
 | |
|             if (Length (S) > 1) and CharInSet(S [1], ['a'..'z']) and
 | |
|               (S [2] = DriveSeparator) and (DriveSeparator <> DirectorySeparator) then
 | |
|                 S [1] := Chr (Ord (S [1]) and not ($20));
 | |
| 
 | |
| {$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;
 | |
| 
 | |
| function FExpand (const Path: PathStr): PathStr;
 | |
| var
 | |
|   BaseDir: {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}RawByteString{$else}PathStr{$endif};
 | |
| begin
 | |
|   GetDirIO(0, BaseDir);
 | |
| {$IF defined(FPC_FEXPAND_SYSUTILS) and not defined(SYSUTILSUNICODE)}
 | |
|   { convert BaseDir to expected code page }
 | |
|   SetCodePage(BaseDir,DefaultRTLFileSystemCodePage);
 | |
| {$ENDIF FPC_FEXPAND_SYSUTILS and not SYSUTILSUNICODE}
 | |
|   FExpand := FExpand(Path, PathStr(BaseDir));
 | |
| 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
 | |
|    PAnsiChar 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.
 | |
| *)
 | |
| 
 | 
