浏览代码

* fix for absolute paths on platforms without drives (*nix), support for long volume names added

Tomas Hajny 24 年之前
父节点
当前提交
a820a0f393
共有 1 个文件被更改,包括 133 次插入66 次删除
  1. 133 66
      rtl/inc/fexpand.inc

+ 133 - 66
rtl/inc/fexpand.inc

@@ -17,7 +17,7 @@
 ****************************************************************************}
 
 
-function GetDirIO (DriveNr: byte; var Dir: OpenString): word;
+procedure GetDirIO (DriveNr: byte; var Dir: OpenString);
 
 (* GetDirIO is supposed to return the root of the given drive   *)
 (* in case of an error for compatibility of FExpand with TP/BP. *)
@@ -28,19 +28,32 @@ begin
   OldInOutRes := InOutRes;
   InOutRes := 0;
   GetDir (DriveNr, Dir);
-  GetDirIO := InOutRes;
   InOutRes := OldInOutRes;
 end;
 
 
+{$IFDEF FPC_FEXPAND_VOLUMES}
+procedure GetDirIO (const VolumeName: OpenString; var Dir: OpenString);
+
+var
+  OldInOutRes: word;
+begin
+  OldInOutRes := InOutRes;
+  InOutRes := 0;
+  GetDir (VolumeName, Dir);
+  InOutRes := OldInOutRes;
+end;
+{$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
-   and FPC_FEXPAND_TILDE conditionals might be defined to specify FExpand
-   behaviour. Only forward slashes are supported if UNIX conditional
-   is defined, both forward and backslashes otherwise.
+   In addition, FPC_FEXPAND_UNC, FPC_FEXPAND_DRIVES, FPC_FEXPAND_GETENV_PCHAR,
+   FPC_FEXPAND_TILDE and FPC_FEXPAND_VOLUMES conditionals might be defined to
+   specify FExpand behaviour. Only forward slashes are supported if UNIX
+   conditional is defined, both forward and backslashes otherwise.
 *)
 
 const
@@ -49,13 +62,14 @@ const
 {$ELSE UNIX}
     DirSep = '\';
 {$ENDIF UNIX}
+    DriveSep = ':';
 {$IFDEF FPC_FEXPAND_DRIVES}
-    PathStart = 3;
+    PathStart: longint = 3;
 {$ELSE FPC_FEXPAND_DRIVES}
     PathStart = 1;
 {$ENDIF FPC_FEXPAND_DRIVES}
 
-var S, Pa: PathStr;
+var S, Pa, Dirs: PathStr;
     I, J: longint;
 
 begin
@@ -68,10 +82,13 @@ begin
     for I := 1 to Length (Pa) do
         if Pa [I] = '/' then
             Pa [I] := DirSep;
-{$ENDIF}
+{$ENDIF UNIX}
+{$IFDEF FPC_FEXPAND_VOLUMES}
+    PathStart := Succ (Pos (DriveSep, Pa));
+{$ENDIF FPC_FEXPAND_VOLUMES}
 {$IFDEF FPC_FEXPAND_TILDE}
-    {Replace ~/ with $HOME}
-    if (Length (Pa) >= 1) and (Pa [1] ='~') and 
+    {Replace ~/ with $HOME/}
+    if (Length (Pa) >= 1) and (Pa [1] = '~') and 
                                   ((Pa [2] = DirSep) or (Length (Pa) = 1)) then
         begin
  {$IFDEF FPC_FEXPAND_GETENV_PCHAR}
@@ -88,45 +105,85 @@ begin
                     Pa := S + Copy (Pa, 2, Pred (Length (Pa)));
    end;
 {$ENDIF FPC_FEXPAND_TILDE}
+{$IFDEF FPC_FEXPAND_VOLUMES}
+    if PathStart > 1 then
+{$ELSE FPC_FEXPAND_VOLUMES}
     if (Length (Pa) > 1) and (Pa [1] in ['A'..'Z', 'a'..'z']) and
-                                                            (Pa [2] = ':') then
+                                                       (Pa [2] = DriveSep) then
+{$ENDIF FPC_FEXPAND_VOLUMES}
         begin
 {$IFDEF FPC_FEXPAND_DRIVES}
+ {$IFDEF FPC_FEXPAND_VOLUMES}
+            GetDirIO (Copy (Pa, 1, PathStart - 2), S);
+ {$ELSE FPC_FEXPAND_VOLUMES}
             { Always uppercase driveletter }
             if (Pa [1] in ['a'..'z']) then
                 Pa [1] := Chr (Ord (Pa [1]) and not ($20));
