fexpand.inc 12 KB

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