cfileutl.pas 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. {
  2. Copyright (c) 1998-2002 by Florian Klaempfl and Peter Vreman
  3. This module provides some basic file/dir handling utils and classes
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  15. ****************************************************************************
  16. }
  17. unit cfileutl;
  18. {$i fpcdefs.inc}
  19. {$define usedircache}
  20. interface
  21. uses
  22. {$ifdef hasunix}
  23. Baseunix,unix,
  24. {$endif hasunix}
  25. {$ifdef win32}
  26. Windows,
  27. {$endif win32}
  28. {$if defined(go32v2) or defined(watcom)}
  29. Dos,
  30. {$endif}
  31. {$IFNDEF USE_FAKE_SYSUTILS}
  32. SysUtils,
  33. {$ELSE}
  34. fksysutl,
  35. {$ENDIF}
  36. GlobType,
  37. CUtils,CClasses,
  38. Systems;
  39. const
  40. { On case sensitive file systems, you have 9 lookups per used unit, }
  41. { including the system unit, in the current directory }
  42. MinSearchesBeforeCache = 20;
  43. type
  44. TCachedDirectory = class(TFPHashObject)
  45. private
  46. FDirectoryEntries : TFPHashList;
  47. FSearchCount: longint;
  48. procedure FreeDirectoryEntries;
  49. function GetItemAttr(const AName: TCmdStr): byte;
  50. function TryUseCache: boolean;
  51. procedure ForceUseCache;
  52. procedure Reload;
  53. public
  54. constructor Create(AList:TFPHashObjectList;const AName:TCmdStr);
  55. destructor destroy;override;
  56. function FileExists(const AName:TCmdStr):boolean;
  57. function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  58. function DirectoryExists(const AName:TCmdStr):boolean;
  59. property DirectoryEntries:TFPHashList read FDirectoryEntries;
  60. end;
  61. TCachedSearchRec = record
  62. Name : TCmdStr;
  63. Attr : byte;
  64. Pattern : TCmdStr;
  65. CachedDir : TCachedDirectory;
  66. EntryIndex : longint;
  67. end;
  68. PCachedDirectoryEntry = ^TCachedDirectoryEntry;
  69. TCachedDirectoryEntry = record
  70. RealName: TCmdStr;
  71. Attr : longint;
  72. end;
  73. TDirectoryCache = class
  74. private
  75. FDirectories : TFPHashObjectList;
  76. function GetDirectory(const ADir:TCmdStr):TCachedDirectory;
  77. public
  78. constructor Create;
  79. destructor destroy;override;
  80. function FileExists(const AName:TCmdStr):boolean;
  81. function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  82. function DirectoryExists(const AName:TCmdStr):boolean;
  83. function FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
  84. function FindNext(var Res:TCachedSearchRec):boolean;
  85. function FindClose(var Res:TCachedSearchRec):boolean;
  86. end;
  87. TSearchPathList = class(TCmdStrList)
  88. procedure AddPath(s:TCmdStr;addfirst:boolean);overload;
  89. procedure AddPath(SrcPath,s:TCmdStr;addfirst:boolean);overload;
  90. procedure AddList(list:TSearchPathList;addfirst:boolean);
  91. function FindFile(const f : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  92. end;
  93. function bstoslash(const s : TCmdStr) : TCmdStr;
  94. {Gives the absolute path to the current directory}
  95. function GetCurrentDir:TCmdStr;
  96. {Gives the relative path to the current directory,
  97. with a trailing dir separator. E. g. on unix ./ }
  98. function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
  99. function path_absolute(const s : TCmdStr) : boolean;
  100. Function PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  101. Function FileExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  102. function FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  103. Function RemoveDir(d:TCmdStr):boolean;
  104. Function FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
  105. function FixFileName(const s:TCmdStr):TCmdStr;
  106. function TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
  107. function TargetFixFileName(const s:TCmdStr):TCmdStr;
  108. procedure SplitBinCmd(const s:TCmdStr;var bstr: TCmdStr;var cstr:TCmdStr);
  109. function FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  110. { function FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;}
  111. function FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  112. function GetShortName(const n:TCmdStr):TCmdStr;
  113. procedure InitFileUtils;
  114. procedure DoneFileUtils;
  115. implementation
  116. uses
  117. Comphook,
  118. Globals;
  119. {$undef AllFilesMaskIsInRTL}
  120. {$if (FPC_VERSION > 2)}
  121. {$define AllFilesMaskIsInRTL}
  122. {$endif FPC_VERSION}
  123. {$if (FPC_VERSION = 2) and (FPC_RELEASE > 2)}
  124. {$define AllFilesMaskIsInRTL}
  125. {$endif}
  126. {$if (FPC_VERSION = 2) and (FPC_RELEASE = 2) and (FPC_PATCH > 0)}
  127. {$define AllFilesMaskIsInRTL}
  128. {$endif}
  129. {$ifndef AllFilesMaskIsInRTL}
  130. {$if defined(go32v2) or defined(watcom)}
  131. const
  132. AllFilesMask = '*.*';
  133. {$else}
  134. const
  135. AllFilesMask = '*';
  136. {$endif not (go32v2 or watcom)}
  137. {$endif not AllFilesMaskIsInRTL}
  138. var
  139. DirCache : TDirectoryCache;
  140. {****************************************************************************
  141. TCachedDirectory
  142. ****************************************************************************}
  143. constructor TCachedDirectory.create(AList:TFPHashObjectList;const AName:TCmdStr);
  144. begin
  145. inherited create(AList,AName);
  146. FDirectoryEntries:=TFPHashList.Create;
  147. end;
  148. destructor TCachedDirectory.destroy;
  149. begin
  150. FreeDirectoryEntries;
  151. FDirectoryEntries.Free;
  152. inherited destroy;
  153. end;
  154. function TCachedDirectory.TryUseCache:boolean;
  155. begin
  156. Result:=true;
  157. if (FSearchCount > MinSearchesBeforeCache) then
  158. exit;
  159. if (FSearchCount = MinSearchesBeforeCache) then
  160. begin
  161. inc(FSearchCount);
  162. Reload;
  163. exit;
  164. end;
  165. inc(FSearchCount);
  166. Result:=false;
  167. end;
  168. procedure TCachedDirectory.ForceUseCache;
  169. begin
  170. if (FSearchCount<=MinSearchesBeforeCache) then
  171. begin
  172. FSearchCount:=MinSearchesBeforeCache+1;
  173. Reload;
  174. end;
  175. end;
  176. procedure TCachedDirectory.FreeDirectoryEntries;
  177. var
  178. i: Integer;
  179. begin
  180. if not(tf_files_case_aware in source_info.flags) then
  181. exit;
  182. for i := 0 to DirectoryEntries.Count-1 do
  183. dispose(PCachedDirectoryEntry(DirectoryEntries[i]));
  184. end;
  185. function TCachedDirectory.GetItemAttr(const AName: TCmdStr): byte;
  186. var
  187. entry: PCachedDirectoryEntry;
  188. begin
  189. if not(tf_files_case_sensitive in source_info.flags) then
  190. if (tf_files_case_aware in source_info.flags) then
  191. begin
  192. entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(AName)));
  193. if assigned(entry) then
  194. Result:=entry^.Attr
  195. else
  196. Result:=0;
  197. end
  198. else
  199. Result:=PtrUInt(DirectoryEntries.Find(Lower(AName)))
  200. else
  201. Result:=PtrUInt(DirectoryEntries.Find(AName));
  202. end;
  203. procedure TCachedDirectory.Reload;
  204. var
  205. dir : TSearchRec;
  206. entry : PCachedDirectoryEntry;
  207. begin
  208. FreeDirectoryEntries;
  209. DirectoryEntries.Clear;
  210. if findfirst(IncludeTrailingPathDelimiter(Name)+AllFilesMask,faAnyFile or faDirectory,dir) = 0 then
  211. begin
  212. repeat
  213. if ((dir.attr and faDirectory)<>faDirectory) or
  214. ((dir.Name<>'.') and
  215. (dir.Name<>'..')) then
  216. begin
  217. { Force Archive bit so the attribute always has a value. This is needed
  218. to be able to see the difference in the directoryentries lookup if a file
  219. exists or not }
  220. Dir.Attr:=Dir.Attr or faArchive;
  221. if not(tf_files_case_sensitive in source_info.flags) then
  222. if (tf_files_case_aware in source_info.flags) then
  223. begin
  224. new(entry);
  225. entry^.RealName:=Dir.Name;
  226. entry^.Attr:=Dir.Attr;
  227. DirectoryEntries.Add(Lower(Dir.Name),entry)
  228. end
  229. else
  230. DirectoryEntries.Add(Lower(Dir.Name),Pointer(Ptrint(Dir.Attr)))
  231. else
  232. DirectoryEntries.Add(Dir.Name,Pointer(Ptrint(Dir.Attr)));
  233. end;
  234. until findnext(dir) <> 0;
  235. end;
  236. findclose(dir);
  237. end;
  238. function TCachedDirectory.FileExists(const AName:TCmdStr):boolean;
  239. var
  240. Attr : Longint;
  241. begin
  242. if not TryUseCache then
  243. begin
  244. { prepend directory name again }
  245. result:=cfileutl.FileExists(Name+AName,false);
  246. exit;
  247. end;
  248. Attr:=GetItemAttr(AName);
  249. if Attr<>0 then
  250. Result:=((Attr and faDirectory)=0)
  251. else
  252. Result:=false;
  253. end;
  254. function TCachedDirectory.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  255. var
  256. entry : PCachedDirectoryEntry;
  257. begin
  258. if (tf_files_case_aware in source_info.flags) then
  259. begin
  260. if not TryUseCache then
  261. begin
  262. Result:=FileExistsNonCase(path,fn,false,FoundName);
  263. exit;
  264. end;
  265. entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(ExtractFileName(fn))));
  266. if assigned(entry) and
  267. (entry^.Attr<>0) and
  268. ((entry^.Attr and faDirectory) = 0) then
  269. begin
  270. FoundName:=ExtractFilePath(path+fn)+entry^.RealName;
  271. Result:=true
  272. end
  273. else
  274. Result:=false;
  275. end
  276. else
  277. { should not be called in this case, use plain FileExists }
  278. Result:=False;
  279. end;
  280. function TCachedDirectory.DirectoryExists(const AName:TCmdStr):boolean;
  281. var
  282. Attr : Longint;
  283. begin
  284. if not TryUseCache then
  285. begin
  286. Result:=PathExists(Name+AName,false);
  287. exit;
  288. end;
  289. Attr:=GetItemAttr(AName);
  290. if Attr<>0 then
  291. Result:=((Attr and faDirectory)=faDirectory)
  292. else
  293. Result:=false;
  294. end;
  295. {****************************************************************************
  296. TDirectoryCache
  297. ****************************************************************************}
  298. constructor TDirectoryCache.create;
  299. begin
  300. inherited create;
  301. FDirectories:=TFPHashObjectList.Create(true);
  302. end;
  303. destructor TDirectoryCache.destroy;
  304. begin
  305. FDirectories.Free;
  306. inherited destroy;
  307. end;
  308. function TDirectoryCache.GetDirectory(const ADir:TCmdStr):TCachedDirectory;
  309. var
  310. CachedDir : TCachedDirectory;
  311. DirName : TCmdStr;
  312. begin
  313. if ADir='' then
  314. DirName:='.'+source_info.DirSep
  315. else
  316. DirName:=ADir;
  317. CachedDir:=TCachedDirectory(FDirectories.Find(DirName));
  318. if not assigned(CachedDir) then
  319. CachedDir:=TCachedDirectory.Create(FDirectories,DirName);
  320. Result:=CachedDir;
  321. end;
  322. function TDirectoryCache.FileExists(const AName:TCmdStr):boolean;
  323. var
  324. CachedDir : TCachedDirectory;
  325. begin
  326. Result:=false;
  327. CachedDir:=GetDirectory(ExtractFilePath(AName));
  328. if assigned(CachedDir) then
  329. Result:=CachedDir.FileExists(ExtractFileName(AName));
  330. end;
  331. function TDirectoryCache.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  332. var
  333. CachedDir : TCachedDirectory;
  334. begin
  335. Result:=false;
  336. CachedDir:=GetDirectory(ExtractFilePath(path+fn));
  337. if assigned(CachedDir) then
  338. Result:=CachedDir.FileExistsCaseAware(path,fn,FoundName);
  339. end;
  340. function TDirectoryCache.DirectoryExists(const AName:TCmdStr):boolean;
  341. var
  342. CachedDir : TCachedDirectory;
  343. begin
  344. Result:=false;
  345. CachedDir:=GetDirectory(ExtractFilePath(AName));
  346. if assigned(CachedDir) then
  347. Result:=CachedDir.DirectoryExists(ExtractFileName(AName));
  348. end;
  349. function TDirectoryCache.FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
  350. begin
  351. Res.Pattern:=ExtractFileName(APattern);
  352. Res.CachedDir:=GetDirectory(ExtractFilePath(APattern));
  353. Res.CachedDir.ForceUseCache;
  354. Res.EntryIndex:=0;
  355. if assigned(Res.CachedDir) then
  356. Result:=FindNext(Res)
  357. else
  358. Result:=false;
  359. end;
  360. function TDirectoryCache.FindNext(var Res:TCachedSearchRec):boolean;
  361. var
  362. entry: PCachedDirectoryEntry;
  363. begin
  364. if Res.EntryIndex<Res.CachedDir.DirectoryEntries.Count then
  365. begin
  366. if (tf_files_case_aware in source_info.flags) then
  367. begin
  368. entry:=Res.CachedDir.DirectoryEntries[Res.EntryIndex];
  369. Res.Name:=entry^.RealName;
  370. Res.Attr:=entry^.Attr;
  371. end
  372. else
  373. begin
  374. Res.Name:=Res.CachedDir.DirectoryEntries.NameOfIndex(Res.EntryIndex);
  375. Res.Attr:=PtrUInt(Res.CachedDir.DirectoryEntries[Res.EntryIndex]);
  376. end;
  377. inc(Res.EntryIndex);
  378. Result:=true;
  379. end
  380. else
  381. Result:=false;
  382. end;
  383. function TDirectoryCache.FindClose(var Res:TCachedSearchRec):boolean;
  384. begin
  385. { nothing todo }
  386. result:=true;
  387. end;
  388. {****************************************************************************
  389. Utils
  390. ****************************************************************************}
  391. function bstoslash(const s : TCmdStr) : TCmdStr;
  392. {
  393. return TCmdStr s with all \ changed into /
  394. }
  395. var
  396. i : longint;
  397. begin
  398. setlength(bstoslash,length(s));
  399. for i:=1to length(s) do
  400. if s[i]='\' then
  401. bstoslash[i]:='/'
  402. else
  403. bstoslash[i]:=s[i];
  404. end;
  405. {Gives the absolute path to the current directory}
  406. var
  407. CachedCurrentDir : TCmdStr;
  408. function GetCurrentDir:TCmdStr;
  409. begin
  410. if CachedCurrentDir='' then
  411. begin
  412. GetDir(0,CachedCurrentDir);
  413. CachedCurrentDir:=FixPath(CachedCurrentDir,false);
  414. end;
  415. result:=CachedCurrentDir;
  416. end;
  417. {Gives the relative path to the current directory,
  418. with a trailing dir separator. E. g. on unix ./ }
  419. function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
  420. begin
  421. if systeminfo.system <> system_powerpc_macos then
  422. CurDirRelPath:= '.'+systeminfo.DirSep
  423. else
  424. CurDirRelPath:= ':'
  425. end;
  426. function path_absolute(const s : TCmdStr) : boolean;
  427. {
  428. is path s an absolute path?
  429. }
  430. begin
  431. result:=false;
  432. {$if defined(unix)}
  433. if (length(s)>0) and (s[1] in AllowDirectorySeparators) then
  434. result:=true;
  435. {$elseif defined(amiga) or defined(morphos)}
  436. if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or (Pos(':',s) = length(s)) then
  437. result:=true;
  438. {$elseif defined(macos)}
  439. if IsMacFullPath(s) then
  440. result:=true;
  441. {$elseif defined(netware)}
  442. if (Pos (DriveSeparator, S) <> 0) or
  443. ((Length (S) > 0) and (S [1] in AllowDirectorySeparators)) then
  444. result:=true;
  445. {$elseif defined(win32) or defined(win64) or defined(go32v2) or defined(os2) or defined(watcom)}
  446. if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
  447. (* The following check for non-empty AllowDriveSeparators assumes that all
  448. other platforms supporting drives and not handled as exceptions above
  449. should work with DOS-like paths, i.e. use absolute paths with one letter
  450. for drive followed by path separator *)
  451. ((length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
  452. result:=true;
  453. {$else}
  454. if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
  455. (* The following check for non-empty AllowDriveSeparators assumes that all
  456. other platforms supporting drives and not handled as exceptions above
  457. should work with DOS-like paths, i.e. use absolute paths with one letter
  458. for drive followed by path separator *)
  459. ((AllowDriveSeparators <> []) and (length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
  460. result:=true;
  461. {$endif unix}
  462. end;
  463. Function FileExists ( Const F : TCmdStr;allowcache:boolean) : Boolean;
  464. begin
  465. {$ifdef usedircache}
  466. if allowcache then
  467. Result:=DirCache.FileExists(F)
  468. else
  469. {$endif usedircache}
  470. Result:=SysUtils.FileExists(F);
  471. if do_checkverbosity(V_Tried) then
  472. begin
  473. if Result then
  474. do_comment(V_Tried,'Searching file '+F+'... found')
  475. else
  476. do_comment(V_Tried,'Searching file '+F+'... not found');
  477. end;
  478. end;
  479. function FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  480. var
  481. fn2 : TCmdStr;
  482. begin
  483. result:=false;
  484. if tf_files_case_sensitive in source_info.flags then
  485. begin
  486. {
  487. Search order for case sensitive systems:
  488. 1. NormalCase
  489. 2. lowercase
  490. 3. UPPERCASE
  491. }
  492. FoundFile:=path+fn;
  493. If FileExists(FoundFile,allowcache) then
  494. begin
  495. result:=true;
  496. exit;
  497. end;
  498. fn2:=Lower(fn);
  499. if fn2<>fn then
  500. begin
  501. FoundFile:=path+fn2;
  502. If FileExists(FoundFile,allowcache) then
  503. begin
  504. result:=true;
  505. exit;
  506. end;
  507. end;
  508. fn2:=Upper(fn);
  509. if fn2<>fn then
  510. begin
  511. FoundFile:=path+fn2;
  512. If FileExists(FoundFile,allowcache) then
  513. begin
  514. result:=true;
  515. exit;
  516. end;
  517. end;
  518. end
  519. else
  520. if tf_files_case_aware in source_info.flags then
  521. begin
  522. {
  523. Search order for case aware systems:
  524. 1. NormalCase
  525. }
  526. {$ifdef usedircache}
  527. if allowcache then
  528. begin
  529. result:=DirCache.FileExistsCaseAware(path,fn,fn2);
  530. if result then
  531. begin
  532. FoundFile:=fn2;
  533. exit;
  534. end;
  535. end
  536. else
  537. {$endif usedircache}
  538. begin
  539. FoundFile:=path+fn;
  540. If FileExists(FoundFile,allowcache) then
  541. begin
  542. { don't know the real name in this case }
  543. result:=true;
  544. exit;
  545. end;
  546. end;
  547. end
  548. else
  549. begin
  550. { None case sensitive only lowercase }
  551. FoundFile:=path+Lower(fn);
  552. If FileExists(FoundFile,allowcache) then
  553. begin
  554. result:=true;
  555. exit;
  556. end;
  557. end;
  558. { Set foundfile to something usefull }
  559. FoundFile:=fn;
  560. end;
  561. Function PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  562. Var
  563. i: longint;
  564. hs : TCmdStr;
  565. begin
  566. if F = '' then
  567. begin
  568. result := true;
  569. exit;
  570. end;
  571. hs := ExpandFileName(F);
  572. I := Pos (DriveSeparator, hs);
  573. if (hs [Length (hs)] = DirectorySeparator) and
  574. (((I = 0) and (Length (hs) > 1)) or (I <> Length (hs) - 1)) then
  575. Delete (hs, Length (hs), 1);
  576. {$ifdef usedircache}
  577. if allowcache then
  578. Result:=DirCache.DirectoryExists(hs)
  579. else
  580. {$endif usedircache}
  581. Result:=SysUtils.DirectoryExists(hs);
  582. end;
  583. Function RemoveDir(d:TCmdStr):boolean;
  584. begin
  585. if d[length(d)]=source_info.DirSep then
  586. Delete(d,length(d),1);
  587. {$I-}
  588. rmdir(d);
  589. {$I+}
  590. RemoveDir:=(ioresult=0);
  591. end;
  592. Function FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
  593. var
  594. i, L : longint;
  595. P: PChar;
  596. begin
  597. Result := s;
  598. L := Length(Result);
  599. if L=0 then
  600. exit;
  601. { Fix separator }
  602. P := @Result[1];
  603. for i:=0 to L-1 do
  604. begin
  605. if p^ in ['/','\'] then
  606. p^:=source_info.DirSep;
  607. inc(p);
  608. end;
  609. { Fix ending / }
  610. if (L>0) and (Result[L]<>source_info.DirSep) and
  611. (Result[L]<>DriveSeparator) then
  612. Result:=Result+source_info.DirSep; { !still results in temp AnsiString }
  613. { Remove ./ }
  614. if (not allowdot) and ((Length(Result)=2) and (Result[1]='.') and (Result[2] = source_info.DirSep)) then
  615. begin
  616. Result:='';
  617. Exit;
  618. end;
  619. { return }
  620. if not ((tf_files_case_aware in source_info.flags) or
  621. (tf_files_case_sensitive in source_info.flags)) then
  622. Result := lower(Result);
  623. end;
  624. {Actually the version in macutils.pp could be used,
  625. but that would not work for crosscompiling, so this is a slightly modified
  626. version of it.}
  627. function TranslatePathToMac (const path: TCmdStr; mpw: Boolean): TCmdStr;
  628. function GetVolumeIdentifier: TCmdStr;
  629. begin
  630. GetVolumeIdentifier := '{Boot}'
  631. (*
  632. if mpw then
  633. GetVolumeIdentifier := '{Boot}'
  634. else
  635. GetVolumeIdentifier := macosBootVolumeName;
  636. *)
  637. end;
  638. var
  639. slashPos, oldpos, newpos, oldlen, maxpos: Longint;
  640. begin
  641. oldpos := 1;
  642. slashPos := Pos('/', path);
  643. if (slashPos <> 0) then {its a unix path}
  644. begin
  645. if slashPos = 1 then
  646. begin {its a full path}
  647. oldpos := 2;
  648. TranslatePathToMac := GetVolumeIdentifier;
  649. end
  650. else {its a partial path}
  651. TranslatePathToMac := ':';
  652. end
  653. else
  654. begin
  655. slashPos := Pos('\', path);
  656. if (slashPos <> 0) then {its a dos path}
  657. begin
  658. if slashPos = 1 then
  659. begin {its a full path, without drive letter}
  660. oldpos := 2;
  661. TranslatePathToMac := GetVolumeIdentifier;
  662. end
  663. else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
  664. begin
  665. oldpos := 4;
  666. TranslatePathToMac := GetVolumeIdentifier;
  667. end
  668. else {its a partial path}
  669. TranslatePathToMac := ':';
  670. end;
  671. end;
  672. if (slashPos <> 0) then {its a unix or dos path}
  673. begin
  674. {Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
  675. newpos := Length(TranslatePathToMac);
  676. oldlen := Length(path);
  677. SetLength(TranslatePathToMac, newpos + oldlen); {It will be no longer than what is already}
  678. {prepended plus length of path.}
  679. maxpos := Length(TranslatePathToMac); {Get real maxpos, can be short if String is ShortString}
  680. {There is never a slash in the beginning, because either it was an absolute path, and then the}
  681. {drive and slash was removed, or it was a relative path without a preceding slash.}
  682. while oldpos <= oldlen do
  683. begin
  684. {Check if special dirs, ./ or ../ }
  685. if path[oldPos] = '.' then
  686. if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
  687. begin
  688. if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
  689. begin
  690. {It is "../" or ".." translates to ":" }
  691. if newPos = maxPos then
  692. begin {Shouldn't actually happen, but..}
  693. Exit('');
  694. end;
  695. newPos := newPos + 1;
  696. TranslatePathToMac[newPos] := ':';
  697. oldPos := oldPos + 3;
  698. continue; {Start over again}
  699. end;
  700. end
  701. else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
  702. begin
  703. {It is "./" or "." ignor it }
  704. oldPos := oldPos + 2;
  705. continue; {Start over again}
  706. end;
  707. {Collect file or dir name}
  708. while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
  709. begin
  710. if newPos = maxPos then
  711. begin {Shouldn't actually happen, but..}
  712. Exit('');
  713. end;
  714. newPos := newPos + 1;
  715. TranslatePathToMac[newPos] := path[oldPos];
  716. oldPos := oldPos + 1;
  717. end;
  718. {When we come here there is either a slash or we are at the end.}
  719. if (oldpos <= oldlen) then
  720. begin
  721. if newPos = maxPos then
  722. begin {Shouldn't actually happen, but..}
  723. Exit('');
  724. end;
  725. newPos := newPos + 1;
  726. TranslatePathToMac[newPos] := ':';
  727. oldPos := oldPos + 1;
  728. end;
  729. end;
  730. SetLength(TranslatePathToMac, newpos);
  731. end
  732. else if (path = '.') then
  733. TranslatePathToMac := ':'
  734. else if (path = '..') then
  735. TranslatePathToMac := '::'
  736. else
  737. TranslatePathToMac := path; {its a mac path}
  738. end;
  739. function FixFileName(const s:TCmdStr):TCmdStr;
  740. var
  741. i : longint;
  742. begin
  743. if source_info.system = system_powerpc_MACOS then
  744. FixFileName:= TranslatePathToMac(s, true)
  745. else
  746. if (tf_files_case_aware in source_info.flags) or
  747. (tf_files_case_sensitive in source_info.flags) then
  748. begin
  749. setlength(FixFileName,length(s));
  750. for i:=1 to length(s) do
  751. begin
  752. case s[i] of
  753. '/','\' :
  754. FixFileName[i]:=source_info.dirsep;
  755. else
  756. FixFileName[i]:=s[i];
  757. end;
  758. end;
  759. end
  760. else
  761. begin
  762. setlength(FixFileName,length(s));
  763. for i:=1 to length(s) do
  764. begin
  765. case s[i] of
  766. '/','\' :
  767. FixFileName[i]:=source_info.dirsep;
  768. 'A'..'Z' :
  769. FixFileName[i]:=char(byte(s[i])+32);
  770. else
  771. FixFileName[i]:=s[i];
  772. end;
  773. end;
  774. end;
  775. end;
  776. Function TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
  777. var
  778. i : longint;
  779. begin
  780. { Fix separator }
  781. for i:=1 to length(s) do
  782. if s[i] in ['/','\'] then
  783. s[i]:=target_info.DirSep;
  784. { Fix ending / }
  785. if (length(s)>0) and (s[length(s)]<>target_info.DirSep) and
  786. (s[length(s)]<>':') then
  787. s:=s+target_info.DirSep;
  788. { Remove ./ }
  789. if (not allowdot) and (s='.'+target_info.DirSep) then
  790. s:='';
  791. { return }
  792. if (tf_files_case_aware in target_info.flags) or
  793. (tf_files_case_sensitive in target_info.flags) then
  794. TargetFixPath:=s
  795. else
  796. TargetFixPath:=Lower(s);
  797. end;
  798. function TargetFixFileName(const s:TCmdStr):TCmdStr;
  799. var
  800. i : longint;
  801. begin
  802. if target_info.system = system_powerpc_MACOS then
  803. TargetFixFileName:= TranslatePathToMac(s, true)
  804. else
  805. if (tf_files_case_aware in target_info.flags) or
  806. (tf_files_case_sensitive in target_info.flags) then
  807. begin
  808. setlength(TargetFixFileName,length(s));
  809. for i:=1 to length(s) do
  810. begin
  811. case s[i] of
  812. '/','\' :
  813. TargetFixFileName[i]:=target_info.dirsep;
  814. else
  815. TargetFixFileName[i]:=s[i];
  816. end;
  817. end;
  818. end
  819. else
  820. begin
  821. setlength(TargetFixFileName,length(s));
  822. for i:=1 to length(s) do
  823. begin
  824. case s[i] of
  825. '/','\' :
  826. TargetFixFileName[i]:=target_info.dirsep;
  827. 'A'..'Z' :
  828. TargetFixFileName[i]:=char(byte(s[i])+32);
  829. else
  830. TargetFixFileName[i]:=s[i];
  831. end;
  832. end;
  833. end;
  834. end;
  835. procedure SplitBinCmd(const s:TCmdStr;var bstr:TCmdStr;var cstr:TCmdStr);
  836. var
  837. i : longint;
  838. begin
  839. i:=pos(' ',s);
  840. if i>0 then
  841. begin
  842. bstr:=Copy(s,1,i-1);
  843. cstr:=Copy(s,i+1,length(s)-i);
  844. end
  845. else
  846. begin
  847. bstr:=s;
  848. cstr:='';
  849. end;
  850. end;
  851. procedure TSearchPathList.AddPath(s:TCmdStr;addfirst:boolean);
  852. begin
  853. AddPath('',s,AddFirst);
  854. end;
  855. procedure TSearchPathList.AddPath(SrcPath,s:TCmdStr;addfirst:boolean);
  856. var
  857. staridx,
  858. i,j : longint;
  859. prefix,
  860. suffix,
  861. CurrentDir,
  862. currPath : TCmdStr;
  863. subdirfound : boolean;
  864. {$ifdef usedircache}
  865. dir : TCachedSearchRec;
  866. {$else usedircache}
  867. dir : TSearchRec;
  868. {$endif usedircache}
  869. hp : TCmdStrListItem;
  870. procedure WarnNonExistingPath(const path : TCmdStr);
  871. begin
  872. if do_checkverbosity(V_Tried) then
  873. do_comment(V_Tried,'Path "'+path+'" not found');
  874. end;
  875. procedure AddCurrPath;
  876. begin
  877. if addfirst then
  878. begin
  879. Remove(currPath);
  880. Insert(currPath);
  881. end
  882. else
  883. begin
  884. { Check if already in path, then we don't add it }
  885. hp:=Find(currPath);
  886. if not assigned(hp) then
  887. Concat(currPath);
  888. end;
  889. end;
  890. begin
  891. if s='' then
  892. exit;
  893. { Support default macro's }
  894. DefaultReplacements(s);
  895. {$warnings off}
  896. if PathSeparator <> ';' then
  897. for i:=1 to length(s) do
  898. if s[i]=PathSeparator then
  899. s[i]:=';';
  900. {$warnings on}
  901. { get current dir }
  902. CurrentDir:=GetCurrentDir;
  903. repeat
  904. { get currpath }
  905. if addfirst then
  906. begin
  907. j:=length(s);
  908. while (j>0) and (s[j]<>';') do
  909. dec(j);
  910. currPath:= TrimSpace(Copy(s,j+1,length(s)-j));
  911. if j=0 then
  912. s:=''
  913. else
  914. System.Delete(s,j,length(s)-j+1);
  915. end
  916. else
  917. begin
  918. j:=Pos(';',s);
  919. if j=0 then
  920. j:=255;
  921. currPath:= TrimSpace(Copy(s,1,j-1));
  922. System.Delete(s,1,j);
  923. end;
  924. { fix pathname }
  925. DePascalQuote(currPath);
  926. currPath:=SrcPath+FixPath(currPath,false);
  927. if currPath='' then
  928. currPath:= CurDirRelPath(source_info)
  929. else
  930. begin
  931. currPath:=FixPath(ExpandFileName(currpath),false);
  932. if (CurrentDir<>'') and (Copy(currPath,1,length(CurrentDir))=CurrentDir) then
  933. begin
  934. {$if defined(amiga) and defined(morphos)}
  935. currPath:= CurrentDir+Copy(currPath,length(CurrentDir)+1,255);
  936. {$else}
  937. currPath:= CurDirRelPath(source_info)+Copy(currPath,length(CurrentDir)+1,255);
  938. {$endif}
  939. end;
  940. end;
  941. { wildcard adding ? }
  942. staridx:=pos('*',currpath);
  943. if staridx>0 then
  944. begin
  945. prefix:=ExtractFilePath(Copy(currpath,1,staridx));
  946. suffix:=Copy(currpath,staridx+1,length(currpath));
  947. subdirfound:=false;
  948. {$ifdef usedircache}
  949. if DirCache.FindFirst(Prefix+AllFilesMask,dir) then
  950. begin
  951. repeat
  952. if (dir.attr and faDirectory)<>0 then
  953. begin
  954. subdirfound:=true;
  955. currpath:=prefix+dir.name+suffix;
  956. if (suffix='') or PathExists(currpath,true) then
  957. begin
  958. hp:=Find(currPath);
  959. if not assigned(hp) then
  960. AddCurrPath;
  961. end;
  962. end;
  963. until not DirCache.FindNext(dir);
  964. end;
  965. DirCache.FindClose(dir);
  966. {$else usedircache}
  967. if findfirst(prefix+AllFilesMask,faDirectory,dir) = 0 then
  968. begin
  969. repeat
  970. if (dir.name<>'.') and
  971. (dir.name<>'..') and
  972. ((dir.attr and faDirectory)<>0) then
  973. begin
  974. subdirfound:=true;
  975. currpath:=prefix+dir.name+suffix;
  976. if (suffix='') or PathExists(currpath,false) then
  977. begin
  978. hp:=Find(currPath);
  979. if not assigned(hp) then
  980. AddCurrPath;
  981. end;
  982. end;
  983. until findnext(dir) <> 0;
  984. end;
  985. FindClose(dir);
  986. {$endif usedircache}
  987. if not subdirfound then
  988. WarnNonExistingPath(currpath);
  989. end
  990. else
  991. begin
  992. if PathExists(currpath,true) then
  993. AddCurrPath
  994. else
  995. WarnNonExistingPath(currpath);
  996. end;
  997. until (s='');
  998. end;
  999. procedure TSearchPathList.AddList(list:TSearchPathList;addfirst:boolean);
  1000. var
  1001. s : TCmdStr;
  1002. hl : TSearchPathList;
  1003. hp,hp2 : TCmdStrListItem;
  1004. begin
  1005. if list.empty then
  1006. exit;
  1007. { create temp and reverse the list }
  1008. if addfirst then
  1009. begin
  1010. hl:=TSearchPathList.Create;
  1011. hp:=TCmdStrListItem(list.first);
  1012. while assigned(hp) do
  1013. begin
  1014. hl.insert(hp.Str);
  1015. hp:=TCmdStrListItem(hp.next);
  1016. end;
  1017. while not hl.empty do
  1018. begin
  1019. s:=hl.GetFirst;
  1020. Remove(s);
  1021. Insert(s);
  1022. end;
  1023. hl.Free;
  1024. end
  1025. else
  1026. begin
  1027. hp:=TCmdStrListItem(list.first);
  1028. while assigned(hp) do
  1029. begin
  1030. hp2:=Find(hp.Str);
  1031. { Check if already in path, then we don't add it }
  1032. if not assigned(hp2) then
  1033. Concat(hp.Str);
  1034. hp:=TCmdStrListItem(hp.next);
  1035. end;
  1036. end;
  1037. end;
  1038. function TSearchPathList.FindFile(const f :TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1039. Var
  1040. p : TCmdStrListItem;
  1041. begin
  1042. FindFile:=false;
  1043. p:=TCmdStrListItem(first);
  1044. while assigned(p) do
  1045. begin
  1046. result:=FileExistsNonCase(p.Str,f,allowcache,FoundFile);
  1047. if result then
  1048. exit;
  1049. p:=TCmdStrListItem(p.next);
  1050. end;
  1051. { Return original filename if not found }
  1052. FoundFile:=f;
  1053. end;
  1054. function FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1055. Var
  1056. StartPos, EndPos, L: LongInt;
  1057. begin
  1058. Result:=False;
  1059. StartPos := 1;
  1060. L := Length(Path);
  1061. repeat
  1062. EndPos := StartPos;
  1063. while (EndPos <= L) and ((Path[EndPos] <> PathSeparator) and (Path[EndPos] <> ';')) do
  1064. Inc(EndPos);
  1065. Result := FileExistsNonCase(FixPath(Copy(Path, StartPos, EndPos-StartPos), False), f, allowcache, FoundFile);
  1066. if Result then
  1067. Exit;
  1068. StartPos := EndPos + 1;
  1069. until StartPos > L;
  1070. FoundFile:=f;
  1071. end;
  1072. {
  1073. function FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1074. Var
  1075. singlepathstring : TCmdStr;
  1076. startpc,pc : pchar;
  1077. begin
  1078. FindFilePchar:=false;
  1079. if Assigned (Path) then
  1080. begin
  1081. pc:=path;
  1082. repeat
  1083. startpc:=pc;
  1084. while (pc^<>PathSeparator) and (pc^<>';') and (pc^<>#0) do
  1085. inc(pc);
  1086. SetLength(singlepathstring, pc-startpc);
  1087. move(startpc^,singlepathstring[1],pc-startpc);
  1088. singlepathstring:=FixPath(ExpandFileName(singlepathstring),false);
  1089. result:=FileExistsNonCase(singlepathstring,f,allowcache,FoundFile);
  1090. if result then
  1091. exit;
  1092. if (pc^=#0) then
  1093. break;
  1094. inc(pc);
  1095. until false;
  1096. end;
  1097. foundfile:=f;
  1098. end;
  1099. }
  1100. function FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1101. var
  1102. Path : TCmdStr;
  1103. found : boolean;
  1104. begin
  1105. found:=FindFile(FixFileName(ChangeFileExt(bin,source_info.exeext)),'.;'+exepath,allowcache,foundfile);
  1106. if not found then
  1107. begin
  1108. {$ifdef macos}
  1109. Path:=GetEnvironmentVariable('Commands');
  1110. {$else}
  1111. Path:=GetEnvironmentVariable('PATH');
  1112. {$endif}
  1113. found:=FindFile(FixFileName(ChangeFileExt(bin,source_info.exeext)),Path,allowcache,foundfile);
  1114. end;
  1115. FindExe:=found;
  1116. end;
  1117. function GetShortName(const n:TCmdStr):TCmdStr;
  1118. {$ifdef win32}
  1119. var
  1120. hs,hs2 : TCmdStr;
  1121. i : longint;
  1122. {$endif}
  1123. {$if defined(go32v2) or defined(watcom)}
  1124. var
  1125. hs : shortstring;
  1126. {$endif}
  1127. begin
  1128. GetShortName:=n;
  1129. {$ifdef win32}
  1130. hs:=n+#0;
  1131. { may become longer in case of e.g. ".a" -> "a~1" or so }
  1132. setlength(hs2,length(hs)*2);
  1133. i:=Windows.GetShortPathName(@hs[1],@hs2[1],length(hs)*2);
  1134. if (i>0) and (i<=length(hs)*2) then
  1135. begin
  1136. setlength(hs2,strlen(@hs2[1]));
  1137. GetShortName:=hs2;
  1138. end;
  1139. {$endif}
  1140. {$if defined(go32v2) or defined(watcom)}
  1141. hs:=n;
  1142. if Dos.GetShortName(hs) then
  1143. GetShortName:=hs;
  1144. {$endif}
  1145. end;
  1146. {****************************************************************************
  1147. Init / Done
  1148. ****************************************************************************}
  1149. procedure InitFileUtils;
  1150. begin
  1151. DirCache:=TDirectoryCache.Create;
  1152. end;
  1153. procedure DoneFileUtils;
  1154. begin
  1155. DirCache.Free;
  1156. end;
  1157. end.