-            if GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S) = 0 then ;
-            case Length (Pa) of
-                2: Pa := S;
+            GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S);
+ {$ENDIF FPC_FEXPAND_VOLUMES}
+            if Length (Pa) = Pred (PathStart) then
+                Pa := S
             else
-                if Pa [3] <> DirSep then
+                if Pa [PathStart] <> DirSep then
+ {$IFDEF FPC_FEXPAND_VOLUMES}
+                    if Copy (Pa, 1, PathStart - 2) = Copy (S, 1, PathStart - 2)
+                                                                           then
+ {$ELSE FPC_FEXPAND_VOLUMES}
                     if Pa [1] = S [1] then
+ {$ENDIF FPC_FEXPAND_VOLUMES}
                         begin
                             { remove ending slash if it already exists }
                             if S [Length (S)] = DirSep then
                                 Dec (S [0]);
-                            Pa := S + DirSep + Copy (Pa, 3, Length (Pa))
+                            Pa := S + DirSep +
+                              Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
                         end
                     else
-                        Pa := Pa [1] + ':' + DirSep + Copy (Pa, 3, Length (Pa))
-            end;
+ {$IFDEF FPC_FEXPAND_VOLUMES}
+                        Pa := Copy (Pa, 1, PathStart - 2) + DriveSep + DirSep +
+                              Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
+ {$ELSE FPC_FEXPAND_VOLUMES}
+                        Pa := Pa [1] + DriveSep + DirSep +
+                              Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
+ {$ENDIF FPC_FEXPAND_VOLUMES}
         end
     else
 {$ELSE FPC_FEXPAND_DRIVES}
             Delete (Pa, 1, 2);
         end;
