fexpand.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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. {$IFDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
  33. GetDir (0, Dir);
  34. {$ELSE FPC_FEXPAND_NO_DEFAULT_PATHS}
  35. GetDir (VolumeName, Dir);
  36. {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
  37. InOutRes := OldInOutRes;
  38. end;
  39. {$ENDIF FPC_FEXPAND_VOLUMES}
  40. function FExpand (const Path: PathStr): PathStr;
  41. (* LFNSupport boolean constant, variable or function must be declared for all
  42. the platforms, at least locally in the Dos unit implementation part.
  43. In addition, FPC_FEXPAND_UNC, FPC_FEXPAND_DRIVES, FPC_FEXPAND_GETENV_PCHAR,
  44. FPC_FEXPAND_TILDE, FPC_FEXPAND_VOLUMES and FPC_FEXPAND_NO_DEFAULT_PATHS
  45. conditionals might be defined to specify FExpand behaviour.
  46. *)
  47. {$IFDEF FPC_FEXPAND_DRIVES}
  48. var
  49. PathStart: longint;
  50. {$ELSE FPC_FEXPAND_DRIVES}
  51. const
  52. PathStart = 1;
  53. {$ENDIF FPC_FEXPAND_DRIVES}
  54. {$IFDEF FPC_FEXPAND_UNC}
  55. var
  56. RootNotNeeded: boolean;
  57. {$ELSE FPC_FEXPAND_UNC}
  58. const
  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. if DirectorySeparator = '\' then
  72. {Allow slash as backslash}
  73. begin
  74. for I := 1 to Length (Pa) do
  75. if Pa [I] = '/' then
  76. Pa [I] := DirectorySeparator
  77. end
  78. else
  79. {Allow backslash as slash}
  80. begin
  81. for I := 1 to Length (Pa) do
  82. if Pa [I] = '\' then
  83. Pa [I] := DirectorySeparator;
  84. end;
  85. {$IFDEF FPC_FEXPAND_DRIVES}
  86. {$IFDEF FPC_FEXPAND_VOLUMES}
  87. {$IFDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
  88. PathStart := Pos (DriveSeparator, Pa);
  89. {$ELSE FPC_FEXPAND_NO_DEFAULT_PATHS}
  90. PathStart := Succ (Pos (DriveSeparator, Pa));
  91. {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
  92. {$ELSE FPC_FEXPAND_VOLUMES}
  93. PathStart := 3;
  94. {$ENDIF FPC_FEXPAND_VOLUMES}
  95. {$ENDIF FPC_FEXPAND_DRIVES}
  96. {$IFDEF FPC_FEXPAND_TILDE}
  97. {Replace ~/ with $HOME/}
  98. if (Length (Pa) >= 1) and (Pa [1] = '~') and
  99. ((Pa [2] = DirectorySeparator) or (Length (Pa) = 1)) then
  100. begin
  101. {$IFDEF FPC_FEXPAND_GETENV_PCHAR}
  102. S := StrPas (GetEnv ('HOME'));
  103. {$ELSE FPC_FEXPAND_GETENV_PCHAR}
  104. S := GetEnv ('HOME');
  105. {$ENDIF FPC_FEXPAND_GETENV_PCHAR}
  106. if (S = '') or (Length (S) = 1)
  107. and (S [1] = DirectorySeparator) then
  108. Delete (Pa, 1, 1)
  109. else
  110. if S [Length (S)] = DirectorySeparator then
  111. Pa := S + Copy (Pa, 3, Length (Pa) - 2)
  112. else
  113. Pa := S + Copy (Pa, 2, Pred (Length (Pa)));
  114. end;
  115. {$ENDIF FPC_FEXPAND_TILDE}
  116. {$IFDEF FPC_FEXPAND_VOLUMES}
  117. if PathStart > 1 then
  118. {$ELSE FPC_FEXPAND_VOLUMES}
  119. if (Length (Pa) > 1) and (Pa [1] in ['A'..'Z', 'a'..'z']) and
  120. (Pa [2] = DriveSeparator) then
  121. {$ENDIF FPC_FEXPAND_VOLUMES}
  122. begin
  123. {$IFDEF FPC_FEXPAND_DRIVES}
  124. {$IFNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
  125. {$IFDEF FPC_FEXPAND_VOLUMES}
  126. GetDirIO (Copy (Pa, 1, PathStart - 2), S);
  127. {$ELSE FPC_FEXPAND_VOLUMES}
  128. { Always uppercase driveletter }
  129. if (Pa [1] in ['a'..'z']) then
  130. Pa [1] := Chr (Ord (Pa [1]) and not ($20));
  131. GetDirIO (Ord (Pa [1]) - Ord ('A') + 1, S);
  132. {$ENDIF FPC_FEXPAND_VOLUMES}
  133. if Length (Pa) = Pred (PathStart) then
  134. Pa := S
  135. else
  136. if Pa [PathStart] <> DirectorySeparator then
  137. {$IFDEF FPC_FEXPAND_VOLUMES}
  138. if Copy (Pa, 1, PathStart - 2) = Copy (S, 1, PathStart - 2)
  139. then
  140. {$ELSE FPC_FEXPAND_VOLUMES}
  141. if Pa [1] = S [1] then
  142. {$ENDIF FPC_FEXPAND_VOLUMES}
  143. begin
  144. { remove ending slash if it already exists }
  145. if S [Length (S)] = DirectorySeparator then
  146. Dec (S [0]);
  147. Pa := S + DirectorySeparator +
  148. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  149. end
  150. else
  151. {$IFDEF FPC_FEXPAND_VOLUMES}
  152. Pa := Copy (Pa, 1, PathStart - 2) + DriveSeparator
  153. + DirectorySeparator +
  154. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  155. {$ELSE FPC_FEXPAND_VOLUMES}
  156. Pa := Pa [1] + DriveSeparator + DirectorySeparator +
  157. Copy (Pa, PathStart, Length (Pa) - PathStart + 1)
  158. {$ENDIF FPC_FEXPAND_VOLUMES}
  159. {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
  160. end
  161. else
  162. {$ELSE FPC_FEXPAND_DRIVES}
  163. Delete (Pa, 1, 2);
  164. end;
  165. {Check whether we don't have an absolute path already}
  166. if (Length (Pa) >= PathStart) and (Pa [PathStart] <> DirectorySeparator) or
  167. (Length (Pa) < PathStart) then
  168. {$ENDIF FPC_FEXPAND_DRIVES}
  169. begin
  170. GetDirIO (0, S);
  171. {$IFDEF FPC_FEXPAND_DRIVES}
  172. {$IFDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
  173. PathStart := Pos (DriveSeparator, S);
  174. {$ELSE FPC_FEXPAND_NO_DEFAULT_PATHS}
  175. if (Length (Pa) > 0) and (Pa [1] = DirectorySeparator) then
  176. begin
  177. {$IFDEF FPC_FEXPAND_UNC}
  178. {Do not touch network drive names}
  179. if (Length (Pa) > 1) and (Pa [2] = DirectorySeparator)
  180. and LFNSupport then
  181. begin
  182. PathStart := 3;
  183. {Find the start of the string of directories}
  184. while (PathStart <= Length (Pa)) and
  185. (Pa [PathStart] <> DirectorySeparator) do
  186. Inc (PathStart);
  187. if PathStart > Length (Pa) then
  188. {We have just a machine name...}
  189. if Length (Pa) = 2 then
  190. {...or not even that one}
  191. PathStart := 2
  192. else
  193. Pa := Pa + DirectorySeparator else
  194. if PathStart < Length (Pa) then
  195. {We have a resource name as well}
  196. begin
  197. RootNotNeeded := true;
  198. {Let's continue in searching}
  199. repeat
  200. Inc (PathStart);
  201. until (PathStart > Length (Pa)) or
  202. (Pa [PathStart] = DirectorySeparator);
  203. end;
  204. end
  205. else
  206. {$ENDIF FPC_FEXPAND_UNC}
  207. {$IFDEF FPC_FEXPAND_VOLUMES}
  208. begin
  209. I := Pos (DriveSeparator, S);
  210. Pa := Copy (S, 1, Pred (I)) + DriveSeparator + Pa;
  211. PathStart := Succ (I);
  212. end;
  213. {$ELSE FPC_FEXPAND_VOLUMES}
  214. Pa := S [1] + DriveSeparator + Pa;
  215. {$ENDIF FPC_FEXPAND_VOLUMES}
  216. end
  217. else
  218. {$ENDIF FPC_FEXPAND_NO_DEFAULT_PATHS}
  219. {$ENDIF FPC_FEXPAND_DRIVES}
  220. (* We already have a slash if root is the curent directory. *)
  221. if Length (S) = PathStart then
  222. Pa := S + Pa
  223. else
  224. (* We need an ending slash if FExpand was called
  225. with an empty string for compatibility. *)
  226. if Length (Pa) = 0 then
  227. Pa := S + DirectorySeparator
  228. else
  229. Pa := S + DirectorySeparator + Pa;
  230. end;
  231. {Get string of directories to only process relative references on this one}
  232. Dirs := Copy (Pa, Succ (PathStart), Length (Pa) - PathStart);
  233. {First remove all references to '\.\'}
  234. I := Pos (DirectorySeparator + '.' + DirectorySeparator, Dirs);
  235. while I <> 0 do
  236. begin
  237. Delete (Dirs, I, 2);
  238. I := Pos (DirectorySeparator + '.' + DirectorySeparator, Dirs);
  239. end;
  240. {Now remove also all references to '\..\' + of course previous dirs..}
  241. I := Pos (DirectorySeparator + '..' + DirectorySeparator, Dirs);
  242. while I <> 0 do
  243. begin
  244. J := Pred (I);
  245. while (J > 0) and (Dirs [J] <> DirectorySeparator) do
  246. Dec (J);
  247. Delete (Dirs, Succ (J), I - J + 3);
  248. I := Pos (DirectorySeparator + '..' + DirectorySeparator, Dirs);
  249. end;
  250. {Then remove also a reference to '\..' at the end of line
  251. + the previous directory, of course,...}
  252. I := Pos (DirectorySeparator + '..', Dirs);
  253. if (I <> 0) and (I = Length (Dirs) - 2) then
  254. begin
  255. J := Pred (I);
  256. while (J > 0) and (Dirs [J] <> DirectorySeparator) do
  257. Dec (J);
  258. if (J = 0) then
  259. Dirs := ''
  260. else
  261. Delete (Dirs, Succ (J), I - J + 2);
  262. end;
  263. {...and also a possible reference to '\.'}
  264. if (Length (Dirs) = 1) then
  265. begin
  266. if (Dirs [1] = '.') then
  267. {A special case}
  268. Dirs := ''
  269. end
  270. else
  271. if (Length (Dirs) <> 0) and (Dirs [Length (Dirs)] = '.') and
  272. (Dirs [Pred (Length (Dirs))] = DirectorySeparator) then
  273. Dec (Dirs [0], 2);
  274. {Finally remove '.\' at the beginning of the string of directories...}
  275. while (Length (Dirs) >= 2) and (Dirs [1] = '.')
  276. and (Dirs [2] = DirectorySeparator) do
  277. Delete (Dirs, 1, 2);
  278. {...and possible (invalid) references to '..\' as well}
  279. while (Length (Dirs) >= 3) and (Dirs [1] = '.') and (Dirs [2] = '.') and
  280. (Dirs [3] = DirectorySeparator) do
  281. Delete (Dirs, 1, 3);
  282. {Two special cases - '.' and '..' alone}
  283. if (Length (Dirs) = 1) and (Dirs [1] = '.') or
  284. (Length (Dirs) = 2) and (Dirs [1] = '.') and (Dirs [2] = '.') then
  285. Dirs := '';
  286. {Join the parts back to create the complete path}
  287. if Length (Dirs) = 0 then
  288. begin
  289. Pa := Copy (Pa, 1, PathStart);
  290. if Pa [PathStart] <> DirectorySeparator then
  291. Pa := Pa + DirectorySeparator;
  292. end
  293. else
  294. Pa := Copy (Pa, 1, PathStart) + Dirs;
  295. {Remove ending \ if not supplied originally, the original string
  296. wasn't empty (to stay compatible) and if not really needed}
  297. if (Pa [Length (Pa)] = DirectorySeparator)
  298. and ((Length (Pa) > PathStart) or
  299. {A special case with UNC paths}
  300. (RootNotNeeded and (Length (Pa) = PathStart))) and
  301. (Length (Path) <> 0)
  302. and (Path [Length (Path)] <> DirectorySeparator) then
  303. Dec (Pa [0]);
  304. FExpand := Pa;
  305. end;
  306. {
  307. $Log$
  308. Revision 1.13 2002-11-25 21:03:57 hajny
  309. * Amiga fixes (among others)
  310. Revision 1.12 2002/11/24 15:49:22 hajny
  311. * make use of constants available in the system unit
  312. Revision 1.11 2002/09/07 15:07:45 peter
  313. * old logs removed and tabs fixed
  314. Revision 1.10 2002/05/14 19:25:24 hajny
  315. * fix for bug 1964 merged
  316. Revision 1.9 2002/03/03 15:19:36 carl
  317. * fixes unix conversion of slashes
  318. }