cfileutils.pas 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083
  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 cfileutils;
  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. type
  40. TCachedDirectory = class(TFPHashObject)
  41. private
  42. FDirectoryEntries : TFPHashList;
  43. public
  44. constructor Create(AList:TFPHashObjectList;const AName:string);
  45. destructor destroy;override;
  46. procedure Reload;
  47. function FileExists(const AName:string):boolean;
  48. function DirectoryExists(const AName:string):boolean;
  49. property DirectoryEntries:TFPHashList read FDirectoryEntries;
  50. end;
  51. TCachedSearchRec = record
  52. Name : string;
  53. Attr : byte;
  54. Pattern : string;
  55. CachedDir : TCachedDirectory;
  56. EntryIndex : longint;
  57. end;
  58. TDirectoryCache = class
  59. private
  60. FDirectories : TFPHashObjectList;
  61. function GetDirectory(const ADir:string):TCachedDirectory;
  62. public
  63. constructor Create;
  64. destructor destroy;override;
  65. function FileExists(const AName:string):boolean;
  66. function DirectoryExists(const AName:string):boolean;
  67. function FindFirst(const APattern:string;var Res:TCachedSearchRec):boolean;
  68. function FindNext(var Res:TCachedSearchRec):boolean;
  69. function FindClose(var Res:TCachedSearchRec):boolean;
  70. end;
  71. TSearchPathList = class(TStringList)
  72. procedure AddPath(s:string;addfirst:boolean);overload;
  73. procedure AddPath(SrcPath,s:string;addfirst:boolean);overload;
  74. procedure AddList(list:TSearchPathList;addfirst:boolean);
  75. function FindFile(const f : string;allowcache:boolean;var foundfile:string):boolean;
  76. end;
  77. function bstoslash(const s : string) : string;
  78. {Gives the absolute path to the current directory}
  79. function GetCurrentDir:string;
  80. {Gives the relative path to the current directory,
  81. with a trailing dir separator. E. g. on unix ./ }
  82. function CurDirRelPath(systeminfo: tsysteminfo): string;
  83. function path_absolute(const s : string) : boolean;
  84. Function PathExists (const F : String;allowcache:boolean) : Boolean;
  85. Function FileExists (const F : String;allowcache:boolean) : Boolean;
  86. function FileExistsNonCase(const path,fn:string;allowcache:boolean;var foundfile:string):boolean;
  87. Function RemoveDir(d:string):boolean;
  88. Function FixPath(s:string;allowdot:boolean):string;
  89. function FixFileName(const s:string):string;
  90. function TargetFixPath(s:string;allowdot:boolean):string;
  91. function TargetFixFileName(const s:string):string;
  92. procedure SplitBinCmd(const s:string;var bstr: String;var cstr:TCmdStr);
  93. function FindFile(const f : string;path : string;allowcache:boolean;var foundfile:string):boolean;
  94. function FindFilePchar(const f : string;path : pchar;allowcache:boolean;var foundfile:string):boolean;
  95. function FindExe(const bin:string;allowcache:boolean;var foundfile:string):boolean;
  96. function GetShortName(const n:string):string;
  97. procedure InitFileUtils;
  98. procedure DoneFileUtils;
  99. implementation
  100. uses
  101. Comphook,
  102. Globals;
  103. var
  104. DirCache : TDirectoryCache;
  105. {****************************************************************************
  106. TCachedDirectory
  107. ****************************************************************************}
  108. constructor TCachedDirectory.create(AList:TFPHashObjectList;const AName:string);
  109. begin
  110. inherited create(AList,AName);
  111. FDirectoryEntries:=TFPHashList.Create;
  112. end;
  113. destructor TCachedDirectory.destroy;
  114. begin
  115. FDirectoryEntries.Free;
  116. inherited destroy;
  117. end;
  118. procedure TCachedDirectory.Reload;
  119. var
  120. dir : TSearchRec;
  121. begin
  122. DirectoryEntries.Clear;
  123. if findfirst(IncludeTrailingPathDelimiter(Name)+'*',faAnyFile or faDirectory,dir) = 0 then
  124. begin
  125. repeat
  126. if ((dir.attr and faDirectory)<>faDirectory) or
  127. (dir.Name<>'.') or
  128. (dir.Name<>'..') then
  129. begin
  130. if not(tf_files_case_sensitive in source_info.flags) then
  131. DirectoryEntries.Add(Lower(Dir.Name),Pointer(Ptrint(Dir.Attr)))
  132. else
  133. DirectoryEntries.Add(Dir.Name,Pointer(Ptrint(Dir.Attr)));
  134. end;
  135. until findnext(dir) <> 0;
  136. end;
  137. end;
  138. function TCachedDirectory.FileExists(const AName:string):boolean;
  139. var
  140. Attr : Longint;
  141. begin
  142. if not(tf_files_case_sensitive in source_info.flags) then
  143. Attr:=PtrInt(DirectoryEntries.Find(Lower(AName)))
  144. else
  145. Attr:=PtrInt(DirectoryEntries.Find(AName));
  146. if Attr<>0 then
  147. Result:=((Attr and faDirectory)=0)
  148. else
  149. Result:=false;
  150. end;
  151. function TCachedDirectory.DirectoryExists(const AName:string):boolean;
  152. var
  153. Attr : Longint;
  154. begin
  155. if not(tf_files_case_sensitive in source_info.flags) then
  156. Attr:=PtrInt(DirectoryEntries.Find(Lower(AName)))
  157. else
  158. Attr:=PtrInt(DirectoryEntries.Find(AName));
  159. if Attr<>0 then
  160. Result:=((Attr and faDirectory)=faDirectory)
  161. else
  162. Result:=false;
  163. end;
  164. {****************************************************************************
  165. TDirectoryCache
  166. ****************************************************************************}
  167. constructor TDirectoryCache.create;
  168. begin
  169. inherited create;
  170. FDirectories:=TFPHashObjectList.Create(true);
  171. end;
  172. destructor TDirectoryCache.destroy;
  173. begin
  174. FDirectories.Free;
  175. inherited destroy;
  176. end;
  177. function TDirectoryCache.GetDirectory(const ADir:string):TCachedDirectory;
  178. var
  179. CachedDir : TCachedDirectory;
  180. DirName : string;
  181. begin
  182. if ADir='' then
  183. DirName:='.'
  184. else
  185. DirName:=ADir;
  186. CachedDir:=TCachedDirectory(FDirectories.Find(DirName));
  187. if not assigned(CachedDir) then
  188. begin
  189. CachedDir:=TCachedDirectory.Create(FDirectories,DirName);
  190. CachedDir.Reload;
  191. end;
  192. Result:=CachedDir;
  193. end;
  194. function TDirectoryCache.FileExists(const AName:string):boolean;
  195. var
  196. CachedDir : TCachedDirectory;
  197. begin
  198. Result:=false;
  199. CachedDir:=GetDirectory(ExtractFileDir(AName));
  200. if assigned(CachedDir) then
  201. Result:=CachedDir.FileExists(ExtractFileName(AName));
  202. end;
  203. function TDirectoryCache.DirectoryExists(const AName:string):boolean;
  204. var
  205. CachedDir : TCachedDirectory;
  206. begin
  207. Result:=false;
  208. CachedDir:=GetDirectory(ExtractFilePath(AName));
  209. if assigned(CachedDir) then
  210. Result:=CachedDir.DirectoryExists(ExtractFileName(AName));
  211. end;
  212. function TDirectoryCache.FindFirst(const APattern:string;var Res:TCachedSearchRec):boolean;
  213. begin
  214. Res.Pattern:=ExtractFileName(APattern);
  215. Res.CachedDir:=GetDirectory(ExtractFilePath(APattern));
  216. Res.EntryIndex:=0;
  217. if assigned(Res.CachedDir) then
  218. Result:=FindNext(Res)
  219. else
  220. Result:=false;
  221. end;
  222. function TDirectoryCache.FindNext(var Res:TCachedSearchRec):boolean;
  223. begin
  224. if Res.EntryIndex<Res.CachedDir.DirectoryEntries.Count then
  225. begin
  226. Res.Name:=Res.CachedDir.DirectoryEntries.NameOfIndex(Res.EntryIndex);
  227. Res.Attr:=PtrInt(Res.CachedDir.DirectoryEntries[Res.EntryIndex]);
  228. inc(Res.EntryIndex);
  229. Result:=true;
  230. end
  231. else
  232. Result:=false;
  233. end;
  234. function TDirectoryCache.FindClose(var Res:TCachedSearchRec):boolean;
  235. begin
  236. { nothing todo }
  237. result:=true;
  238. end;
  239. {****************************************************************************
  240. Utils
  241. ****************************************************************************}
  242. function bstoslash(const s : string) : string;
  243. {
  244. return string s with all \ changed into /
  245. }
  246. var
  247. i : longint;
  248. begin
  249. for i:=1to length(s) do
  250. if s[i]='\' then
  251. bstoslash[i]:='/'
  252. else
  253. bstoslash[i]:=s[i];
  254. bstoslash[0]:=s[0];
  255. end;
  256. {Gives the absolute path to the current directory}
  257. var
  258. CachedCurrentDir : string;
  259. function GetCurrentDir:string;
  260. begin
  261. if CachedCurrentDir='' then
  262. begin
  263. GetDir(0,CachedCurrentDir);
  264. CachedCurrentDir:=FixPath(CachedCurrentDir,false);
  265. end;
  266. result:=CachedCurrentDir;
  267. end;
  268. {Gives the relative path to the current directory,
  269. with a trailing dir separator. E. g. on unix ./ }
  270. function CurDirRelPath(systeminfo: tsysteminfo): string;
  271. begin
  272. if systeminfo.system <> system_powerpc_macos then
  273. CurDirRelPath:= '.'+systeminfo.DirSep
  274. else
  275. CurDirRelPath:= ':'
  276. end;
  277. function path_absolute(const s : string) : boolean;
  278. {
  279. is path s an absolute path?
  280. }
  281. begin
  282. result:=false;
  283. {$if defined(unix)}
  284. if (length(s)>0) and (s[1]='/') then
  285. result:=true;
  286. {$elseif defined(amiga)}
  287. if ((length(s)>0) and ((s[1]='\') or (s[1]='/'))) or (Pos(':',s) = length(s)) then
  288. result:=true;
  289. {$elseif defined(macos)}
  290. if IsMacFullPath(s) then
  291. result:=true;
  292. if ((length(s)>0) and ((s[1]='\') or (s[1]='/'))) or
  293. ((length(s)>2) and (s[2]=':') and ((s[3]='\') or (s[3]='/'))) then
  294. result:=true;
  295. {$endif unix}
  296. end;
  297. Function FileExists ( Const F : String;allowcache:boolean) : Boolean;
  298. begin
  299. {$ifdef usedircache}
  300. if allowcache then
  301. Result:=DirCache.FileExists(F)
  302. else
  303. {$endif usedircache}
  304. Result:=SysUtils.FileExists(F);
  305. if assigned(do_comment) then
  306. begin
  307. if Result then
  308. do_comment(V_Tried,'Searching file '+F+'... found')
  309. else
  310. do_comment(V_Tried,'Searching file '+F+'... not found');
  311. end;
  312. end;
  313. function FileExistsNonCase(const path,fn:string;allowcache:boolean;var foundfile:string):boolean;
  314. var
  315. fn2 : string;
  316. begin
  317. result:=false;
  318. if tf_files_case_sensitive in source_info.flags then
  319. begin
  320. {
  321. Search order for case sensitive systems:
  322. 1. NormalCase
  323. 2. lowercase
  324. 3. UPPERCASE
  325. }
  326. FoundFile:=path+fn;
  327. If FileExists(FoundFile,allowcache) then
  328. begin
  329. result:=true;
  330. exit;
  331. end;
  332. fn2:=Lower(fn);
  333. if fn2<>fn then
  334. begin
  335. FoundFile:=path+fn2;
  336. If FileExists(FoundFile,allowcache) then
  337. begin
  338. result:=true;
  339. exit;
  340. end;
  341. end;
  342. fn2:=Upper(fn);
  343. if fn2<>fn then
  344. begin
  345. FoundFile:=path+fn2;
  346. If FileExists(FoundFile,allowcache) then
  347. begin
  348. result:=true;
  349. exit;
  350. end;
  351. end;
  352. end
  353. else
  354. if tf_files_case_aware in source_info.flags then
  355. begin
  356. {
  357. Search order for case aware systems:
  358. 1. NormalCase
  359. }
  360. FoundFile:=path+fn;
  361. If FileExists(FoundFile,allowcache) then
  362. begin
  363. result:=true;
  364. exit;
  365. end;
  366. end
  367. else
  368. begin
  369. { None case sensitive only lowercase }
  370. FoundFile:=path+Lower(fn);
  371. If FileExists(FoundFile,allowcache) then
  372. begin
  373. result:=true;
  374. exit;
  375. end;
  376. end;
  377. { Set foundfile to something usefull }
  378. FoundFile:=fn;
  379. end;
  380. Function PathExists (const F : String;allowcache:boolean) : Boolean;
  381. Var
  382. i: longint;
  383. hs : string;
  384. begin
  385. if F = '' then
  386. begin
  387. result := true;
  388. exit;
  389. end;
  390. hs := ExpandFileName(F);
  391. I := Pos (DriveSeparator, hs);
  392. if (hs [Length (hs)] = DirectorySeparator) and
  393. (((I = 0) and (Length (hs) > 1)) or (I <> Length (hs) - 1)) then
  394. Delete (hs, Length (hs), 1);
  395. {$ifdef usedircache}
  396. if allowcache then
  397. Result:=DirCache.DirectoryExists(hs)
  398. else
  399. {$endif usedircache}
  400. Result:=SysUtils.DirectoryExists(hs);
  401. end;
  402. Function RemoveDir(d:string):boolean;
  403. begin
  404. if d[length(d)]=source_info.DirSep then
  405. Delete(d,length(d),1);
  406. {$I-}
  407. rmdir(d);
  408. {$I+}
  409. RemoveDir:=(ioresult=0);
  410. end;
  411. Function FixPath(s:string;allowdot:boolean):string;
  412. var
  413. i : longint;
  414. begin
  415. { Fix separator }
  416. for i:=1 to length(s) do
  417. if s[i] in ['/','\'] then
  418. s[i]:=source_info.DirSep;
  419. { Fix ending / }
  420. if (length(s)>0) and (s[length(s)]<>source_info.DirSep) and
  421. (s[length(s)]<>':') then
  422. s:=s+source_info.DirSep;
  423. { Remove ./ }
  424. if (not allowdot) and (s='.'+source_info.DirSep) then
  425. s:='';
  426. { return }
  427. if (tf_files_case_aware in source_info.flags) or
  428. (tf_files_case_sensitive in source_info.flags) then
  429. FixPath:=s
  430. else
  431. FixPath:=Lower(s);
  432. end;
  433. {Actually the version in macutils.pp could be used,
  434. but that would not work for crosscompiling, so this is a slightly modified
  435. version of it.}
  436. function TranslatePathToMac (const path: string; mpw: Boolean): string;
  437. function GetVolumeIdentifier: string;
  438. begin
  439. GetVolumeIdentifier := '{Boot}'
  440. (*
  441. if mpw then
  442. GetVolumeIdentifier := '{Boot}'
  443. else
  444. GetVolumeIdentifier := macosBootVolumeName;
  445. *)
  446. end;
  447. var
  448. slashPos, oldpos, newpos, oldlen, maxpos: Longint;
  449. begin
  450. oldpos := 1;
  451. slashPos := Pos('/', path);
  452. if (slashPos <> 0) then {its a unix path}
  453. begin
  454. if slashPos = 1 then
  455. begin {its a full path}
  456. oldpos := 2;
  457. TranslatePathToMac := GetVolumeIdentifier;
  458. end
  459. else {its a partial path}
  460. TranslatePathToMac := ':';
  461. end
  462. else
  463. begin
  464. slashPos := Pos('\', path);
  465. if (slashPos <> 0) then {its a dos path}
  466. begin
  467. if slashPos = 1 then
  468. begin {its a full path, without drive letter}
  469. oldpos := 2;
  470. TranslatePathToMac := GetVolumeIdentifier;
  471. end
  472. else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
  473. begin
  474. oldpos := 4;
  475. TranslatePathToMac := GetVolumeIdentifier;
  476. end
  477. else {its a partial path}
  478. TranslatePathToMac := ':';
  479. end;
  480. end;
  481. if (slashPos <> 0) then {its a unix or dos path}
  482. begin
  483. {Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
  484. newpos := Length(TranslatePathToMac);
  485. oldlen := Length(path);
  486. SetLength(TranslatePathToMac, newpos + oldlen); {It will be no longer than what is already}
  487. {prepended plus length of path.}
  488. maxpos := Length(TranslatePathToMac); {Get real maxpos, can be short if String is ShortString}
  489. {There is never a slash in the beginning, because either it was an absolute path, and then the}
  490. {drive and slash was removed, or it was a relative path without a preceding slash.}
  491. while oldpos <= oldlen do
  492. begin
  493. {Check if special dirs, ./ or ../ }
  494. if path[oldPos] = '.' then
  495. if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
  496. begin
  497. if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
  498. begin
  499. {It is "../" or ".." translates to ":" }
  500. if newPos = maxPos then
  501. begin {Shouldn't actually happen, but..}
  502. Exit('');
  503. end;
  504. newPos := newPos + 1;
  505. TranslatePathToMac[newPos] := ':';
  506. oldPos := oldPos + 3;
  507. continue; {Start over again}
  508. end;
  509. end
  510. else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
  511. begin
  512. {It is "./" or "." ignor it }
  513. oldPos := oldPos + 2;
  514. continue; {Start over again}
  515. end;
  516. {Collect file or dir name}
  517. while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
  518. begin
  519. if newPos = maxPos then
  520. begin {Shouldn't actually happen, but..}
  521. Exit('');
  522. end;
  523. newPos := newPos + 1;
  524. TranslatePathToMac[newPos] := path[oldPos];
  525. oldPos := oldPos + 1;
  526. end;
  527. {When we come here there is either a slash or we are at the end.}
  528. if (oldpos <= oldlen) then
  529. begin
  530. if newPos = maxPos then
  531. begin {Shouldn't actually happen, but..}
  532. Exit('');
  533. end;
  534. newPos := newPos + 1;
  535. TranslatePathToMac[newPos] := ':';
  536. oldPos := oldPos + 1;
  537. end;
  538. end;
  539. SetLength(TranslatePathToMac, newpos);
  540. end
  541. else if (path = '.') then
  542. TranslatePathToMac := ':'
  543. else if (path = '..') then
  544. TranslatePathToMac := '::'
  545. else
  546. TranslatePathToMac := path; {its a mac path}
  547. end;
  548. function FixFileName(const s:string):string;
  549. var
  550. i : longint;
  551. begin
  552. if source_info.system = system_powerpc_MACOS then
  553. FixFileName:= TranslatePathToMac(s, true)
  554. else
  555. if (tf_files_case_aware in source_info.flags) or
  556. (tf_files_case_sensitive in source_info.flags) then
  557. begin
  558. for i:=1 to length(s) do
  559. begin
  560. case s[i] of
  561. '/','\' :
  562. FixFileName[i]:=source_info.dirsep;
  563. else
  564. FixFileName[i]:=s[i];
  565. end;
  566. end;
  567. FixFileName[0]:=s[0];
  568. end
  569. else
  570. begin
  571. for i:=1 to length(s) do
  572. begin
  573. case s[i] of
  574. '/','\' :
  575. FixFileName[i]:=source_info.dirsep;
  576. 'A'..'Z' :
  577. FixFileName[i]:=char(byte(s[i])+32);
  578. else
  579. FixFileName[i]:=s[i];
  580. end;
  581. end;
  582. FixFileName[0]:=s[0];
  583. end;
  584. end;
  585. Function TargetFixPath(s:string;allowdot:boolean):string;
  586. var
  587. i : longint;
  588. begin
  589. { Fix separator }
  590. for i:=1 to length(s) do
  591. if s[i] in ['/','\'] then
  592. s[i]:=target_info.DirSep;
  593. { Fix ending / }
  594. if (length(s)>0) and (s[length(s)]<>target_info.DirSep) and
  595. (s[length(s)]<>':') then
  596. s:=s+target_info.DirSep;
  597. { Remove ./ }
  598. if (not allowdot) and (s='.'+target_info.DirSep) then
  599. s:='';
  600. { return }
  601. if (tf_files_case_aware in target_info.flags) or
  602. (tf_files_case_sensitive in target_info.flags) then
  603. TargetFixPath:=s
  604. else
  605. TargetFixPath:=Lower(s);
  606. end;
  607. function TargetFixFileName(const s:string):string;
  608. var
  609. i : longint;
  610. begin
  611. if target_info.system = system_powerpc_MACOS then
  612. TargetFixFileName:= TranslatePathToMac(s, true)
  613. else
  614. if (tf_files_case_aware in target_info.flags) or
  615. (tf_files_case_sensitive in target_info.flags) then
  616. begin
  617. for i:=1 to length(s) do
  618. begin
  619. case s[i] of
  620. '/','\' :
  621. TargetFixFileName[i]:=target_info.dirsep;
  622. else
  623. TargetFixFileName[i]:=s[i];
  624. end;
  625. end;
  626. TargetFixFileName[0]:=s[0];
  627. end
  628. else
  629. begin
  630. for i:=1 to length(s) do
  631. begin
  632. case s[i] of
  633. '/','\' :
  634. TargetFixFileName[i]:=target_info.dirsep;
  635. 'A'..'Z' :
  636. TargetFixFileName[i]:=char(byte(s[i])+32);
  637. else
  638. TargetFixFileName[i]:=s[i];
  639. end;
  640. end;
  641. TargetFixFileName[0]:=s[0];
  642. end;
  643. end;
  644. procedure SplitBinCmd(const s:string;var bstr:String;var cstr:TCmdStr);
  645. var
  646. i : longint;
  647. begin
  648. i:=pos(' ',s);
  649. if i>0 then
  650. begin
  651. bstr:=Copy(s,1,i-1);
  652. cstr:=Copy(s,i+1,length(s)-i);
  653. end
  654. else
  655. begin
  656. bstr:=s;
  657. cstr:='';
  658. end;
  659. end;
  660. procedure TSearchPathList.AddPath(s:string;addfirst:boolean);
  661. begin
  662. AddPath('',s,AddFirst);
  663. end;
  664. procedure TSearchPathList.AddPath(SrcPath,s:string;addfirst:boolean);
  665. var
  666. staridx,
  667. j : longint;
  668. prefix,
  669. suffix,
  670. CurrentDir,
  671. currPath : string;
  672. subdirfound : boolean;
  673. {$ifdef usedircache}
  674. dir : TCachedSearchRec;
  675. {$else usedircache}
  676. dir : TSearchRec;
  677. {$endif usedircache}
  678. hp : TStringListItem;
  679. procedure WarnNonExistingPath(const path : string);
  680. begin
  681. if assigned(do_comment) then
  682. do_comment(V_Tried,'Path "'+path+'" not found');
  683. end;
  684. procedure AddCurrPath;
  685. begin
  686. if addfirst then
  687. begin
  688. Remove(currPath);
  689. Insert(currPath);
  690. end
  691. else
  692. begin
  693. { Check if already in path, then we don't add it }
  694. hp:=Find(currPath);
  695. if not assigned(hp) then
  696. Concat(currPath);
  697. end;
  698. end;
  699. begin
  700. if s='' then
  701. exit;
  702. { Support default macro's }
  703. DefaultReplacements(s);
  704. { get current dir }
  705. CurrentDir:=GetCurrentDir;
  706. repeat
  707. { get currpath }
  708. if addfirst then
  709. begin
  710. j:=length(s);
  711. while (j>0) and (s[j]<>';') do
  712. dec(j);
  713. currPath:= TrimSpace(Copy(s,j+1,length(s)-j));
  714. DePascalQuote(currPath);
  715. currPath:=FixPath(currPath,false);
  716. if j=0 then
  717. s:=''
  718. else
  719. System.Delete(s,j,length(s)-j+1);
  720. end
  721. else
  722. begin
  723. j:=Pos(';',s);
  724. if j=0 then
  725. j:=255;
  726. currPath:= TrimSpace(Copy(s,1,j-1));
  727. DePascalQuote(currPath);
  728. currPath:=SrcPath+FixPath(currPath,false);
  729. System.Delete(s,1,j);
  730. end;
  731. { fix pathname }
  732. if currPath='' then
  733. currPath:= CurDirRelPath(source_info)
  734. else
  735. begin
  736. currPath:=FixPath(ExpandFileName(currpath),false);
  737. if (CurrentDir<>'') and (Copy(currPath,1,length(CurrentDir))=CurrentDir) then
  738. begin
  739. {$ifdef AMIGA}
  740. currPath:= CurrentDir+Copy(currPath,length(CurrentDir)+1,255);
  741. {$else}
  742. currPath:= CurDirRelPath(source_info)+Copy(currPath,length(CurrentDir)+1,255);
  743. {$endif}
  744. end;
  745. end;
  746. { wildcard adding ? }
  747. staridx:=pos('*',currpath);
  748. if staridx>0 then
  749. begin
  750. prefix:=ExtractFilePath(Copy(currpath,1,staridx));
  751. suffix:=Copy(currpath,staridx+1,length(currpath));
  752. subdirfound:=false;
  753. {$ifdef usedircache}
  754. if DirCache.FindFirst(Prefix+'*',dir) then
  755. begin
  756. repeat
  757. if (dir.attr and faDirectory)<>0 then
  758. begin
  759. subdirfound:=true;
  760. currpath:=prefix+dir.name+suffix;
  761. if (suffix='') or PathExists(currpath,true) then
  762. begin
  763. hp:=Find(currPath);
  764. if not assigned(hp) then
  765. AddCurrPath;
  766. end;
  767. end;
  768. until not DirCache.FindNext(dir);
  769. end;
  770. DirCache.FindClose(dir);
  771. {$else usedircache}
  772. if findfirst(prefix+'*',faDirectory,dir) = 0 then
  773. begin
  774. repeat
  775. if (dir.name<>'.') and
  776. (dir.name<>'..') and
  777. ((dir.attr and faDirectory)<>0) then
  778. begin
  779. subdirfound:=true;
  780. currpath:=prefix+dir.name+suffix;
  781. if (suffix='') or PathExists(currpath,false) then
  782. begin
  783. hp:=Find(currPath);
  784. if not assigned(hp) then
  785. AddCurrPath;
  786. end;
  787. end;
  788. until findnext(dir) <> 0;
  789. end;
  790. FindClose(dir);
  791. {$endif usedircache}
  792. if not subdirfound then
  793. WarnNonExistingPath(currpath);
  794. end
  795. else
  796. begin
  797. if PathExists(currpath,true) then
  798. AddCurrPath
  799. else
  800. WarnNonExistingPath(currpath);
  801. end;
  802. until (s='');
  803. end;
  804. procedure TSearchPathList.AddList(list:TSearchPathList;addfirst:boolean);
  805. var
  806. s : string;
  807. hl : TSearchPathList;
  808. hp,hp2 : TStringListItem;
  809. begin
  810. if list.empty then
  811. exit;
  812. { create temp and reverse the list }
  813. if addfirst then
  814. begin
  815. hl:=TSearchPathList.Create;
  816. hp:=TStringListItem(list.first);
  817. while assigned(hp) do
  818. begin
  819. hl.insert(hp.Str);
  820. hp:=TStringListItem(hp.next);
  821. end;
  822. while not hl.empty do
  823. begin
  824. s:=hl.GetFirst;
  825. Remove(s);
  826. Insert(s);
  827. end;
  828. hl.Free;
  829. end
  830. else
  831. begin
  832. hp:=TStringListItem(list.first);
  833. while assigned(hp) do
  834. begin
  835. hp2:=Find(hp.Str);
  836. { Check if already in path, then we don't add it }
  837. if not assigned(hp2) then
  838. Concat(hp.Str);
  839. hp:=TStringListItem(hp.next);
  840. end;
  841. end;
  842. end;
  843. function TSearchPathList.FindFile(const f : string;allowcache:boolean;var foundfile:string):boolean;
  844. Var
  845. p : TStringListItem;
  846. begin
  847. FindFile:=false;
  848. p:=TStringListItem(first);
  849. while assigned(p) do
  850. begin
  851. result:=FileExistsNonCase(p.Str,f,allowcache,FoundFile);
  852. if result then
  853. exit;
  854. p:=TStringListItem(p.next);
  855. end;
  856. { Return original filename if not found }
  857. FoundFile:=f;
  858. end;
  859. function FindFile(const f : string;path : string;allowcache:boolean;var foundfile:string):boolean;
  860. Var
  861. singlepathstring : string;
  862. i : longint;
  863. begin
  864. {$ifdef Unix}
  865. for i:=1 to length(path) do
  866. if path[i]=':' then
  867. path[i]:=';';
  868. {$endif Unix}
  869. FindFile:=false;
  870. repeat
  871. i:=pos(';',path);
  872. if i=0 then
  873. i:=256;
  874. singlepathstring:=FixPath(copy(path,1,i-1),false);
  875. delete(path,1,i);
  876. result:=FileExistsNonCase(singlepathstring,f,allowcache,FoundFile);
  877. if result then
  878. exit;
  879. until path='';
  880. FoundFile:=f;
  881. end;
  882. function FindFilePchar(const f : string;path : pchar;allowcache:boolean;var foundfile:string):boolean;
  883. Var
  884. singlepathstring : string;
  885. startpc,pc : pchar;
  886. sepch : char;
  887. begin
  888. FindFilePchar:=false;
  889. if Assigned (Path) then
  890. begin
  891. {$ifdef Unix}
  892. sepch:=':';
  893. {$else}
  894. {$ifdef macos}
  895. sepch:=',';
  896. {$else}
  897. sepch:=';';
  898. {$endif macos}
  899. {$endif Unix}
  900. pc:=path;
  901. repeat
  902. startpc:=pc;
  903. while (pc^<>sepch) and (pc^<>';') and (pc^<>#0) do
  904. inc(pc);
  905. move(startpc^,singlepathstring[1],pc-startpc);
  906. singlepathstring[0]:=char(longint(pc-startpc));
  907. singlepathstring:=FixPath(ExpandFileName(singlepathstring),false);
  908. result:=FileExistsNonCase(singlepathstring,f,allowcache,FoundFile);
  909. if result then
  910. exit;
  911. if (pc^=#0) then
  912. break;
  913. inc(pc);
  914. until false;
  915. end;
  916. foundfile:=f;
  917. end;
  918. function FindExe(const bin:string;allowcache:boolean;var foundfile:string):boolean;
  919. var
  920. p : pchar;
  921. found : boolean;
  922. begin
  923. found:=FindFile(FixFileName(ChangeFileExt(bin,source_info.exeext)),'.;'+exepath,allowcache,foundfile);
  924. if not found then
  925. begin
  926. {$ifdef macos}
  927. p:=GetEnvPchar('Commands');
  928. {$else}
  929. p:=GetEnvPchar('PATH');
  930. {$endif}
  931. found:=FindFilePChar(FixFileName(ChangeFileExt(bin,source_info.exeext)),p,allowcache,foundfile);
  932. FreeEnvPChar(p);
  933. end;
  934. FindExe:=found;
  935. end;
  936. function GetShortName(const n:string):string;
  937. {$ifdef win32}
  938. var
  939. hs,hs2 : string;
  940. i : longint;
  941. {$endif}
  942. {$if defined(go32v2) or defined(watcom)}
  943. var
  944. hs : string;
  945. {$endif}
  946. begin
  947. GetShortName:=n;
  948. {$ifdef win32}
  949. hs:=n+#0;
  950. i:=Windows.GetShortPathName(@hs[1],@hs2[1],high(hs2));
  951. if (i>0) and (i<=high(hs2)) then
  952. begin
  953. hs2[0]:=chr(strlen(@hs2[1]));
  954. GetShortName:=hs2;
  955. end;
  956. {$endif}
  957. {$if defined(go32v2) or defined(watcom)}
  958. hs:=n;
  959. if Dos.GetShortName(hs) then
  960. GetShortName:=hs;
  961. {$endif}
  962. end;
  963. {****************************************************************************
  964. Init / Done
  965. ****************************************************************************}
  966. procedure InitFileUtils;
  967. begin
  968. DirCache:=TDirectoryCache.Create;
  969. end;
  970. procedure DoneFileUtils;
  971. begin
  972. DirCache.Free;
  973. end;
  974. end.