+    {Check whether we don't have an absolute path already}
+    if (Length (Pa) >= PathStart) and (Pa [PathStart] <> DirSep) then
 {$ENDIF FPC_FEXPAND_DRIVES}
         begin
-            if GetDirIO (0, S) = 0 then ;
+            GetDirIO (0, S);
 {$IFDEF FPC_FEXPAND_DRIVES}
             if (Length (Pa) > 0) and (Pa [1] = DirSep) then
                 begin
  {$IFDEF FPC_FEXPAND_UNC}
-                    { Do not touch Network drive names }
-                    if not ((Length (Pa) > 1) and (Pa [2] = Pa [1])
-                                                           and LFNSupport) then
+                    {Do not touch network drive names}
+                    if (Length (Pa) > 1) and (Pa [2] = DirSep)
+                                                            and LFNSupport then
+                        begin
+                            if Length (Pa) = 2 then
+                                Pa := DirSep + DirSep + '.' + DirSep;
+                            PathStart := 3;
+                            {Find the start of the string of directories}
+                            while (Pa [PathStart] <> DirSep) and
+                                                  (PathStart <= Length (Pa)) do
+                                Inc (PathStart);
+                            if PathStart > Length (Pa) then Pa := Pa + DirSep;
+                        end
+                    else
  {$ENDIF FPC_FEXPAND_UNC}
-                        Pa := S [1] + ':' + Pa
+ {$IFDEF FPC_FEXPAND_VOLUMES}
+                        begin
+                            I := Pos (DriveSep, S);
+                            Pa := Copy (S, 1, Pred (I)) + DriveSep + Pa;
+                            PathStart := Succ (I);
+                        end;
+ {$ELSE FPC_FEXPAND_VOLUMES}
+                        Pa := S [1] + DriveSep + Pa;
+ {$ENDIF FPC_FEXPAND_VOLUMES}
                 end
             else
 {$ENDIF FPC_FEXPAND_DRIVES}
@@ -141,62 +198,69 @@ begin
                     else
                         Pa := S + DirSep + Pa;
         end;
+    {Get string of directories to only process relative references on this one}
+    Dirs := Copy (Pa, Succ (PathStart), Length (Pa) - PathStart);
     {First remove all references to '\.\'}
-    I := Pos (DirSep + '.' + DirSep, Pa);
+    I := Pos (DirSep + '.' + DirSep, Dirs);
     while I <> 0 do
         begin
-            Delete (Pa, I, 2);
-            I := Pos (DirSep + '.' + DirSep, Pa);
+            Delete (Dirs, I, 2);
+            I := Pos (DirSep + '.' + DirSep, Dirs);
         end;
     {Now remove also all references to '\..\' + of course previous dirs..}
-    I := Pos (DirSep + '..' + DirSep, Pa);
+    I := Pos (DirSep + '..' + DirSep, Dirs);
     while I <> 0 do
         begin
             J := Pred (I);
-            while (J > 0) and (Pa [J] <> DirSep) do
+            while (J > 0) and (Dirs [J] <> DirSep) do
                 Dec (J);
-            if (J = 0)
-{$IFDEF FPC_FEXPAND_UNC}
-                       or (J = 1) and (I = 2)
-{$ENDIF FPC_FEXPAND_UNC}
-                                              then
-                Delete (Pa, Succ (I), 3)
-            else
-                Delete (Pa, Succ (J), I - J + 3);
-            I := Pos (DirSep + '..' + DirSep, Pa);
+            Delete (Dirs, Succ (J), I - J + 3);
+            I := Pos (DirSep + '..' + DirSep, Dirs);
         end;
-    {Now remove also any reference to '\..' at the end of line
-    + of course previous dir..}
-    I := Pos (DirSep + '..', Pa);
-    if (I <> 0) and (I = Length (Pa) - 2) then
+    {Then remove also a reference to '\..' at the end of line
+    + the previous directory, of course,...}
+    I := Pos (DirSep + '..', Dirs);
+    if (I <> 0) and (I = Length (Dirs) - 2) then
         begin
             J := Pred (I);
-            while (J >= 1) and (Pa [J] <> DirSep) do
+            while (J >= 0) and (Dirs [J] <> DirSep) do
                 Dec (J);
-            if (J = 0)
-{$IFDEF FPC_FEXPAND_UNC}
-                       or (J = 1) and (I = 2)
-{$ENDIF FPC_FEXPAND_UNC}
-                                              then
-                Delete (Pa, Succ (I), 2)
+            if (J = 0) then
+                Dirs := ''
             else
-                Delete (Pa, Succ (J), I - J + 2);
+                Delete (Dirs, Succ (J), I - J + 2);
         end;
-    {Now remove also any reference to '\.' at the end of line}
-    I := Pos (DirSep + '.', Pa);
-    if (I <> 0) and (I = Pred (Length (Pa))) then
-{$IFDEF FPC_FEXPAND_DRIVES}
-        if (I = 3) and (Pa [2] = ':')
-{$ELSE FPC_FEXPAND_DRIVES}
-        if (I = 1)
-{$ENDIF FPC_FEXPAND_DRIVES}
-{$IFDEF FPC_FEXPAND_UNC}
-                                      or (I = 2) and (Pa [1] = '\')
-{$ENDIF FPC_FEXPAND_UNC}
-                                                                    then
-            Dec (Pa [0])
-        else
-            Delete (Pa, I, 2);
+    {...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))] = DirSep) then
+            Dec (Dirs [0], 2);
+    {Finally remove '.\' at the beginning of the string of directories...}
+    while (Length (Dirs) >= 2) and (Dirs [1] = '.') and (Dirs [2] = DirSep) do
+        Delete (Dirs, 1, 2);
+    {...and possible (invalid) references to '..\' as well}
+    while (Length (Dirs) >= 3) and (Dirs [1] = '.') and (Dirs [2] = '.') and
+                                                         (Dirs [3] = DirSep) do
+        Delete (Dirs, 1, 3);
+    {Two special cases - '.' and '..' alone}
+    if (Length (Dirs) = 1) and (Dirs [1] = '.') or
+             (Length (Dirs) = 2) and (Dirs [1] = '.') and (Dirs [2] = '.') then
+        Dirs := '';
+    {Join the parts back to create the complete path}
+    if Length (Dirs) = 0 then
+        begin
+            Pa := Copy (Pa, 1, PathStart);
+            if Pa [PathStart] <> DirSep then
+                Pa := Pa + DirSep;
+        end
+    else
+        Pa := Copy (Pa, 1, PathStart) + Dirs;
     {Remove ending \ if not supplied originally, the original string
     wasn't empty (to stay compatible) and if not really needed}
     if (Length (Pa) > PathStart) and (Pa [Length (Pa)] = DirSep)
@@ -207,7 +271,10 @@ end;
 
 {
   $Log$
-  Revision 1.5  2001-03-21 21:08:20  hajny
+  Revision 1.6  2001-04-07 19:37:27  hajny
+    * fix for absolute paths on platforms without drives (*nix), support for long volume names added
+
+  Revision 1.5  2001/03/21 21:08:20  hajny
     * GetDir fixed
 
   Revision 1.4  2001/03/19 21:09:30  hajny