cfileutils.pas 32 KB

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