fexpand.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  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. {$ELSE}
  77. {Allow backslash as slash}
  78. for I := 1 to Length (Pa) do
  79. if Pa [I] = '\' then
  80. Pa [I] := DirSep;
  81. {$ENDIF UNIX}
  82. {$IFDEF FPC_FEXPAND_VOLUMES}
  83. PathStart := Succ (Pos (DriveSep, Pa));
  84. {$ENDIF FPC_FEXPAND_VOLUMES}
  85. {$IFDEF FPC_FEXPAND_TILDE}
  86. {Replace ~/ with $HOME/}
  87. if (Length (Pa) >= 1) and (Pa [1] = '~') and
  88. ((Pa [2] = DirSep) or (Length (Pa) = 1)) then
  89. begin
  90. {$IFDEF FPC_FEXPAND_GETENV_PCHAR}
  91. S := StrPas (GetEnv ('HOME'));
  92. {$ELSE FPC_FEXPAND_GETENV_PCHAR}
  93. S := GetEnv ('HOME');
  94. {$ENDIF FPC_FEXPAND_GETENV_PCHAR}
  95. if (S = '') or (Length (S) = 1) and (S [1] = DirSep) then
  96. Delete (Pa, 1, 1)
  97. else
  98. if S [Length (S)] = DirSep then
  99. Pa := S + Copy (Pa, 3, Length (Pa) - 2)
  100. else
  101. Pa := S + Copy (Pa, 2, Pred (Length (Pa)));
  102. end;
  103. {$ENDIF FPC_FEXPAND_TILDE}
  104. {$IFDEF FPC_FEXPAND_VOLUMES}
  105. if PathStart > 1 then
  106. {$ELSE FPC_FEXPAND_VOLUMES}
  107. if (Length (Pa) > 1) and (Pa [1] in ['A'..'Z', 'a'..'z']) and
  108. (Pa [2] = DriveSep) then
  109. {$ENDIF FPC_FEXPAND_VOLUMES}
  110. begin
  111. {$IFDEF FPC_FEXPAND_DRIVES}
  112. {$IFDEF FPC_FEXPAND_VOLUMES}
  113. GetDirIO (Copy (Pa, 1, PathStart - 2), S);
  114. {$ELSE FPC_FEXPAND_VOLUMES}
  115. { Always uppercase driveletter }
  116. if (Pa [1] in ['a'..'z']) then
  117. Pa [1] := Chr (Ord (Pa [1]) and not ($20));
  118. GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S);
  119. {$ENDIF FPC_FEXPAND_VOLUMES}
  120. if Length (Pa) = Pred (PathStart) then
  121. Pa := S
  122. else
  123. if Pa [PathStart] <> DirSep then
  124. {$IFDEF FPC_FEXPAND_VOLUMES}
  125. if Copy (Pa, 1, PathStart - 2) = Copy (S, 1, PathStart - 2)
  126. then
  127. {$ELSE FPC_FEXPAND_VOLUMES}
  128. if Pa [1] = S [1] then
  129. {$ENDIF FPC_FEXPAND_VOLUMES}
  130. begin
  131. { remove ending slash if it already exists }
  132. if S [Length (S)] = DirSep then
  133. Dec (S [0]);
  134. Pa := S + DirSep +
  135. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  136. end
  137. else
  138. {$IFDEF FPC_FEXPAND_VOLUMES}
  139. Pa := Copy (Pa, 1, PathStart - 2) + DriveSep + DirSep +
  140. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  141. {$ELSE FPC_FEXPAND_VOLUMES}
  142. Pa := Pa [1] + DriveSep + DirSep +
  143. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  144. {$ENDIF FPC_FEXPAND_VOLUMES}
  145. end
  146. else
  147. {$ELSE FPC_FEXPAND_DRIVES}
  148. Delete (Pa, 1, 2);
  149. end;
  150. {Check whether we don't have an absolute path already}
  151. if (Length (Pa) >= PathStart) and (Pa [PathStart] <> DirSep) or
  152. (Length (Pa) < PathStart) then
  153. {$ENDIF FPC_FEXPAND_DRIVES}
  154. begin
  155. GetDirIO (0, S);
  156. {$IFDEF FPC_FEXPAND_DRIVES}
  157. if (Length (Pa) > 0) and (Pa [1] = DirSep) then
  158. begin
  159. {$IFDEF FPC_FEXPAND_UNC}
  160. {Do not touch network drive names}
  161. if (Length (Pa) > 1) and (Pa [2] = DirSep)
  162. and LFNSupport then
  163. begin
  164. PathStart := 3;
  165. {Find the start of the string of directories}
  166. while (PathStart <= Length (Pa)) and
  167. (Pa [PathStart] <> DirSep) do
  168. Inc (PathStart);
  169. if PathStart > Length (Pa) then
  170. {We have just a machine name...}
  171. if Length (Pa) = 2 then
  172. {...or not even that one}
  173. PathStart := 2
  174. else
  175. Pa := Pa + DirSep
  176. else
  177. if PathStart < Length (Pa) then
  178. {We have a resource name as well}
  179. begin
  180. RootNotNeeded := true;
  181. {Let's continue in searching}
  182. repeat
  183. Inc (PathStart);
  184. until (PathStart > Length (Pa)) or
  185. (Pa [PathStart] = DirSep);
  186. end;
  187. end
  188. else
  189. {$ENDIF FPC_FEXPAND_UNC}
  190. {$IFDEF FPC_FEXPAND_VOLUMES}
  191. begin
  192. I := Pos (DriveSep, S);
  193. Pa := Copy (S, 1, Pred (I)) + DriveSep + Pa;
  194. PathStart := Succ (I);
  195. end;
  196. {$ELSE FPC_FEXPAND_VOLUMES}
  197. Pa := S [1] + DriveSep + Pa;
  198. {$ENDIF FPC_FEXPAND_VOLUMES}
  199. end
  200. else
  201. {$ENDIF FPC_FEXPAND_DRIVES}
  202. (* We already have a slash if root is the curent directory. *)
  203. if Length (S) = PathStart then
  204. Pa := S + Pa
  205. else
  206. (* We need an ending slash if FExpand was called
  207. with an empty string for compatibility. *)
  208. if Length (Pa) = 0 then
  209. Pa := S + DirSep
  210. else
  211. Pa := S + DirSep + Pa;
  212. end;
  213. {Get string of directories to only process relative references on this one}
  214. Dirs := Copy (Pa, Succ (PathStart), Length (Pa) - PathStart);
  215. {First remove all references to '\.\'}
  216. I := Pos (DirSep + '.' + DirSep, Dirs);
  217. while I <> 0 do
  218. begin
  219. Delete (Dirs, I, 2);
  220. I := Pos (DirSep + '.' + DirSep, Dirs);
  221. end;
  222. {Now remove also all references to '\..\' + of course previous dirs..}
  223. I := Pos (DirSep + '..' + DirSep, Dirs);
  224. while I <> 0 do
  225. begin
  226. J := Pred (I);
  227. while (J > 0) and (Dirs [J] <> DirSep) do
  228. Dec (J);
  229. Delete (Dirs, Succ (J), I - J + 3);
  230. I := Pos (DirSep + '..' + DirSep, Dirs);
  231. end;
  232. {Then remove also a reference to '\..' at the end of line
  233. + the previous directory, of course,...}
  234. I := Pos (DirSep + '..', Dirs);
  235. if (I <> 0) and (I = Length (Dirs) - 2) then
  236. begin
  237. J := Pred (I);
  238. while (J >= 0) and (Dirs [J] <> DirSep) do
  239. Dec (J);
  240. if (J = 0) then
  241. Dirs := ''
  242. else
  243. Delete (Dirs, Succ (J), I - J + 2);
  244. end;
  245. {...and also a possible reference to '\.'}
  246. if (Length (Dirs) = 1) then
  247. begin
  248. if (Dirs [1] = '.') then
  249. {A special case}
  250. Dirs := ''
  251. end
  252. else
  253. if (Length (Dirs) <> 0) and (Dirs [Length (Dirs)] = '.') and
  254. (Dirs [Pred (Length (Dirs))] = DirSep) then
  255. Dec (Dirs [0], 2);
  256. {Finally remove '.\' at the beginning of the string of directories...}
  257. while (Length (Dirs) >= 2) and (Dirs [1] = '.') and (Dirs [2] = DirSep) do
  258. Delete (Dirs, 1, 2);
  259. {...and possible (invalid) references to '..\' as well}
  260. while (Length (Dirs) >= 3) and (Dirs [1] = '.') and (Dirs [2] = '.') and
  261. (Dirs [3] = DirSep) do
  262. Delete (Dirs, 1, 3);
  263. {Two special cases - '.' and '..' alone}
  264. if (Length (Dirs) = 1) and (Dirs [1] = '.') or
  265. (Length (Dirs) = 2) and (Dirs [1] = '.') and (Dirs [2] = '.') then
  266. Dirs := '';
  267. {Join the parts back to create the complete path}
  268. if Length (Dirs) = 0 then
  269. begin
  270. Pa := Copy (Pa, 1, PathStart);
  271. if Pa [PathStart] <> DirSep then
  272. Pa := Pa + DirSep;
  273. end
  274. else
  275. Pa := Copy (Pa, 1, PathStart) + Dirs;
  276. {Remove ending \ if not supplied originally, the original string
  277. wasn't empty (to stay compatible) and if not really needed}
  278. if (Pa [Length (Pa)] = DirSep) and ((Length (Pa) > PathStart) or
  279. {A special case with UNC paths}
  280. (RootNotNeeded and (Length (Pa) = PathStart))) and
  281. (Length (Path) <> 0) and (Path [Length (Path)] <> DirSep) then
  282. Dec (Pa [0]);
  283. FExpand := Pa;
  284. end;
  285. {
  286. $Log$
  287. Revision 1.9 2002-03-03 15:19:36 carl
  288. * fixes unix conversion of slashes
  289. Revision 1.8 2001/04/09 19:28:40 hajny
  290. * yet another fix for FExpand under Unix
  291. Revision 1.5 2001/03/21 21:08:20 hajny
  292. * GetDir fixed
  293. Revision 1.4 2001/03/19 21:09:30 hajny
  294. * one more problem in the Unix part
  295. Revision 1.3 2001/03/19 21:05:42 hajny
  296. * mistyping in the Unix part fixed
  297. Revision 1.2 2001/03/10 09:57:51 hajny
  298. * FExpand without IOResult change, remaining direct asm removed
  299. }