fexpand.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1997-2000 by the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {****************************************************************************
  12. A platform independent FExpand implementation
  13. ****************************************************************************}
  14. procedure GetDirIO (DriveNr: byte; var Dir: OpenString);
  15. (* GetDirIO is supposed to return the root of the given drive *)
  16. (* in case of an error for compatibility of FExpand with TP/BP. *)
  17. var
  18. OldInOutRes: word;
  19. begin
  20. OldInOutRes := InOutRes;
  21. InOutRes := 0;
  22. GetDir (DriveNr, Dir);
  23. InOutRes := OldInOutRes;
  24. end;
  25. {$IFDEF FPC_FEXPAND_VOLUMES}
  26. procedure GetDirIO (const VolumeName: OpenString; var Dir: OpenString);
  27. var
  28. OldInOutRes: word;
  29. begin
  30. OldInOutRes := InOutRes;
  31. InOutRes := 0;
  32. GetDir (VolumeName, Dir);
  33. InOutRes := OldInOutRes;
  34. end;
  35. {$ENDIF FPC_FEXPAND_VOLUMES}
  36. function FExpand (const Path: PathStr): PathStr;
  37. (* LFNSupport boolean constant, variable or function must be declared for all
  38. the platforms, at least locally in the Dos unit implementation part.
  39. In addition, FPC_FEXPAND_UNC, FPC_FEXPAND_DRIVES, FPC_FEXPAND_GETENV_PCHAR,
  40. FPC_FEXPAND_TILDE and FPC_FEXPAND_VOLUMES conditionals might be defined to
  41. specify FExpand behaviour. Only forward slashes are supported if UNIX
  42. conditional is defined, both forward and backslashes otherwise.
  43. *)
  44. const
  45. {$IFDEF UNIX}
  46. DirSep = '/';
  47. {$ELSE UNIX}
  48. DirSep = '\';
  49. {$ENDIF UNIX}
  50. DriveSep = ':';
  51. {$IFDEF FPC_FEXPAND_DRIVES}
  52. PathStart: longint = 3;
  53. {$ELSE FPC_FEXPAND_DRIVES}
  54. PathStart = 1;
  55. {$ENDIF FPC_FEXPAND_DRIVES}
  56. {$IFDEF FPC_FEXPAND_UNC}
  57. RootNotNeeded: boolean = false;
  58. {$ELSE FPC_FEXPAND_UNC}
  59. RootNotNeeded = false;
  60. {$ENDIF FPC_FEXPAND_UNC}
  61. var S, Pa, Dirs: PathStr;
  62. I, J: longint;
  63. begin
  64. {$IFDEF FPC_FEXPAND_UNC}
  65. RootNotNeeded := false;
  66. {$ENDIF FPC_FEXPAND_UNC}
  67. if FileNameCaseSensitive then
  68. Pa := Path
  69. else
  70. Pa := UpCase (Path);
  71. {$IFNDEF UNIX}
  72. {Allow slash as backslash}
  73. for I := 1 to Length (Pa) do
  74. if Pa [I] = '/' then
  75. Pa [I] := DirSep;
  76. {$ENDIF UNIX}
  77. {$IFDEF FPC_FEXPAND_VOLUMES}
  78. PathStart := Succ (Pos (DriveSep, Pa));
  79. {$ENDIF FPC_FEXPAND_VOLUMES}
  80. {$IFDEF FPC_FEXPAND_TILDE}
  81. {Replace ~/ with $HOME/}
  82. if (Length (Pa) >= 1) and (Pa [1] = '~') and
  83. ((Pa [2] = DirSep) or (Length (Pa) = 1)) then
  84. begin
  85. {$IFDEF FPC_FEXPAND_GETENV_PCHAR}
  86. S := StrPas (GetEnv ('HOME'));
  87. {$ELSE FPC_FEXPAND_GETENV_PCHAR}
  88. S := GetEnv ('HOME');
  89. {$ENDIF FPC_FEXPAND_GETENV_PCHAR}
  90. if (S = '') or (Length (S) = 1) and (S [1] = DirSep) then
  91. Delete (Pa, 1, 1)
  92. else
  93. if S [Length (S)] = DirSep then
  94. Pa := S + Copy (Pa, 3, Length (Pa) - 2)
  95. else
  96. Pa := S + Copy (Pa, 2, Pred (Length (Pa)));
  97. end;
  98. {$ENDIF FPC_FEXPAND_TILDE}
  99. {$IFDEF FPC_FEXPAND_VOLUMES}
  100. if PathStart > 1 then
  101. {$ELSE FPC_FEXPAND_VOLUMES}
  102. if (Length (Pa) > 1) and (Pa [1] in ['A'..'Z', 'a'..'z']) and
  103. (Pa [2] = DriveSep) then
  104. {$ENDIF FPC_FEXPAND_VOLUMES}
  105. begin
  106. {$IFDEF FPC_FEXPAND_DRIVES}
  107. {$IFDEF FPC_FEXPAND_VOLUMES}
  108. GetDirIO (Copy (Pa, 1, PathStart - 2), S);
  109. {$ELSE FPC_FEXPAND_VOLUMES}
  110. { Always uppercase driveletter }
  111. if (Pa [1] in ['a'..'z']) then
  112. Pa [1] := Chr (Ord (Pa [1]) and not ($20));
  113. GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S);
  114. {$ENDIF FPC_FEXPAND_VOLUMES}
  115. if Length (Pa) = Pred (PathStart) then
  116. Pa := S
  117. else
  118. if Pa [PathStart] <> DirSep then
  119. {$IFDEF FPC_FEXPAND_VOLUMES}
  120. if Copy (Pa, 1, PathStart - 2) = Copy (S, 1, PathStart - 2)
  121. then
  122. {$ELSE FPC_FEXPAND_VOLUMES}
  123. if Pa [1] = S [1] then
  124. {$ENDIF FPC_FEXPAND_VOLUMES}
  125. begin
  126. { remove ending slash if it already exists }
  127. if S [Length (S)] = DirSep then
  128. Dec (S [0]);
  129. Pa := S + DirSep +
  130. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  131. end
  132. else
  133. {$IFDEF FPC_FEXPAND_VOLUMES}
  134. Pa := Copy (Pa, 1, PathStart - 2) + DriveSep + DirSep +
  135. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  136. {$ELSE FPC_FEXPAND_VOLUMES}
  137. Pa := Pa [1] + DriveSep + DirSep +
  138. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  139. {$ENDIF FPC_FEXPAND_VOLUMES}
  140. end
  141. else
  142. {$ELSE FPC_FEXPAND_DRIVES}
  143. Delete (Pa, 1, 2);
  144. end;
  145. {Check whether we don't have an absolute path already}
  146. if (Length (Pa) >= PathStart) and (Pa [PathStart] <> DirSep) or
  147. (Length (Pa) < PathStart) then
  148. {$ENDIF FPC_FEXPAND_DRIVES}
  149. begin
  150. GetDirIO (0, S);
  151. {$IFDEF FPC_FEXPAND_DRIVES}
  152. if (Length (Pa) > 0) and (Pa [1] = DirSep) then
  153. begin
  154. {$IFDEF FPC_FEXPAND_UNC}
  155. {Do not touch network drive names}
  156. if (Length (Pa) > 1) and (Pa [2] = DirSep)
  157. and LFNSupport then
  158. begin
  159. PathStart := 3;
  160. {Find the start of the string of directories}
  161. while (PathStart <= Length (Pa)) and
  162. (Pa [PathStart] <> DirSep) do
  163. Inc (PathStart);
  164. if PathStart > Length (Pa) then
  165. {We have just a machine name...}
  166. if Length (Pa) = 2 then
  167. {...or not even that one}
  168. PathStart := 2
  169. else
  170. Pa := Pa + DirSep
  171. else
  172. if PathStart < Length (Pa) then
  173. {We have a resource name as well}
  174. begin
  175. RootNotNeeded := true;
  176. {Let's continue in searching}
  177. repeat
  178. Inc (PathStart);
  179. until (PathStart > Length (Pa)) or
  180. (Pa [PathStart] = DirSep);
  181. end;
  182. end
  183. else
  184. {$ENDIF FPC_FEXPAND_UNC}
  185. {$IFDEF FPC_FEXPAND_VOLUMES}
  186. begin
  187. I := Pos (DriveSep, S);
  188. Pa := Copy (S, 1, Pred (I)) + DriveSep + Pa;
  189. PathStart := Succ (I);
  190. end;
  191. {$ELSE FPC_FEXPAND_VOLUMES}
  192. Pa := S [1] + DriveSep + Pa;
  193. {$ENDIF FPC_FEXPAND_VOLUMES}
  194. end
  195. else
  196. {$ENDIF FPC_FEXPAND_DRIVES}
  197. (* We already have a slash if root is the curent directory. *)
  198. if Length (S) = PathStart then
  199. Pa := S + Pa
  200. else
  201. (* We need an ending slash if FExpand was called
  202. with an empty string for compatibility. *)
  203. if Length (Pa) = 0 then
  204. Pa := S + DirSep
  205. else
  206. Pa := S + DirSep + Pa;
  207. end;
  208. {Get string of directories to only process relative references on this one}
  209. Dirs := Copy (Pa, Succ (PathStart), Length (Pa) - PathStart);
  210. {First remove all references to '\.\'}
  211. I := Pos (DirSep + '.' + DirSep, Dirs);
  212. while I <> 0 do
  213. begin
  214. Delete (Dirs, I, 2);
  215. I := Pos (DirSep + '.' + DirSep, Dirs);
  216. end;
  217. {Now remove also all references to '\..\' + of course previous dirs..}
  218. I := Pos (DirSep + '..' + DirSep, Dirs);
  219. while I <> 0 do
  220. begin
  221. J := Pred (I);
  222. while (J > 0) and (Dirs [J] <> DirSep) do
  223. Dec (J);
  224. Delete (Dirs, Succ (J), I - J + 3);
  225. I := Pos (DirSep + '..' + DirSep, Dirs);
  226. end;
  227. {Then remove also a reference to '\..' at the end of line
  228. + the previous directory, of course,...}
  229. I := Pos (DirSep + '..', Dirs);
  230. if (I <> 0) and (I = Length (Dirs) - 2) then
  231. begin
  232. J := Pred (I);
  233. while (J >= 0) and (Dirs [J] <> DirSep) do
  234. Dec (J);
  235. if (J = 0) then
  236. Dirs := ''
  237. else
  238. Delete (Dirs, Succ (J), I - J + 2);
  239. end;
  240. {...and also a possible reference to '\.'}
  241. if (Length (Dirs) = 1) then
  242. begin
  243. if (Dirs [1] = '.') then
  244. {A special case}
  245. Dirs := ''
  246. end
  247. else
  248. if (Length (Dirs) <> 0) and (Dirs [Length (Dirs)] = '.') and
  249. (Dirs [Pred (Length (Dirs))] = DirSep) then
  250. Dec (Dirs [0], 2);
  251. {Finally remove '.\' at the beginning of the string of directories...}
  252. while (Length (Dirs) >= 2) and (Dirs [1] = '.') and (Dirs [2] = DirSep) do
  253. Delete (Dirs, 1, 2);
  254. {...and possible (invalid) references to '..\' as well}
  255. while (Length (Dirs) >= 3) and (Dirs [1] = '.') and (Dirs [2] = '.') and
  256. (Dirs [3] = DirSep) do
  257. Delete (Dirs, 1, 3);
  258. {Two special cases - '.' and '..' alone}
  259. if (Length (Dirs) = 1) and (Dirs [1] = '.') or
  260. (Length (Dirs) = 2) and (Dirs [1] = '.') and (Dirs [2] = '.') then
  261. Dirs := '';
  262. {Join the parts back to create the complete path}
  263. if Length (Dirs) = 0 then
  264. begin
  265. Pa := Copy (Pa, 1, PathStart);
  266. if Pa [PathStart] <> DirSep then
  267. Pa := Pa + DirSep;
  268. end
  269. else
  270. Pa := Copy (Pa, 1, PathStart) + Dirs;
  271. {Remove ending \ if not supplied originally, the original string
  272. wasn't empty (to stay compatible) and if not really needed}
  273. if (Pa [Length (Pa)] = DirSep) and ((Length (Pa) > PathStart) or
  274. {A special case with UNC paths}
  275. (RootNotNeeded and (Length (Pa) = PathStart))) and
  276. (Length (Path) <> 0) and (Path [Length (Path)] <> DirSep) then
  277. Dec (Pa [0]);
  278. FExpand := Pa;
  279. end;
  280. {
  281. $Log$
  282. Revision 1.8 2001-04-09 19:28:40 hajny
  283. * yet another fix for FExpand under Unix
  284. Revision 1.5 2001/03/21 21:08:20 hajny
  285. * GetDir fixed
  286. Revision 1.4 2001/03/19 21:09:30 hajny
  287. * one more problem in the Unix part
  288. Revision 1.3 2001/03/19 21:05:42 hajny
  289. * mistyping in the Unix part fixed
  290. Revision 1.2 2001/03/10 09:57:51 hajny
  291. * FExpand without IOResult change, remaining direct asm removed
  292. }