cfileutl.pas 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576
  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. type
  40. TCachedDirectory = class(TFPHashObject)
  41. private
  42. FDirectoryEntries : TFPHashList;
  43. FCached : Boolean;
  44. procedure FreeDirectoryEntries;
  45. function GetItemAttr(const AName: TCmdStr): longint;
  46. function TryUseCache: boolean;
  47. procedure ForceUseCache;
  48. procedure Reload;
  49. public
  50. constructor Create(AList:TFPHashObjectList;const AName:TCmdStr);
  51. destructor destroy;override;
  52. function FileExists(const AName:TCmdStr):boolean;
  53. function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  54. function DirectoryExists(const AName:TCmdStr):boolean;
  55. property DirectoryEntries:TFPHashList read FDirectoryEntries;
  56. end;
  57. TCachedSearchRec = record
  58. Name : TCmdStr;
  59. Attr : byte;
  60. Pattern : TCmdStr;
  61. CachedDir : TCachedDirectory;
  62. EntryIndex : longint;
  63. end;
  64. PCachedDirectoryEntry = ^TCachedDirectoryEntry;
  65. TCachedDirectoryEntry = record
  66. RealName: TCmdStr;
  67. Attr : longint;
  68. end;
  69. TDirectoryCache = class
  70. private
  71. FDirectories : TFPHashObjectList;
  72. function GetDirectory(const ADir:TCmdStr):TCachedDirectory;
  73. public
  74. constructor Create;
  75. destructor destroy;override;
  76. function FileExists(const AName:TCmdStr):boolean;
  77. function FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  78. function DirectoryExists(const AName:TCmdStr):boolean;
  79. function FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
  80. function FindNext(var Res:TCachedSearchRec):boolean;
  81. function FindClose(var Res:TCachedSearchRec):boolean;
  82. end;
  83. TSearchPathList = class(TCmdStrList)
  84. procedure AddPath(s:TCmdStr;addfirst:boolean);overload;
  85. procedure AddPath(SrcPath,s:TCmdStr;addfirst:boolean);overload;
  86. procedure AddList(list:TSearchPathList;addfirst:boolean);
  87. function FindFile(const f : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  88. end;
  89. function bstoslash(const s : TCmdStr) : TCmdStr;
  90. {Gives the absolute path to the current directory}
  91. function GetCurrentDir:TCmdStr;
  92. {Gives the relative path to the current directory,
  93. with a trailing dir separator. E. g. on unix ./ }
  94. function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
  95. function path_absolute(const s : TCmdStr) : boolean;
  96. Function PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  97. Function FileExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  98. function FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  99. Function RemoveDir(d:TCmdStr):boolean;
  100. Function FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
  101. function FixFileName(const s:TCmdStr):TCmdStr;
  102. function TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
  103. function TargetFixFileName(const s:TCmdStr):TCmdStr;
  104. procedure SplitBinCmd(const s:TCmdStr;var bstr: TCmdStr;var cstr:TCmdStr);
  105. function FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  106. { function FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;}
  107. function FindFileInExeLocations(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  108. function FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  109. function GetShortName(const n:TCmdStr):TCmdStr;
  110. function maybequoted(const s:string):string;
  111. function maybequoted(const s:ansistring):ansistring;
  112. procedure InitFileUtils;
  113. procedure DoneFileUtils;
  114. function UnixRequoteWithDoubleQuotes(const QuotedStr: TCmdStr): TCmdStr;
  115. function RequotedExecuteProcess(const Path: AnsiString; const ComLine: AnsiString; Flags: TExecuteFlags = []): Longint;
  116. function RequotedExecuteProcess(const Path: AnsiString; const ComLine: array of AnsiString; Flags: TExecuteFlags = []): Longint;
  117. function Shell(const command:ansistring): longint;
  118. { hide Sysutils.ExecuteProcess in units using this one after SysUtils}
  119. const
  120. ExecuteProcess = 'Do not use' deprecated 'Use cfileutil.RequotedExecuteProcess instead, ExecuteProcess cannot deal with single quotes as used by Unix command lines';
  121. { * Since native Amiga commands can't handle Unix-style relative paths used by the compiler,
  122. and some GNU tools, Unix2AmigaPath is needed to handle such situations (KB) * }
  123. {$IF DEFINED(MORPHOS) OR DEFINED(AMIGA)}
  124. { * PATHCONV is implemented in the Amiga/MorphOS system unit * }
  125. {$NOTE TODO Amiga: implement PathConv() in System unit, which works with AnsiString}
  126. function Unix2AmigaPath(path: ShortString): ShortString; external name 'PATHCONV';
  127. {$ELSE}
  128. function Unix2AmigaPath(path: String): String;{$IFDEF USEINLINE}inline;{$ENDIF}
  129. {$ENDIF}
  130. implementation
  131. uses
  132. Comphook,
  133. Globals;
  134. {$undef AllFilesMaskIsInRTL}
  135. {$if (FPC_VERSION > 2)}
  136. {$define AllFilesMaskIsInRTL}
  137. {$endif FPC_VERSION}
  138. {$if (FPC_VERSION = 2) and (FPC_RELEASE > 2)}
  139. {$define AllFilesMaskIsInRTL}
  140. {$endif}
  141. {$if (FPC_VERSION = 2) and (FPC_RELEASE = 2) and (FPC_PATCH > 0)}
  142. {$define AllFilesMaskIsInRTL}
  143. {$endif}
  144. {$ifndef AllFilesMaskIsInRTL}
  145. {$if defined(go32v2) or defined(watcom)}
  146. const
  147. AllFilesMask = '*.*';
  148. {$else}
  149. const
  150. AllFilesMask = '*';
  151. {$endif not (go32v2 or watcom)}
  152. {$endif not AllFilesMaskIsInRTL}
  153. var
  154. DirCache : TDirectoryCache;
  155. {$IF NOT (DEFINED(MORPHOS) OR DEFINED(AMIGA))}
  156. { Stub function for Unix2Amiga Path conversion functionality, only available in
  157. Amiga/MorphOS RTL. I'm open for better solutions. (KB) }
  158. function Unix2AmigaPath(path: String): String;{$IFDEF USEINLINE}inline;{$ENDIF}
  159. begin
  160. Unix2AmigaPath:=path;
  161. end;
  162. {$ENDIF}
  163. {****************************************************************************
  164. TCachedDirectory
  165. ****************************************************************************}
  166. constructor TCachedDirectory.create(AList:TFPHashObjectList;const AName:TCmdStr);
  167. begin
  168. inherited create(AList,AName);
  169. FDirectoryEntries:=TFPHashList.Create;
  170. FCached:=False;
  171. end;
  172. destructor TCachedDirectory.destroy;
  173. begin
  174. FreeDirectoryEntries;
  175. FDirectoryEntries.Free;
  176. inherited destroy;
  177. end;
  178. function TCachedDirectory.TryUseCache:boolean;
  179. begin
  180. Result:=True;
  181. if FCached then
  182. exit;
  183. if not current_settings.disabledircache then
  184. ForceUseCache
  185. else
  186. Result:=False;
  187. end;
  188. procedure TCachedDirectory.ForceUseCache;
  189. begin
  190. if not FCached then
  191. begin
  192. FCached:=True;
  193. Reload;
  194. end;
  195. end;
  196. procedure TCachedDirectory.FreeDirectoryEntries;
  197. var
  198. i: Integer;
  199. begin
  200. if not(tf_files_case_aware in source_info.flags) then
  201. exit;
  202. for i := 0 to DirectoryEntries.Count-1 do
  203. dispose(PCachedDirectoryEntry(DirectoryEntries[i]));
  204. end;
  205. function TCachedDirectory.GetItemAttr(const AName: TCmdStr): longint;
  206. var
  207. entry: PCachedDirectoryEntry;
  208. begin
  209. if not(tf_files_case_sensitive in source_info.flags) then
  210. if (tf_files_case_aware in source_info.flags) then
  211. begin
  212. entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(AName)));
  213. if assigned(entry) then
  214. Result:=entry^.Attr
  215. else
  216. Result:=0;
  217. end
  218. else
  219. Result:=PtrUInt(DirectoryEntries.Find(Lower(AName)))
  220. else
  221. Result:=PtrUInt(DirectoryEntries.Find(AName));
  222. end;
  223. procedure TCachedDirectory.Reload;
  224. var
  225. dir : TSearchRec;
  226. entry : PCachedDirectoryEntry;
  227. begin
  228. FreeDirectoryEntries;
  229. DirectoryEntries.Clear;
  230. if findfirst(IncludeTrailingPathDelimiter(Name)+AllFilesMask,faAnyFile or faDirectory,dir) = 0 then
  231. begin
  232. repeat
  233. if ((dir.attr and faDirectory)<>faDirectory) or
  234. ((dir.Name<>'.') and
  235. (dir.Name<>'..')) then
  236. begin
  237. { Force Archive bit so the attribute always has a value. This is needed
  238. to be able to see the difference in the directoryentries lookup if a file
  239. exists or not }
  240. Dir.Attr:=Dir.Attr or faArchive;
  241. if not(tf_files_case_sensitive in source_info.flags) then
  242. if (tf_files_case_aware in source_info.flags) then
  243. begin
  244. new(entry);
  245. entry^.RealName:=Dir.Name;
  246. entry^.Attr:=Dir.Attr;
  247. DirectoryEntries.Add(Lower(Dir.Name),entry)
  248. end
  249. else
  250. DirectoryEntries.Add(Lower(Dir.Name),Pointer(Ptrint(Dir.Attr)))
  251. else
  252. DirectoryEntries.Add(Dir.Name,Pointer(Ptrint(Dir.Attr)));
  253. end;
  254. until findnext(dir) <> 0;
  255. end;
  256. findclose(dir);
  257. end;
  258. function TCachedDirectory.FileExists(const AName:TCmdStr):boolean;
  259. var
  260. Attr : Longint;
  261. begin
  262. if not TryUseCache then
  263. begin
  264. { prepend directory name again }
  265. result:=cfileutl.FileExists(Name+AName,false);
  266. exit;
  267. end;
  268. Attr:=GetItemAttr(AName);
  269. if Attr<>0 then
  270. Result:=((Attr and faDirectory)=0)
  271. else
  272. Result:=false;
  273. end;
  274. function TCachedDirectory.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  275. var
  276. entry : PCachedDirectoryEntry;
  277. begin
  278. if (tf_files_case_aware in source_info.flags) then
  279. begin
  280. if not TryUseCache then
  281. begin
  282. Result:=FileExistsNonCase(path,fn,false,FoundName);
  283. exit;
  284. end;
  285. entry:=PCachedDirectoryEntry(DirectoryEntries.Find(Lower(ExtractFileName(fn))));
  286. if assigned(entry) and
  287. (entry^.Attr<>0) and
  288. ((entry^.Attr and faDirectory) = 0) then
  289. begin
  290. FoundName:=ExtractFilePath(path+fn)+entry^.RealName;
  291. Result:=true
  292. end
  293. else
  294. Result:=false;
  295. end
  296. else
  297. { should not be called in this case, use plain FileExists }
  298. Result:=False;
  299. end;
  300. function TCachedDirectory.DirectoryExists(const AName:TCmdStr):boolean;
  301. var
  302. Attr : Longint;
  303. begin
  304. if not TryUseCache then
  305. begin
  306. Result:=PathExists(Name+AName,false);
  307. exit;
  308. end;
  309. Attr:=GetItemAttr(AName);
  310. if Attr<>0 then
  311. Result:=((Attr and faDirectory)=faDirectory)
  312. else
  313. Result:=false;
  314. end;
  315. {****************************************************************************
  316. TDirectoryCache
  317. ****************************************************************************}
  318. constructor TDirectoryCache.create;
  319. begin
  320. inherited create;
  321. FDirectories:=TFPHashObjectList.Create(true);
  322. end;
  323. destructor TDirectoryCache.destroy;
  324. begin
  325. FDirectories.Free;
  326. inherited destroy;
  327. end;
  328. function TDirectoryCache.GetDirectory(const ADir:TCmdStr):TCachedDirectory;
  329. var
  330. CachedDir : TCachedDirectory;
  331. DirName : TCmdStr;
  332. begin
  333. if ADir='' then
  334. DirName:='.'+source_info.DirSep
  335. else
  336. DirName:=ADir;
  337. CachedDir:=TCachedDirectory(FDirectories.Find(DirName));
  338. if not assigned(CachedDir) then
  339. CachedDir:=TCachedDirectory.Create(FDirectories,DirName);
  340. Result:=CachedDir;
  341. end;
  342. function TDirectoryCache.FileExists(const AName:TCmdStr):boolean;
  343. var
  344. CachedDir : TCachedDirectory;
  345. begin
  346. Result:=false;
  347. CachedDir:=GetDirectory(ExtractFilePath(AName));
  348. if assigned(CachedDir) then
  349. Result:=CachedDir.FileExists(ExtractFileName(AName));
  350. end;
  351. function TDirectoryCache.FileExistsCaseAware(const path, fn: TCmdStr; out FoundName: TCmdStr):boolean;
  352. var
  353. CachedDir : TCachedDirectory;
  354. begin
  355. Result:=false;
  356. CachedDir:=GetDirectory(ExtractFilePath(path+fn));
  357. if assigned(CachedDir) then
  358. Result:=CachedDir.FileExistsCaseAware(path,fn,FoundName);
  359. end;
  360. function TDirectoryCache.DirectoryExists(const AName:TCmdStr):boolean;
  361. var
  362. CachedDir : TCachedDirectory;
  363. begin
  364. Result:=false;
  365. CachedDir:=GetDirectory(ExtractFilePath(AName));
  366. if assigned(CachedDir) then
  367. Result:=CachedDir.DirectoryExists(ExtractFileName(AName));
  368. end;
  369. function TDirectoryCache.FindFirst(const APattern:TCmdStr;var Res:TCachedSearchRec):boolean;
  370. begin
  371. Res.Pattern:=ExtractFileName(APattern);
  372. Res.CachedDir:=GetDirectory(ExtractFilePath(APattern));
  373. Res.CachedDir.ForceUseCache;
  374. Res.EntryIndex:=0;
  375. if assigned(Res.CachedDir) then
  376. Result:=FindNext(Res)
  377. else
  378. Result:=false;
  379. end;
  380. function TDirectoryCache.FindNext(var Res:TCachedSearchRec):boolean;
  381. var
  382. entry: PCachedDirectoryEntry;
  383. begin
  384. if Res.EntryIndex<Res.CachedDir.DirectoryEntries.Count then
  385. begin
  386. if (tf_files_case_aware in source_info.flags) then
  387. begin
  388. entry:=Res.CachedDir.DirectoryEntries[Res.EntryIndex];
  389. Res.Name:=entry^.RealName;
  390. Res.Attr:=entry^.Attr;
  391. end
  392. else
  393. begin
  394. Res.Name:=Res.CachedDir.DirectoryEntries.NameOfIndex(Res.EntryIndex);
  395. Res.Attr:=PtrUInt(Res.CachedDir.DirectoryEntries[Res.EntryIndex]);
  396. end;
  397. inc(Res.EntryIndex);
  398. Result:=true;
  399. end
  400. else
  401. Result:=false;
  402. end;
  403. function TDirectoryCache.FindClose(var Res:TCachedSearchRec):boolean;
  404. begin
  405. { nothing todo }
  406. result:=true;
  407. end;
  408. {****************************************************************************
  409. Utils
  410. ****************************************************************************}
  411. function bstoslash(const s : TCmdStr) : TCmdStr;
  412. {
  413. return TCmdStr s with all \ changed into /
  414. }
  415. var
  416. i : longint;
  417. begin
  418. setlength(bstoslash,length(s));
  419. for i:=1to length(s) do
  420. if s[i]='\' then
  421. bstoslash[i]:='/'
  422. else
  423. bstoslash[i]:=s[i];
  424. end;
  425. {Gives the absolute path to the current directory}
  426. var
  427. CachedCurrentDir : TCmdStr;
  428. function GetCurrentDir:TCmdStr;
  429. begin
  430. if CachedCurrentDir='' then
  431. begin
  432. GetDir(0,CachedCurrentDir);
  433. CachedCurrentDir:=FixPath(CachedCurrentDir,false);
  434. end;
  435. result:=CachedCurrentDir;
  436. end;
  437. {Gives the relative path to the current directory,
  438. with a trailing dir separator. E. g. on unix ./ }
  439. function CurDirRelPath(systeminfo: tsysteminfo): TCmdStr;
  440. begin
  441. if systeminfo.system <> system_powerpc_macos then
  442. CurDirRelPath:= '.'+systeminfo.DirSep
  443. else
  444. CurDirRelPath:= ':'
  445. end;
  446. function path_absolute(const s : TCmdStr) : boolean;
  447. {
  448. is path s an absolute path?
  449. }
  450. begin
  451. result:=false;
  452. {$if defined(unix)}
  453. if (length(s)>0) and (s[1] in AllowDirectorySeparators) then
  454. result:=true;
  455. {$elseif defined(amiga) or defined(morphos)}
  456. (* An Amiga path is absolute, if it has a volume/device name in it (contains ":"),
  457. otherwise it's always a relative path, no matter if it starts with a directory
  458. separator or not. (KB) *)
  459. if (length(s)>0) and (Pos(':',s) <> 0) then
  460. result:=true;
  461. {$elseif defined(macos)}
  462. if IsMacFullPath(s) then
  463. result:=true;
  464. {$elseif defined(netware)}
  465. if (Pos (DriveSeparator, S) <> 0) or
  466. ((Length (S) > 0) and (S [1] in AllowDirectorySeparators)) then
  467. result:=true;
  468. {$elseif defined(win32) or defined(win64) or defined(go32v2) or defined(os2) or defined(watcom)}
  469. if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
  470. (* The following check for non-empty AllowDriveSeparators assumes that all
  471. other platforms supporting drives and not handled as exceptions above
  472. should work with DOS-like paths, i.e. use absolute paths with one letter
  473. for drive followed by path separator *)
  474. ((length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
  475. result:=true;
  476. {$else}
  477. if ((length(s)>0) and (s[1] in AllowDirectorySeparators)) or
  478. (* The following check for non-empty AllowDriveSeparators assumes that all
  479. other platforms supporting drives and not handled as exceptions above
  480. should work with DOS-like paths, i.e. use absolute paths with one letter
  481. for drive followed by path separator *)
  482. ((AllowDriveSeparators <> []) and (length(s)>2) and (s[2] in AllowDriveSeparators) and (s[3] in AllowDirectorySeparators)) then
  483. result:=true;
  484. {$endif unix}
  485. end;
  486. Function FileExists ( Const F : TCmdStr;allowcache:boolean) : Boolean;
  487. begin
  488. {$ifdef usedircache}
  489. if allowcache then
  490. Result:=DirCache.FileExists(F)
  491. else
  492. {$endif usedircache}
  493. Result:=SysUtils.FileExists(F);
  494. if do_checkverbosity(V_Tried) then
  495. begin
  496. if Result then
  497. do_comment(V_Tried,'Searching file '+F+'... found')
  498. else
  499. do_comment(V_Tried,'Searching file '+F+'... not found');
  500. end;
  501. end;
  502. function FileExistsNonCase(const path,fn:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  503. var
  504. fn2 : TCmdStr;
  505. begin
  506. result:=false;
  507. if tf_files_case_sensitive in source_info.flags then
  508. begin
  509. {
  510. Search order for case sensitive systems:
  511. 1. NormalCase
  512. 2. lowercase
  513. 3. UPPERCASE
  514. }
  515. FoundFile:=path+fn;
  516. If FileExists(FoundFile,allowcache) then
  517. begin
  518. result:=true;
  519. exit;
  520. end;
  521. fn2:=Lower(fn);
  522. if fn2<>fn then
  523. begin
  524. FoundFile:=path+fn2;
  525. If FileExists(FoundFile,allowcache) then
  526. begin
  527. result:=true;
  528. exit;
  529. end;
  530. end;
  531. fn2:=Upper(fn);
  532. if fn2<>fn then
  533. begin
  534. FoundFile:=path+fn2;
  535. If FileExists(FoundFile,allowcache) then
  536. begin
  537. result:=true;
  538. exit;
  539. end;
  540. end;
  541. end
  542. else
  543. if tf_files_case_aware in source_info.flags then
  544. begin
  545. {
  546. Search order for case aware systems:
  547. 1. NormalCase
  548. }
  549. {$ifdef usedircache}
  550. if allowcache then
  551. begin
  552. result:=DirCache.FileExistsCaseAware(path,fn,fn2);
  553. if result then
  554. begin
  555. FoundFile:=fn2;
  556. exit;
  557. end;
  558. end
  559. else
  560. {$endif usedircache}
  561. begin
  562. FoundFile:=path+fn;
  563. If FileExists(FoundFile,allowcache) then
  564. begin
  565. { don't know the real name in this case }
  566. result:=true;
  567. exit;
  568. end;
  569. end;
  570. end
  571. else
  572. begin
  573. { None case sensitive only lowercase }
  574. FoundFile:=path+Lower(fn);
  575. If FileExists(FoundFile,allowcache) then
  576. begin
  577. result:=true;
  578. exit;
  579. end;
  580. end;
  581. { Set foundfile to something useful }
  582. FoundFile:=fn;
  583. end;
  584. Function PathExists (const F : TCmdStr;allowcache:boolean) : Boolean;
  585. Var
  586. i: longint;
  587. hs : TCmdStr;
  588. begin
  589. if F = '' then
  590. begin
  591. result := true;
  592. exit;
  593. end;
  594. hs := ExpandFileName(F);
  595. I := Pos (DriveSeparator, hs);
  596. if (hs [Length (hs)] = DirectorySeparator) and
  597. (((I = 0) and (Length (hs) > 1)) or (I <> Length (hs) - 1)) then
  598. Delete (hs, Length (hs), 1);
  599. {$ifdef usedircache}
  600. if allowcache then
  601. Result:=DirCache.DirectoryExists(hs)
  602. else
  603. {$endif usedircache}
  604. Result:=SysUtils.DirectoryExists(hs);
  605. end;
  606. Function RemoveDir(d:TCmdStr):boolean;
  607. begin
  608. if d[length(d)]=source_info.DirSep then
  609. Delete(d,length(d),1);
  610. {$push}{$I-}
  611. rmdir(d);
  612. {$pop}
  613. RemoveDir:=(ioresult=0);
  614. end;
  615. Function FixPath(const s:TCmdStr;allowdot:boolean):TCmdStr;
  616. var
  617. i, L : longint;
  618. P: PChar;
  619. begin
  620. Result := s;
  621. { make result unique since we're going to change it via a pchar }
  622. uniquestring(result);
  623. L := Length(Result);
  624. if L=0 then
  625. exit;
  626. { Fix separator }
  627. P := @Result[1];
  628. for i:=0 to L-1 do
  629. begin
  630. if p^ in ['/','\'] then
  631. p^:=source_info.DirSep;
  632. inc(p);
  633. end;
  634. { Fix ending / }
  635. if (L>0) and (Result[L]<>source_info.DirSep) and
  636. (Result[L]<>DriveSeparator) then
  637. Result:=Result+source_info.DirSep; { !still results in temp AnsiString }
  638. { Remove ./ }
  639. if (not allowdot) and ((Length(Result)=2) and (Result[1]='.') and (Result[2] = source_info.DirSep)) then
  640. begin
  641. Result:='';
  642. Exit;
  643. end;
  644. { return }
  645. if not ((tf_files_case_aware in source_info.flags) or
  646. (tf_files_case_sensitive in source_info.flags)) then
  647. Result := lower(Result);
  648. end;
  649. {Actually the version in macutils.pp could be used,
  650. but that would not work for crosscompiling, so this is a slightly modified
  651. version of it.}
  652. function TranslatePathToMac (const path: TCmdStr; mpw: Boolean): TCmdStr;
  653. function GetVolumeIdentifier: TCmdStr;
  654. begin
  655. GetVolumeIdentifier := '{Boot}'
  656. (*
  657. if mpw then
  658. GetVolumeIdentifier := '{Boot}'
  659. else
  660. GetVolumeIdentifier := macosBootVolumeName;
  661. *)
  662. end;
  663. var
  664. slashPos, oldpos, newpos, oldlen, maxpos: Longint;
  665. begin
  666. oldpos := 1;
  667. slashPos := Pos('/', path);
  668. if (slashPos <> 0) then {its a unix path}
  669. begin
  670. if slashPos = 1 then
  671. begin {its a full path}
  672. oldpos := 2;
  673. TranslatePathToMac := GetVolumeIdentifier;
  674. end
  675. else {its a partial path}
  676. TranslatePathToMac := ':';
  677. end
  678. else
  679. begin
  680. slashPos := Pos('\', path);
  681. if (slashPos <> 0) then {its a dos path}
  682. begin
  683. if slashPos = 1 then
  684. begin {its a full path, without drive letter}
  685. oldpos := 2;
  686. TranslatePathToMac := GetVolumeIdentifier;
  687. end
  688. else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
  689. begin
  690. oldpos := 4;
  691. TranslatePathToMac := GetVolumeIdentifier;
  692. end
  693. else {its a partial path}
  694. TranslatePathToMac := ':';
  695. end;
  696. end;
  697. if (slashPos <> 0) then {its a unix or dos path}
  698. begin
  699. {Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
  700. newpos := Length(TranslatePathToMac);
  701. oldlen := Length(path);
  702. SetLength(TranslatePathToMac, newpos + oldlen); {It will be no longer than what is already}
  703. {prepended plus length of path.}
  704. maxpos := Length(TranslatePathToMac); {Get real maxpos, can be short if String is ShortString}
  705. {There is never a slash in the beginning, because either it was an absolute path, and then the}
  706. {drive and slash was removed, or it was a relative path without a preceding slash.}
  707. while oldpos <= oldlen do
  708. begin
  709. {Check if special dirs, ./ or ../ }
  710. if path[oldPos] = '.' then
  711. if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
  712. begin
  713. if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
  714. begin
  715. {It is "../" or ".." translates to ":" }
  716. if newPos = maxPos then
  717. begin {Shouldn't actually happen, but..}
  718. Exit('');
  719. end;
  720. newPos := newPos + 1;
  721. TranslatePathToMac[newPos] := ':';
  722. oldPos := oldPos + 3;
  723. continue; {Start over again}
  724. end;
  725. end
  726. else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
  727. begin
  728. {It is "./" or "." ignor it }
  729. oldPos := oldPos + 2;
  730. continue; {Start over again}
  731. end;
  732. {Collect file or dir name}
  733. while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
  734. begin
  735. if newPos = maxPos then
  736. begin {Shouldn't actually happen, but..}
  737. Exit('');
  738. end;
  739. newPos := newPos + 1;
  740. TranslatePathToMac[newPos] := path[oldPos];
  741. oldPos := oldPos + 1;
  742. end;
  743. {When we come here there is either a slash or we are at the end.}
  744. if (oldpos <= oldlen) then
  745. begin
  746. if newPos = maxPos then
  747. begin {Shouldn't actually happen, but..}
  748. Exit('');
  749. end;
  750. newPos := newPos + 1;
  751. TranslatePathToMac[newPos] := ':';
  752. oldPos := oldPos + 1;
  753. end;
  754. end;
  755. SetLength(TranslatePathToMac, newpos);
  756. end
  757. else if (path = '.') then
  758. TranslatePathToMac := ':'
  759. else if (path = '..') then
  760. TranslatePathToMac := '::'
  761. else
  762. TranslatePathToMac := path; {its a mac path}
  763. end;
  764. function FixFileName(const s:TCmdStr):TCmdStr;
  765. var
  766. i : longint;
  767. begin
  768. if source_info.system = system_powerpc_MACOS then
  769. FixFileName:= TranslatePathToMac(s, true)
  770. else
  771. if (tf_files_case_aware in source_info.flags) or
  772. (tf_files_case_sensitive in source_info.flags) then
  773. begin
  774. setlength(FixFileName,length(s));
  775. for i:=1 to length(s) do
  776. begin
  777. case s[i] of
  778. '/','\' :
  779. FixFileName[i]:=source_info.dirsep;
  780. else
  781. FixFileName[i]:=s[i];
  782. end;
  783. end;
  784. end
  785. else
  786. begin
  787. setlength(FixFileName,length(s));
  788. for i:=1 to length(s) do
  789. begin
  790. case s[i] of
  791. '/','\' :
  792. FixFileName[i]:=source_info.dirsep;
  793. 'A'..'Z' :
  794. FixFileName[i]:=char(byte(s[i])+32);
  795. else
  796. FixFileName[i]:=s[i];
  797. end;
  798. end;
  799. end;
  800. end;
  801. Function TargetFixPath(s:TCmdStr;allowdot:boolean):TCmdStr;
  802. var
  803. i : longint;
  804. begin
  805. { Fix separator }
  806. for i:=1 to length(s) do
  807. if s[i] in ['/','\'] then
  808. s[i]:=target_info.DirSep;
  809. { Fix ending / }
  810. if (length(s)>0) and (s[length(s)]<>target_info.DirSep) and
  811. (s[length(s)]<>':') then
  812. s:=s+target_info.DirSep;
  813. { Remove ./ }
  814. if (not allowdot) and (s='.'+target_info.DirSep) then
  815. s:='';
  816. { return }
  817. if (tf_files_case_aware in target_info.flags) or
  818. (tf_files_case_sensitive in target_info.flags) then
  819. TargetFixPath:=s
  820. else
  821. TargetFixPath:=Lower(s);
  822. end;
  823. function TargetFixFileName(const s:TCmdStr):TCmdStr;
  824. var
  825. i : longint;
  826. begin
  827. if target_info.system = system_powerpc_MACOS then
  828. TargetFixFileName:= TranslatePathToMac(s, true)
  829. else
  830. if (tf_files_case_aware in target_info.flags) or
  831. (tf_files_case_sensitive in target_info.flags) then
  832. begin
  833. setlength(TargetFixFileName,length(s));
  834. for i:=1 to length(s) do
  835. begin
  836. case s[i] of
  837. '/','\' :
  838. TargetFixFileName[i]:=target_info.dirsep;
  839. else
  840. TargetFixFileName[i]:=s[i];
  841. end;
  842. end;
  843. end
  844. else
  845. begin
  846. setlength(TargetFixFileName,length(s));
  847. for i:=1 to length(s) do
  848. begin
  849. case s[i] of
  850. '/','\' :
  851. TargetFixFileName[i]:=target_info.dirsep;
  852. 'A'..'Z' :
  853. TargetFixFileName[i]:=char(byte(s[i])+32);
  854. else
  855. TargetFixFileName[i]:=s[i];
  856. end;
  857. end;
  858. end;
  859. end;
  860. procedure SplitBinCmd(const s:TCmdStr;var bstr:TCmdStr;var cstr:TCmdStr);
  861. var
  862. i : longint;
  863. begin
  864. i:=pos(' ',s);
  865. if i>0 then
  866. begin
  867. bstr:=Copy(s,1,i-1);
  868. cstr:=Copy(s,i+1,length(s)-i);
  869. end
  870. else
  871. begin
  872. bstr:=s;
  873. cstr:='';
  874. end;
  875. end;
  876. procedure TSearchPathList.AddPath(s:TCmdStr;addfirst:boolean);
  877. begin
  878. AddPath('',s,AddFirst);
  879. end;
  880. procedure TSearchPathList.AddPath(SrcPath,s:TCmdStr;addfirst:boolean);
  881. var
  882. staridx,
  883. i,j : longint;
  884. prefix,
  885. suffix,
  886. CurrentDir,
  887. currPath : TCmdStr;
  888. subdirfound : boolean;
  889. {$ifdef usedircache}
  890. dir : TCachedSearchRec;
  891. {$else usedircache}
  892. dir : TSearchRec;
  893. {$endif usedircache}
  894. hp : TCmdStrListItem;
  895. procedure WarnNonExistingPath(const path : TCmdStr);
  896. begin
  897. if do_checkverbosity(V_Tried) then
  898. do_comment(V_Tried,'Path "'+path+'" not found');
  899. end;
  900. procedure AddCurrPath;
  901. begin
  902. if addfirst then
  903. begin
  904. Remove(currPath);
  905. Insert(currPath);
  906. end
  907. else
  908. begin
  909. { Check if already in path, then we don't add it }
  910. hp:=Find(currPath);
  911. if not assigned(hp) then
  912. Concat(currPath);
  913. end;
  914. end;
  915. begin
  916. if s='' then
  917. exit;
  918. { Support default macro's }
  919. DefaultReplacements(s);
  920. {$warnings off}
  921. if PathSeparator <> ';' then
  922. for i:=1 to length(s) do
  923. if s[i]=PathSeparator then
  924. s[i]:=';';
  925. {$warnings on}
  926. { get current dir }
  927. CurrentDir:=GetCurrentDir;
  928. repeat
  929. { get currpath }
  930. if addfirst then
  931. begin
  932. j:=length(s);
  933. while (j>0) and (s[j]<>';') do
  934. dec(j);
  935. currPath:= TrimSpace(Copy(s,j+1,length(s)-j));
  936. if j=0 then
  937. s:=''
  938. else
  939. System.Delete(s,j,length(s)-j+1);
  940. end
  941. else
  942. begin
  943. j:=Pos(';',s);
  944. if j=0 then
  945. j:=length(s)+1;
  946. currPath:= TrimSpace(Copy(s,1,j-1));
  947. System.Delete(s,1,j);
  948. end;
  949. { fix pathname }
  950. DePascalQuote(currPath);
  951. currPath:=SrcPath+FixPath(currPath,false);
  952. if currPath='' then
  953. currPath:= CurDirRelPath(source_info)
  954. else
  955. begin
  956. currPath:=FixPath(ExpandFileName(currpath),false);
  957. if (CurrentDir<>'') and (Copy(currPath,1,length(CurrentDir))=CurrentDir) then
  958. begin
  959. {$if defined(amiga) and defined(morphos)}
  960. currPath:= CurrentDir+Copy(currPath,length(CurrentDir)+1,length(currPath));
  961. {$else}
  962. currPath:= CurDirRelPath(source_info)+Copy(currPath,length(CurrentDir)+1,length(currPath));
  963. {$endif}
  964. end;
  965. end;
  966. { wildcard adding ? }
  967. staridx:=pos('*',currpath);
  968. if staridx>0 then
  969. begin
  970. prefix:=ExtractFilePath(Copy(currpath,1,staridx));
  971. suffix:=Copy(currpath,staridx+1,length(currpath));
  972. subdirfound:=false;
  973. {$ifdef usedircache}
  974. if DirCache.FindFirst(Prefix+AllFilesMask,dir) then
  975. begin
  976. repeat
  977. if (dir.attr and faDirectory)<>0 then
  978. begin
  979. subdirfound:=true;
  980. currpath:=prefix+dir.name+suffix;
  981. if (suffix='') or PathExists(currpath,true) then
  982. begin
  983. hp:=Find(currPath);
  984. if not assigned(hp) then
  985. AddCurrPath;
  986. end;
  987. end;
  988. until not DirCache.FindNext(dir);
  989. end;
  990. DirCache.FindClose(dir);
  991. {$else usedircache}
  992. if findfirst(prefix+AllFilesMask,faDirectory,dir) = 0 then
  993. begin
  994. repeat
  995. if (dir.name<>'.') and
  996. (dir.name<>'..') and
  997. ((dir.attr and faDirectory)<>0) then
  998. begin
  999. subdirfound:=true;
  1000. currpath:=prefix+dir.name+suffix;
  1001. if (suffix='') or PathExists(currpath,false) then
  1002. begin
  1003. hp:=Find(currPath);
  1004. if not assigned(hp) then
  1005. AddCurrPath;
  1006. end;
  1007. end;
  1008. until findnext(dir) <> 0;
  1009. end;
  1010. FindClose(dir);
  1011. {$endif usedircache}
  1012. if not subdirfound then
  1013. WarnNonExistingPath(currpath);
  1014. end
  1015. else
  1016. begin
  1017. if PathExists(currpath,true) then
  1018. AddCurrPath
  1019. else
  1020. WarnNonExistingPath(currpath);
  1021. end;
  1022. until (s='');
  1023. end;
  1024. procedure TSearchPathList.AddList(list:TSearchPathList;addfirst:boolean);
  1025. var
  1026. s : TCmdStr;
  1027. hl : TSearchPathList;
  1028. hp,hp2 : TCmdStrListItem;
  1029. begin
  1030. if list.empty then
  1031. exit;
  1032. { create temp and reverse the list }
  1033. if addfirst then
  1034. begin
  1035. hl:=TSearchPathList.Create;
  1036. hp:=TCmdStrListItem(list.first);
  1037. while assigned(hp) do
  1038. begin
  1039. hl.insert(hp.Str);
  1040. hp:=TCmdStrListItem(hp.next);
  1041. end;
  1042. while not hl.empty do
  1043. begin
  1044. s:=hl.GetFirst;
  1045. Remove(s);
  1046. Insert(s);
  1047. end;
  1048. hl.Free;
  1049. end
  1050. else
  1051. begin
  1052. hp:=TCmdStrListItem(list.first);
  1053. while assigned(hp) do
  1054. begin
  1055. hp2:=Find(hp.Str);
  1056. { Check if already in path, then we don't add it }
  1057. if not assigned(hp2) then
  1058. Concat(hp.Str);
  1059. hp:=TCmdStrListItem(hp.next);
  1060. end;
  1061. end;
  1062. end;
  1063. function TSearchPathList.FindFile(const f :TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1064. Var
  1065. p : TCmdStrListItem;
  1066. begin
  1067. FindFile:=false;
  1068. p:=TCmdStrListItem(first);
  1069. while assigned(p) do
  1070. begin
  1071. result:=FileExistsNonCase(p.Str,f,allowcache,FoundFile);
  1072. if result then
  1073. exit;
  1074. p:=TCmdStrListItem(p.next);
  1075. end;
  1076. { Return original filename if not found }
  1077. FoundFile:=f;
  1078. end;
  1079. function FindFile(const f : TCmdStr; const path : TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1080. Var
  1081. StartPos, EndPos, L: LongInt;
  1082. begin
  1083. Result:=False;
  1084. if (path_absolute(f)) then
  1085. begin
  1086. Result:=FileExistsNonCase('',f, allowcache, foundfile);
  1087. if Result then
  1088. Exit;
  1089. end;
  1090. StartPos := 1;
  1091. L := Length(Path);
  1092. repeat
  1093. EndPos := StartPos;
  1094. while (EndPos <= L) and ((Path[EndPos] <> PathSeparator) and (Path[EndPos] <> ';')) do
  1095. Inc(EndPos);
  1096. Result := FileExistsNonCase(FixPath(Copy(Path, StartPos, EndPos-StartPos), False), f, allowcache, FoundFile);
  1097. if Result then
  1098. Exit;
  1099. StartPos := EndPos + 1;
  1100. until StartPos > L;
  1101. FoundFile:=f;
  1102. end;
  1103. {
  1104. function FindFilePchar(const f : TCmdStr;path : pchar;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1105. Var
  1106. singlepathstring : TCmdStr;
  1107. startpc,pc : pchar;
  1108. begin
  1109. FindFilePchar:=false;
  1110. if Assigned (Path) then
  1111. begin
  1112. pc:=path;
  1113. repeat
  1114. startpc:=pc;
  1115. while (pc^<>PathSeparator) and (pc^<>';') and (pc^<>#0) do
  1116. inc(pc);
  1117. SetLength(singlepathstring, pc-startpc);
  1118. move(startpc^,singlepathstring[1],pc-startpc);
  1119. singlepathstring:=FixPath(ExpandFileName(singlepathstring),false);
  1120. result:=FileExistsNonCase(singlepathstring,f,allowcache,FoundFile);
  1121. if result then
  1122. exit;
  1123. if (pc^=#0) then
  1124. break;
  1125. inc(pc);
  1126. until false;
  1127. end;
  1128. foundfile:=f;
  1129. end;
  1130. }
  1131. function FindFileInExeLocations(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1132. var
  1133. Path : TCmdStr;
  1134. found : boolean;
  1135. begin
  1136. found:=FindFile(FixFileName(bin),exepath,allowcache,foundfile);
  1137. if not found then
  1138. begin
  1139. {$ifdef macos}
  1140. Path:=GetEnvironmentVariable('Commands');
  1141. {$else}
  1142. Path:=GetEnvironmentVariable('PATH');
  1143. {$endif}
  1144. found:=FindFile(FixFileName(bin),Path,allowcache,foundfile);
  1145. end;
  1146. FindFileInExeLocations:=found;
  1147. end;
  1148. function FindExe(const bin:TCmdStr;allowcache:boolean;var foundfile:TCmdStr):boolean;
  1149. begin
  1150. FindExe:=FindFileInExeLocations(ChangeFileExt(bin,source_info.exeext),allowcache,foundfile);
  1151. end;
  1152. function GetShortName(const n:TCmdStr):TCmdStr;
  1153. {$ifdef win32}
  1154. var
  1155. hs,hs2 : TCmdStr;
  1156. i : longint;
  1157. {$endif}
  1158. {$if defined(go32v2) or defined(watcom)}
  1159. var
  1160. hs : shortstring;
  1161. {$endif}
  1162. begin
  1163. GetShortName:=n;
  1164. {$ifdef win32}
  1165. hs:=n+#0;
  1166. { may become longer in case of e.g. ".a" -> "a~1" or so }
  1167. setlength(hs2,length(hs)*2);
  1168. i:=Windows.GetShortPathName(@hs[1],@hs2[1],length(hs)*2);
  1169. if (i>0) and (i<=length(hs)*2) then
  1170. begin
  1171. setlength(hs2,strlen(@hs2[1]));
  1172. GetShortName:=hs2;
  1173. end;
  1174. {$endif}
  1175. {$if defined(go32v2) or defined(watcom)}
  1176. hs:=n;
  1177. if Dos.GetShortName(hs) then
  1178. GetShortName:=hs;
  1179. {$endif}
  1180. end;
  1181. function maybequoted(const s:string):string;
  1182. const
  1183. FORBIDDEN_CHARS_DOS = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
  1184. '{', '}', '''', '`', '~'];
  1185. FORBIDDEN_CHARS_OTHER = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
  1186. '{', '}', '''', ':', '\', '`', '~'];
  1187. var
  1188. forbidden_chars: set of char;
  1189. i : integer;
  1190. quote_script: tscripttype;
  1191. quote_char: ansichar;
  1192. quoted : boolean;
  1193. begin
  1194. if not(cs_link_on_target in current_settings.globalswitches) then
  1195. quote_script:=source_info.script
  1196. else
  1197. quote_script:=target_info.script;
  1198. if quote_script=script_dos then
  1199. forbidden_chars:=FORBIDDEN_CHARS_DOS
  1200. else
  1201. begin
  1202. forbidden_chars:=FORBIDDEN_CHARS_OTHER;
  1203. if quote_script=script_unix then
  1204. include(forbidden_chars,'"');
  1205. end;
  1206. if quote_script=script_unix then
  1207. quote_char:=''''
  1208. else
  1209. quote_char:='"';
  1210. quoted:=false;
  1211. result:=quote_char;
  1212. for i:=1 to length(s) do
  1213. begin
  1214. if s[i]=quote_char then
  1215. begin
  1216. quoted:=true;
  1217. result:=result+'\'+quote_char;
  1218. end
  1219. else case s[i] of
  1220. '\':
  1221. begin
  1222. if quote_script=script_unix then
  1223. begin
  1224. result:=result+'\\';
  1225. quoted:=true
  1226. end
  1227. else
  1228. result:=result+'\';
  1229. end;
  1230. ' ',
  1231. #128..#255 :
  1232. begin
  1233. quoted:=true;
  1234. result:=result+s[i];
  1235. end;
  1236. else begin
  1237. if s[i] in forbidden_chars then
  1238. quoted:=True;
  1239. result:=result+s[i];
  1240. end;
  1241. end;
  1242. end;
  1243. if quoted then
  1244. result:=result+quote_char
  1245. else
  1246. result:=s;
  1247. end;
  1248. function maybequoted_for_script(const s:ansistring; quote_script: tscripttype):ansistring;
  1249. const
  1250. FORBIDDEN_CHARS_DOS = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
  1251. '{', '}', '''', '`', '~'];
  1252. FORBIDDEN_CHARS_OTHER = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')',
  1253. '{', '}', '''', ':', '\', '`', '~'];
  1254. var
  1255. forbidden_chars: set of char;
  1256. i : integer;
  1257. quote_char: ansichar;
  1258. quoted : boolean;
  1259. begin
  1260. if quote_script=script_dos then
  1261. forbidden_chars:=FORBIDDEN_CHARS_DOS
  1262. else
  1263. begin
  1264. forbidden_chars:=FORBIDDEN_CHARS_OTHER;
  1265. if quote_script=script_unix then
  1266. include(forbidden_chars,'"');
  1267. end;
  1268. if quote_script=script_unix then
  1269. quote_char:=''''
  1270. else
  1271. quote_char:='"';
  1272. quoted:=false;
  1273. result:=quote_char;
  1274. for i:=1 to length(s) do
  1275. begin
  1276. if s[i]=quote_char then
  1277. begin
  1278. quoted:=true;
  1279. result:=result+'\'+quote_char;
  1280. end
  1281. else case s[i] of
  1282. '\':
  1283. begin
  1284. if quote_script=script_unix then
  1285. begin
  1286. result:=result+'\\';
  1287. quoted:=true
  1288. end
  1289. else
  1290. result:=result+'\';
  1291. end;
  1292. ' ',
  1293. #128..#255 :
  1294. begin
  1295. quoted:=true;
  1296. result:=result+s[i];
  1297. end;
  1298. else begin
  1299. if s[i] in forbidden_chars then
  1300. quoted:=True;
  1301. result:=result+s[i];
  1302. end;
  1303. end;
  1304. end;
  1305. if quoted then
  1306. result:=result+quote_char
  1307. else
  1308. result:=s;
  1309. end;
  1310. function maybequoted(const s:ansistring):ansistring;
  1311. var
  1312. quote_script: tscripttype;
  1313. begin
  1314. if not(cs_link_on_target in current_settings.globalswitches) then
  1315. quote_script:=source_info.script
  1316. else
  1317. quote_script:=target_info.script;
  1318. result:=maybequoted_for_script(s,quote_script);
  1319. end;
  1320. { requotes a string that was quoted for Unix for passing to ExecuteProcess,
  1321. because it only supports Windows-style quoting; this routine assumes that
  1322. everything that has to be quoted for Windows, was also quoted (but
  1323. differently for Unix) -- which is the case }
  1324. function UnixRequoteWithDoubleQuotes(const QuotedStr: TCmdStr): TCmdStr;
  1325. var
  1326. i: longint;
  1327. temp: TCmdStr;
  1328. inquotes: boolean;
  1329. begin
  1330. if QuotedStr='' then
  1331. begin
  1332. result:='';
  1333. exit;
  1334. end;
  1335. inquotes:=false;
  1336. result:='';
  1337. i:=1;
  1338. while i<=length(QuotedStr) do
  1339. begin
  1340. case QuotedStr[i] of
  1341. '''':
  1342. begin
  1343. if not(inquotes) then
  1344. begin
  1345. inquotes:=true;
  1346. temp:=''
  1347. end
  1348. else
  1349. begin
  1350. { requote for Windows }
  1351. result:=result+maybequoted_for_script(temp,script_dos);
  1352. inquotes:=false;
  1353. end;
  1354. end;
  1355. '\':
  1356. begin
  1357. if inquotes then
  1358. temp:=temp+QuotedStr[i+1]
  1359. else
  1360. result:=result+QuotedStr[i+1];
  1361. inc(i);
  1362. end;
  1363. else
  1364. begin
  1365. if inquotes then
  1366. temp:=temp+QuotedStr[i]
  1367. else
  1368. result:=result+QuotedStr[i];
  1369. end;
  1370. end;
  1371. inc(i);
  1372. end;
  1373. end;
  1374. function RequotedExecuteProcess(const Path: AnsiString; const ComLine: AnsiString; Flags: TExecuteFlags): Longint;
  1375. var
  1376. quote_script: tscripttype;
  1377. begin
  1378. if (cs_link_on_target in current_settings.globalswitches) then
  1379. quote_script:=target_info.script
  1380. else
  1381. quote_script:=source_info.script;
  1382. if quote_script=script_unix then
  1383. result:=sysutils.ExecuteProcess(Path,UnixRequoteWithDoubleQuotes(ComLine),Flags)
  1384. else
  1385. result:=sysutils.ExecuteProcess(Path,ComLine,Flags)
  1386. end;
  1387. function RequotedExecuteProcess(const Path: AnsiString; const ComLine: array of AnsiString; Flags: TExecuteFlags): Longint;
  1388. begin
  1389. result:=sysutils.ExecuteProcess(Path,ComLine,Flags);
  1390. end;
  1391. function Shell(const command:ansistring): longint;
  1392. { This is already defined in the linux.ppu for linux, need for the *
  1393. expansion under linux }
  1394. {$ifdef hasunix}
  1395. begin
  1396. result := Unix.fpsystem(command);
  1397. end;
  1398. {$else hasunix}
  1399. {$ifdef amigashell}
  1400. begin
  1401. result := RequotedExecuteProcess('',command);
  1402. end;
  1403. {$else amigashell}
  1404. var
  1405. comspec : string;
  1406. begin
  1407. comspec:=GetEnvironmentVariable('COMSPEC');
  1408. result := RequotedExecuteProcess(comspec,' /C '+command);
  1409. end;
  1410. {$endif amigashell}
  1411. {$endif hasunix}
  1412. {****************************************************************************
  1413. Init / Done
  1414. ****************************************************************************}
  1415. procedure InitFileUtils;
  1416. begin
  1417. DirCache:=TDirectoryCache.Create;
  1418. end;
  1419. procedure DoneFileUtils;
  1420. begin
  1421. DirCache.Free;
  1422. end;
  1423. end.