dcosutils.pas 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108
  1. {
  2. Double Commander
  3. -------------------------------------------------------------------------
  4. This unit contains platform dependent functions dealing with operating system.
  5. Copyright (C) 2006-2025 Alexander Koblov ([email protected])
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. }
  17. unit DCOSUtils;
  18. {$mode objfpc}{$H+}
  19. {$modeswitch advancedrecords}
  20. interface
  21. uses
  22. SysUtils, Classes, DynLibs, DCClassesUtf8, DCBasicTypes, DCConvertEncoding
  23. {$IFDEF UNIX}
  24. , BaseUnix, DCUnix
  25. {$ENDIF}
  26. {$IFDEF LINUX}
  27. , DCLinux
  28. {$ENDIF}
  29. {$IFDEF HAIKU}
  30. , DCHaiku
  31. {$ENDIF}
  32. {$IFDEF MSWINDOWS}
  33. , JwaWinBase, Windows
  34. {$ENDIF}
  35. ;
  36. const
  37. fmOpenSync = $10000;
  38. fmOpenDirect = $20000;
  39. fmOpenNoATime = $40000;
  40. {$IF DEFINED(UNIX)}
  41. ERROR_NOT_SAME_DEVICE = ESysEXDEV;
  42. {$ELSE}
  43. ERROR_NOT_SAME_DEVICE = Windows.ERROR_NOT_SAME_DEVICE;
  44. {$ENDIF}
  45. type
  46. TFileMapRec = record
  47. FileHandle : System.THandle;
  48. FileSize : Int64;
  49. {$IFDEF MSWINDOWS}
  50. MappingHandle : System.THandle;
  51. {$ENDIF}
  52. MappedFile : Pointer;
  53. end;
  54. TFileAttributeData = packed record
  55. Size: Int64;
  56. {$IF DEFINED(UNIX)}
  57. FindData: BaseUnix.Stat;
  58. property Attr: TUnixMode read FindData.st_mode;
  59. property PlatformTime: TUnixTime read FindData.st_ctime;
  60. property LastWriteTime: TUnixTime read FindData.st_mtime;
  61. property LastAccessTime: TUnixTime read FindData.st_atime;
  62. {$ELSE}
  63. case Boolean of
  64. True: (
  65. FindData: Windows.TWin32FileAttributeData;
  66. );
  67. False: (
  68. Attr: TFileAttrs;
  69. PlatformTime: DCBasicTypes.TFileTime;
  70. LastAccessTime: DCBasicTypes.TFileTime;
  71. LastWriteTime: DCBasicTypes.TFileTime;
  72. );
  73. {$ENDIF}
  74. end;
  75. TCopyAttributesOption = (caoCopyAttributes,
  76. caoCopyTime,
  77. caoCopyOwnership,
  78. caoCopyPermissions,
  79. caoCopyXattributes,
  80. // Modifiers
  81. caoCopyTimeEx,
  82. caoCopyAttrEx,
  83. caoRemoveReadOnlyAttr);
  84. TCopyAttributesOptions = set of TCopyAttributesOption;
  85. TCopyAttributesResult = array[TCopyAttributesOption] of Integer;
  86. PCopyAttributesResult = ^TCopyAttributesResult;
  87. const
  88. faInvalidAttributes = TFileAttrs(-1);
  89. CopyAttributesOptionCopyAll = [caoCopyAttributes, caoCopyTime, caoCopyOwnership];
  90. {en
  91. Is file a directory
  92. @param(iAttr File attributes)
  93. @returns(@true if file is a directory, @false otherwise)
  94. }
  95. function FPS_ISDIR(iAttr: TFileAttrs) : Boolean;
  96. {en
  97. Is file a symbolic link
  98. @param(iAttr File attributes)
  99. @returns(@true if file is a symbolic link, @false otherwise)
  100. }
  101. function FPS_ISLNK(iAttr: TFileAttrs) : Boolean;
  102. {en
  103. Is file a regular file
  104. @param(iAttr File attributes)
  105. @returns(@true if file is a regular file, @false otherwise)
  106. }
  107. function FPS_ISREG(iAttr: TFileAttrs) : Boolean;
  108. {en
  109. Is file executable
  110. @param(sFileName File name)
  111. @returns(@true if file is executable, @false otherwise)
  112. }
  113. function FileIsExeLib(const sFileName : String) : Boolean;
  114. {en
  115. Is file console executable
  116. @param(sFileName File name)
  117. @returns(@true if file is console executable, @false otherwise)
  118. }
  119. function FileIsConsoleExe(const FileName: String): Boolean;
  120. {en
  121. Copies a file attributes (attributes, date/time, owner & group, permissions).
  122. @param(sSrc String expression that specifies the name of the file to be copied)
  123. @param(sDst String expression that specifies the target file name)
  124. @param(bDropReadOnlyFlag Drop read only attribute if @true)
  125. @returns(The function returns @true if successful, @false otherwise)
  126. }
  127. function FileIsReadOnly(iAttr: TFileAttrs): Boolean; inline;
  128. {en
  129. Returns path to a temporary name. It ensures that returned path doesn't exist,
  130. i.e., there is no filesystem entry by that name.
  131. If it could not create a unique temporary name then it returns empty string.
  132. @param(PathPrefix
  133. This parameter is added at the beginning of each path that is tried.
  134. The directories in this path are not created if they don't exist.
  135. If it is empty then the system temporary directory is used.
  136. For example:
  137. If PathPrefix is '/tmp/myfile' then files '/tmp/myfile~XXXXXX.tmp' are tried.
  138. The path '/tmp' must already exist.)
  139. }
  140. function GetTempName(PathPrefix: String; Extension: String = 'tmp'): String;
  141. {en
  142. Find file in the system PATH
  143. }
  144. function FindInSystemPath(var FileName: String): Boolean;
  145. {en
  146. Extract file root directory
  147. @param(FileName File name)
  148. }
  149. function ExtractRootDir(const FileName: String): String;
  150. (* File mapping/unmapping routines *)
  151. {en
  152. Create memory map of a file
  153. @param(sFileName Name of file to mapping)
  154. @param(FileMapRec TFileMapRec structure)
  155. @returns(The function returns @true if successful, @false otherwise)
  156. }
  157. function MapFile(const sFileName : String; out FileMapRec : TFileMapRec) : Boolean;
  158. {en
  159. Unmap previously mapped file
  160. @param(FileMapRec TFileMapRec structure)
  161. }
  162. procedure UnMapFile(var FileMapRec : TFileMapRec);
  163. {en
  164. Convert from console to UTF8 encoding.
  165. }
  166. function ConsoleToUTF8(const Source: String): RawByteString;
  167. { File handling functions}
  168. function mbFileOpen(const FileName: String; Mode: LongWord): System.THandle;
  169. function mbFileCreate(const FileName: String): System.THandle; overload; inline;
  170. function mbFileCreate(const FileName: String; Mode: LongWord): System.THandle; overload; inline;
  171. function mbFileCreate(const FileName: String; Mode, Rights: LongWord): System.THandle; overload;
  172. function mbFileAge(const FileName: String): DCBasicTypes.TFileTime;
  173. function mbFileGetTime(const FileName: String): DCBasicTypes.TFileTimeEx;
  174. // On success returns True.
  175. // nanoseconds supported
  176. function mbFileGetTime(const FileName: String;
  177. var ModificationTime: DCBasicTypes.TFileTimeEx;
  178. var CreationTime : DCBasicTypes.TFileTimeEx;
  179. var LastAccessTime : DCBasicTypes.TFileTimeEx): Boolean;
  180. // On success returns True.
  181. function mbFileSetTime(const FileName: String;
  182. ModificationTime: DCBasicTypes.TFileTime;
  183. CreationTime : DCBasicTypes.TFileTime = 0;
  184. LastAccessTime : DCBasicTypes.TFileTime = 0): Boolean;
  185. // nanoseconds supported
  186. function mbFileSetTimeEx(const FileName: String;
  187. ModificationTime: DCBasicTypes.TFileTimeEx;
  188. CreationTime : DCBasicTypes.TFileTimeEx;
  189. LastAccessTime : DCBasicTypes.TFileTimeEx): Boolean;
  190. {en
  191. Checks if a given file exists - it can be a real file or a link to a file,
  192. but it can be opened and read from.
  193. Even if the result is @false, we can't be sure a file by that name can be created,
  194. because there may still exist a directory or link by that name.
  195. }
  196. function mbFileExists(const FileName: String): Boolean;
  197. function mbFileAccess(const FileName: String; Mode: Word): Boolean;
  198. function mbFileGetAttr(const FileName: String): TFileAttrs; overload;
  199. function mbFileGetAttr(const FileName: String; out Attr: TFileAttributeData): Boolean; overload;
  200. function mbFileSetAttr(const FileName: String; Attr: TFileAttrs): Boolean;
  201. {en
  202. If any operation in Options is performed and does not succeed it is included
  203. in the result set. If all performed operations succeed the function returns empty set.
  204. For example for Options=[caoCopyTime, caoCopyOwnership] setting ownership
  205. doesn't succeed then the function returns [caoCopyOwnership].
  206. }
  207. function mbFileCopyAttr(const sSrc, sDst: String;
  208. Options: TCopyAttributesOptions;
  209. Errors: PCopyAttributesResult = nil): TCopyAttributesOptions;
  210. // Returns True on success.
  211. function mbFileSetReadOnly(const FileName: String; ReadOnly: Boolean): Boolean;
  212. function mbDeleteFile(const FileName: String): Boolean;
  213. function mbRenameFile(const OldName: String; NewName: String): Boolean;
  214. function mbFileSize(const FileName: String): Int64;
  215. function FileGetSize(Handle: System.THandle): Int64;
  216. function FileFlush(Handle: System.THandle): Boolean;
  217. function FileFlushData(Handle: System.THandle): Boolean;
  218. function FileIsReadOnlyEx(Handle: System.THandle): Boolean;
  219. function FileAllocate(Handle: System.THandle; Size: Int64): Boolean;
  220. { Directory handling functions}
  221. function mbGetCurrentDir: String;
  222. function mbSetCurrentDir(const NewDir: String): Boolean;
  223. {en
  224. Checks if a given directory exists - it may be a real directory or a link to directory.
  225. Even if the result is @false, we can't be sure a directory by that name can be created,
  226. because there may still exist a file or link by that name.
  227. }
  228. function mbDirectoryExists(const Directory : String) : Boolean;
  229. function mbCreateDir(const NewDir: String): Boolean;
  230. function mbRemoveDir(const Dir: String): Boolean;
  231. {en
  232. Checks if any file system entry exists at given path.
  233. It can be file, directory, link, etc. (links are not followed).
  234. }
  235. function mbFileSystemEntryExists(const Path: String): Boolean;
  236. function mbCompareFileNames(const FileName1, FileName2: String): Boolean;
  237. function mbFileSame(const FileName1, FileName2: String): Boolean;
  238. function mbFileSameVolume(const FileName1, FileName2: String) : Boolean;
  239. { Other functions }
  240. function mbGetEnvironmentString(Index : Integer) : String;
  241. {en
  242. Expands environment-variable strings and replaces
  243. them with the values defined for the current user
  244. }
  245. function mbExpandEnvironmentStrings(const FileName: String): String;
  246. function mbGetEnvironmentVariable(const sName: String): String;
  247. function mbSetEnvironmentVariable(const sName, sValue: String): Boolean;
  248. function mbUnsetEnvironmentVariable(const sName: String): Boolean;
  249. function mbSysErrorMessage: String; overload; inline;
  250. function mbSysErrorMessage(ErrorCode: Integer): String; overload;
  251. {en
  252. Get current module name
  253. }
  254. function mbGetModuleName(Address: Pointer = nil): String;
  255. function mbLoadLibrary(const Name: String): TLibHandle;
  256. function mbLoadLibraryEx(const Name: String): TLibHandle;
  257. function SafeGetProcAddress(Lib: TLibHandle; const ProcName: AnsiString): Pointer;
  258. {en
  259. Reads the concrete file's name that the link points to.
  260. If the link points to a link then it's resolved recursively
  261. until a valid file name that is not a link is found.
  262. @param(PathToLink Name of symbolic link (absolute path))
  263. @returns(The absolute filename the symbolic link name is pointing to,
  264. or an empty string when the link is invalid or
  265. the file it points to does not exist.)
  266. }
  267. function mbReadAllLinks(const PathToLink : String) : String;
  268. {en
  269. If PathToLink points to a link then it returns file that the link points to (recursively).
  270. If PathToLink does not point to a link then PathToLink value is returned.
  271. }
  272. function mbCheckReadLinks(const PathToLink : String) : String;
  273. {en
  274. Same as mbFileGetAttr, but dereferences any encountered links.
  275. }
  276. function mbFileGetAttrNoLinks(const FileName: String): TFileAttrs;
  277. {en
  278. Create a hard link to a file
  279. @param(Path Name of file)
  280. @param(LinkName Name of hard link)
  281. @returns(The function returns @true if successful, @false otherwise)
  282. }
  283. function CreateHardLink(const Path, LinkName: String) : Boolean;
  284. {en
  285. Create a symbolic link
  286. @param(Path Name of file)
  287. @param(LinkName Name of symbolic link)
  288. @returns(The function returns @true if successful, @false otherwise)
  289. }
  290. function CreateSymLink(const Path, LinkName: string; Attr: UInt32 = faInvalidAttributes) : Boolean;
  291. {en
  292. Read destination of symbolic link
  293. @param(LinkName Name of symbolic link)
  294. @returns(The file name/path the symbolic link name is pointing to.
  295. The path may be relative to link's location.)
  296. }
  297. function ReadSymLink(const LinkName : String) : String;
  298. {en
  299. Sets the last-error code for the calling thread
  300. }
  301. procedure SetLastOSError(LastError: Integer);
  302. function GetTickCountEx: UInt64;
  303. implementation
  304. uses
  305. {$IF DEFINED(MSWINDOWS)}
  306. DCDateTimeUtils, DCWindows, DCNtfsLinks,
  307. {$ENDIF}
  308. {$IF DEFINED(UNIX)}
  309. Unix, dl,
  310. {$ENDIF}
  311. DCStrUtils, LazUTF8;
  312. {$IFDEF UNIX}
  313. function SetModeReadOnly(mode: TMode; ReadOnly: Boolean): TMode;
  314. begin
  315. mode := mode and not (S_IWUSR or S_IWGRP or S_IWOTH);
  316. if ReadOnly = False then
  317. begin
  318. if (mode AND S_IRUSR) = S_IRUSR then
  319. mode := mode or S_IWUSR;
  320. if (mode AND S_IRGRP) = S_IRGRP then
  321. mode := mode or S_IWGRP;
  322. if (mode AND S_IROTH) = S_IROTH then
  323. mode := mode or S_IWOTH;
  324. end;
  325. Result := mode;
  326. end;
  327. {$ENDIF}
  328. {$IF DEFINED(MSWINDOWS)}
  329. const
  330. AccessModes: array[0..2] of DWORD = (
  331. GENERIC_READ,
  332. GENERIC_WRITE,
  333. GENERIC_READ or GENERIC_WRITE);
  334. ShareModes: array[0..4] of DWORD = (
  335. 0,
  336. 0,
  337. FILE_SHARE_READ,
  338. FILE_SHARE_WRITE,
  339. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE);
  340. OpenFlags: array[0..3] of DWORD = (
  341. 0,
  342. FILE_FLAG_WRITE_THROUGH,
  343. FILE_FLAG_NO_BUFFERING,
  344. FILE_FLAG_WRITE_THROUGH or FILE_FLAG_NO_BUFFERING);
  345. var
  346. CurrentDirectory: String;
  347. PerformanceFrequency: LARGE_INTEGER;
  348. {$ELSEIF DEFINED(UNIX)}
  349. const
  350. {$IF NOT DECLARED(O_SYNC)}
  351. O_SYNC = 0;
  352. {$ENDIF}
  353. {$IF NOT DECLARED(O_DIRECT)}
  354. O_DIRECT = 0;
  355. {$ENDIF}
  356. AccessModes: array[0..2] of cInt = (
  357. O_RdOnly,
  358. O_WrOnly,
  359. O_RdWr);
  360. OpenFlags: array[0..3] of cInt = (
  361. 0,
  362. O_SYNC,
  363. O_DIRECT,
  364. O_SYNC or O_DIRECT);
  365. {$ENDIF}
  366. function FPS_ISDIR(iAttr: TFileAttrs) : Boolean; inline;
  367. {$IFDEF MSWINDOWS}
  368. begin
  369. Result := (iAttr and FILE_ATTRIBUTE_DIRECTORY <> 0);
  370. end;
  371. {$ELSE}
  372. begin
  373. Result := BaseUnix.FPS_ISDIR(TMode(iAttr));
  374. end;
  375. {$ENDIF}
  376. function FPS_ISLNK(iAttr: TFileAttrs) : Boolean; inline;
  377. {$IFDEF MSWINDOWS}
  378. begin
  379. Result := (iAttr and FILE_ATTRIBUTE_REPARSE_POINT <> 0);
  380. end;
  381. {$ELSE}
  382. begin
  383. Result := BaseUnix.FPS_ISLNK(TMode(iAttr));
  384. end;
  385. {$ENDIF}
  386. function FPS_ISREG(iAttr: TFileAttrs) : Boolean; inline;
  387. {$IFDEF MSWINDOWS}
  388. begin
  389. Result := (iAttr and FILE_ATTRIBUTE_DIRECTORY = 0);
  390. end;
  391. {$ELSE}
  392. begin
  393. Result := BaseUnix.FPS_ISREG(TMode(iAttr));
  394. end;
  395. {$ENDIF}
  396. function FileIsExeLib(const sFileName : String) : Boolean;
  397. var
  398. fsExeLib : TFileStreamEx;
  399. {$IFDEF MSWINDOWS}
  400. Sign : Word;
  401. {$ELSE}
  402. Sign : DWord;
  403. {$ENDIF}
  404. begin
  405. Result := False;
  406. if mbFileExists(sFileName) and (mbFileSize(sFileName) >= SizeOf(Sign)) then
  407. try
  408. fsExeLib := TFileStreamEx.Create(sFileName, fmOpenRead or fmShareDenyNone);
  409. try
  410. {$IFDEF MSWINDOWS}
  411. Sign := fsExeLib.ReadWord;
  412. Result := (Sign = $5A4D);
  413. {$ELSE}
  414. Sign := fsExeLib.ReadDWord;
  415. Result := (Sign = $464C457F);
  416. {$ENDIF}
  417. finally
  418. fsExeLib.Free;
  419. end;
  420. except
  421. Result := False;
  422. end;
  423. end;
  424. function FileIsConsoleExe(const FileName: String): Boolean;
  425. {$IF DEFINED(UNIX)}
  426. begin
  427. Result:= True;
  428. end;
  429. {$ELSE}
  430. var
  431. fsFileStream: TFileStreamEx;
  432. begin
  433. Result:= False;
  434. try
  435. fsFileStream:= TFileStreamEx.Create(FileName, fmOpenRead or fmShareDenyNone);
  436. try
  437. if fsFileStream.ReadWord = IMAGE_DOS_SIGNATURE then
  438. begin
  439. fsFileStream.Seek(60, soBeginning);
  440. fsFileStream.Seek(fsFileStream.ReadDWord, soBeginning);
  441. if fsFileStream.ReadDWord = IMAGE_NT_SIGNATURE then
  442. begin
  443. fsFileStream.Seek(88, soCurrent);
  444. Result:= (fsFileStream.ReadWord = IMAGE_SUBSYSTEM_WINDOWS_CUI);
  445. end;
  446. end;
  447. finally
  448. fsFileStream.Free;
  449. end;
  450. except
  451. Result:= False;
  452. end;
  453. end;
  454. {$ENDIF}
  455. function FileIsReadOnly(iAttr: TFileAttrs): Boolean;
  456. {$IFDEF MSWINDOWS}
  457. begin
  458. Result:= (iAttr and (faReadOnly or faHidden or faSysFile)) <> 0;
  459. end;
  460. {$ELSE}
  461. begin
  462. Result:= (((iAttr AND S_IRUSR) = S_IRUSR) and ((iAttr AND S_IWUSR) <> S_IWUSR));
  463. end;
  464. {$ENDIF}
  465. function mbFileCopyAttr(const sSrc, sDst: String;
  466. Options: TCopyAttributesOptions; Errors: PCopyAttributesResult
  467. ): TCopyAttributesOptions;
  468. {$IFDEF MSWINDOWS}
  469. var
  470. Attr: TWin32FileAttributeData;
  471. Option: TCopyAttributesOption;
  472. ModificationTime, CreationTime, LastAccessTime: DCBasicTypes.TFileTime;
  473. begin
  474. Result := [];
  475. if not GetFileAttributesExW(PWideChar(UTF16LongName(sSrc)), GetFileExInfoStandard, @Attr) then
  476. begin
  477. Result := Options;
  478. if Assigned(Errors) then
  479. begin
  480. for Option in Result do
  481. Errors^[Option]:= GetLastOSError;
  482. end;
  483. Exit;
  484. end;
  485. if [caoCopyAttributes, caoCopyAttrEx] * Options <> [] then
  486. begin
  487. if (not (caoCopyAttributes in Options)) and (Attr.dwFileAttributes and faDirectory = 0) then
  488. Attr.dwFileAttributes := (Attr.dwFileAttributes or faArchive);
  489. if (caoRemoveReadOnlyAttr in Options) and ((Attr.dwFileAttributes and faReadOnly) <> 0) then
  490. Attr.dwFileAttributes := (Attr.dwFileAttributes and not faReadOnly);
  491. if not mbFileSetAttr(sDst, Attr.dwFileAttributes) then
  492. begin
  493. Include(Result, caoCopyAttributes);
  494. if Assigned(Errors) then Errors^[caoCopyAttributes]:= GetLastOSError;
  495. end;
  496. end;
  497. if not FPS_ISLNK(Attr.dwFileAttributes) then
  498. begin
  499. if (caoCopyXattributes in Options) then
  500. begin
  501. if not mbFileCopyXattr(sSrc, sDst) then
  502. begin
  503. Include(Result, caoCopyXattributes);
  504. if Assigned(Errors) then Errors^[caoCopyXattributes]:= GetLastOSError;
  505. end;
  506. end;
  507. if ([caoCopyTime, caoCopyTimeEx] * Options <> []) then
  508. begin
  509. if not (caoCopyTime in Options) then
  510. begin
  511. CreationTime:= 0;
  512. LastAccessTime:= 0;
  513. end
  514. else begin
  515. CreationTime:= DCBasicTypes.TFileTime(Attr.ftCreationTime);
  516. LastAccessTime:= DCBasicTypes.TFileTime(Attr.ftLastAccessTime);
  517. end;
  518. ModificationTime:= DCBasicTypes.TFileTime(Attr.ftLastWriteTime);
  519. if not mbFileSetTime(sDst, ModificationTime, CreationTime, LastAccessTime) then
  520. begin
  521. Include(Result, caoCopyTime);
  522. if Assigned(Errors) then Errors^[caoCopyTime]:= GetLastOSError;
  523. end;
  524. end;
  525. end;
  526. if caoCopyPermissions in Options then
  527. begin
  528. if not CopyNtfsPermissions(sSrc, sDst) then
  529. begin
  530. Include(Result, caoCopyPermissions);
  531. if Assigned(Errors) then Errors^[caoCopyPermissions]:= GetLastOSError;
  532. end;
  533. end;
  534. end;
  535. {$ELSE} // *nix
  536. var
  537. Option: TCopyAttributesOption;
  538. StatInfo : TDCStat;
  539. modificationTime: TFileTimeEx;
  540. creationTime: TFileTimeEx;
  541. lastAccessTime: TFileTimeEx;
  542. mode : TMode;
  543. begin
  544. if DC_fpLStat(UTF8ToSys(sSrc), StatInfo) < 0 then
  545. begin
  546. Result := Options;
  547. if Assigned(Errors) then
  548. begin
  549. for Option in Result do
  550. Errors^[Option]:= GetLastOSError;
  551. end;
  552. end
  553. else begin
  554. Result := [];
  555. if FPS_ISLNK(StatInfo.st_mode) then
  556. begin
  557. if caoCopyOwnership in Options then
  558. begin
  559. // Only group/owner can be set for links.
  560. if fpLChown(sDst, StatInfo.st_uid, StatInfo.st_gid) = -1 then
  561. begin
  562. Include(Result, caoCopyOwnership);
  563. if Assigned(Errors) then Errors^[caoCopyOwnership]:= GetLastOSError;
  564. end;
  565. end;
  566. {$IF DEFINED(HAIKU)}
  567. if caoCopyXattributes in Options then
  568. begin
  569. if not mbFileCopyXattr(sSrc, sDst) then
  570. begin
  571. Include(Result, caoCopyXattributes);
  572. if Assigned(Errors) then Errors^[caoCopyXattributes]:= GetLastOSError;
  573. end;
  574. end;
  575. {$ENDIF}
  576. end
  577. else
  578. begin
  579. if caoCopyTime in Options then
  580. begin
  581. modificationTime:= StatInfo.mtime;
  582. lastAccessTime:= StatInfo.atime;
  583. creationTime:= StatInfo.birthtime;
  584. if DC_FileSetTime(sDst, modificationTime, creationTime, lastAccessTime) = false then
  585. begin
  586. Include(Result, caoCopyTime);
  587. if Assigned(Errors) then Errors^[caoCopyTime]:= GetLastOSError;
  588. end;
  589. end;
  590. if caoCopyOwnership in Options then
  591. begin
  592. if fpChown(PChar(UTF8ToSys(sDst)), StatInfo.st_uid, StatInfo.st_gid) = -1 then
  593. begin
  594. Include(Result, caoCopyOwnership);
  595. if Assigned(Errors) then Errors^[caoCopyOwnership]:= GetLastOSError;
  596. end;
  597. end;
  598. if caoCopyAttributes in Options then
  599. begin
  600. mode := StatInfo.st_mode;
  601. if caoRemoveReadOnlyAttr in Options then
  602. mode := SetModeReadOnly(mode, False);
  603. if fpChmod(UTF8ToSys(sDst), mode) = -1 then
  604. begin
  605. Include(Result, caoCopyAttributes);
  606. if Assigned(Errors) then Errors^[caoCopyAttributes]:= GetLastOSError;
  607. end;
  608. end;
  609. {$IF DEFINED(LINUX) or DEFINED(HAIKU)}
  610. if caoCopyXattributes in Options then
  611. begin
  612. if not mbFileCopyXattr(sSrc, sDst) then
  613. begin
  614. Include(Result, caoCopyXattributes);
  615. if Assigned(Errors) then Errors^[caoCopyXattributes]:= GetLastOSError;
  616. end;
  617. end;
  618. {$ENDIF}
  619. end;
  620. end;
  621. end;
  622. {$ENDIF}
  623. function GetTempName(PathPrefix: String; Extension: String): String;
  624. const
  625. MaxTries = 100;
  626. var
  627. FileName: String;
  628. TryNumber: Integer = 0;
  629. begin
  630. if PathPrefix = '' then
  631. PathPrefix := GetTempDir
  632. else begin
  633. FileName:= ExtractOnlyFileName(PathPrefix);
  634. PathPrefix:= ExtractFilePath(PathPrefix);
  635. // Generated file name should be less the maximum file name length
  636. if (Length(FileName) > 0) then PathPrefix += UTF8Copy(FileName, 1, 48) + '~';
  637. end;
  638. if (Length(Extension) > 0) then
  639. begin
  640. if (not StrBegins(Extension, ExtensionSeparator)) then
  641. Extension := ExtensionSeparator + Extension;
  642. end;
  643. repeat
  644. Result := PathPrefix + IntToStr(System.Random(MaxInt)) + Extension;
  645. Inc(TryNumber);
  646. if TryNumber = MaxTries then
  647. Exit('');
  648. until not mbFileSystemEntryExists(Result);
  649. end;
  650. function FindInSystemPath(var FileName: String): Boolean;
  651. var
  652. I: Integer;
  653. Path, FullName: String;
  654. Value: TDynamicStringArray;
  655. begin
  656. Path:= mbGetEnvironmentVariable('PATH');
  657. Value:= SplitString(Path, PathSeparator);
  658. for I:= Low(Value) to High(Value) do
  659. begin
  660. FullName:= IncludeTrailingPathDelimiter(Value[I]) + FileName;
  661. if mbFileExists(FullName) then
  662. begin
  663. FileName:= FullName;
  664. Exit(True);
  665. end;
  666. end;
  667. Result:= False;
  668. end;
  669. function ExtractRootDir(const FileName: String): String;
  670. {$IFDEF UNIX}
  671. begin
  672. Result:= ExcludeTrailingPathDelimiter(FindMountPointPath(ExcludeTrailingPathDelimiter(FileName)));
  673. end;
  674. {$ELSE}
  675. begin
  676. Result:= ExtractFileDrive(FileName);
  677. end;
  678. {$ENDIF}
  679. function MapFile(const sFileName : String; out FileMapRec : TFileMapRec) : Boolean;
  680. {$IFDEF MSWINDOWS}
  681. begin
  682. Result := False;
  683. with FileMapRec do
  684. begin
  685. MappedFile := nil;
  686. MappingHandle := 0;
  687. FileHandle := mbFileOpen(sFileName, fmOpenRead);
  688. if FileHandle = feInvalidHandle then Exit;
  689. Int64Rec(FileSize).Lo := GetFileSize(FileHandle, @Int64Rec(FileSize).Hi);
  690. if FileSize = 0 then // Cannot map empty files
  691. begin
  692. UnMapFile(FileMapRec);
  693. Exit;
  694. end;
  695. MappingHandle := CreateFileMapping(FileHandle, nil, PAGE_READONLY, 0, 0, nil);
  696. if MappingHandle = 0 then
  697. begin
  698. UnMapFile(FileMapRec);
  699. Exit;
  700. end;
  701. MappedFile := MapViewOfFile(MappingHandle, FILE_MAP_READ, 0, 0, 0);
  702. if not Assigned(MappedFile) then
  703. begin
  704. UnMapFile(FileMapRec);
  705. Exit;
  706. end;
  707. end;
  708. Result := True;
  709. end;
  710. {$ELSE}
  711. var
  712. StatInfo: BaseUnix.Stat;
  713. begin
  714. Result:= False;
  715. with FileMapRec do
  716. begin
  717. MappedFile := nil;
  718. FileHandle:= mbFileOpen(sFileName, fmOpenRead);
  719. if FileHandle = feInvalidHandle then Exit;
  720. if fpfstat(FileHandle, StatInfo) <> 0 then
  721. begin
  722. UnMapFile(FileMapRec);
  723. Exit;
  724. end;
  725. FileSize := StatInfo.st_size;
  726. if FileSize = 0 then // Cannot map empty files
  727. begin
  728. UnMapFile(FileMapRec);
  729. Exit;
  730. end;
  731. MappedFile:= fpmmap(nil,FileSize,PROT_READ, MAP_PRIVATE{SHARED},FileHandle,0 );
  732. if MappedFile = MAP_FAILED then
  733. begin
  734. MappedFile := nil;
  735. UnMapFile(FileMapRec);
  736. Exit;
  737. end;
  738. end;
  739. Result := True;
  740. end;
  741. {$ENDIF}
  742. procedure UnMapFile(var FileMapRec : TFileMapRec);
  743. {$IFDEF MSWINDOWS}
  744. begin
  745. with FileMapRec do
  746. begin
  747. if Assigned(MappedFile) then
  748. begin
  749. UnmapViewOfFile(MappedFile);
  750. MappedFile := nil;
  751. end;
  752. if MappingHandle <> 0 then
  753. begin
  754. CloseHandle(MappingHandle);
  755. MappingHandle := 0;
  756. end;
  757. if FileHandle <> feInvalidHandle then
  758. begin
  759. FileClose(FileHandle);
  760. FileHandle := feInvalidHandle;
  761. end;
  762. end;
  763. end;
  764. {$ELSE}
  765. begin
  766. with FileMapRec do
  767. begin
  768. if FileHandle <> feInvalidHandle then
  769. begin
  770. fpClose(FileHandle);
  771. FileHandle := feInvalidHandle;
  772. end;
  773. if Assigned(MappedFile) then
  774. begin
  775. fpmunmap(MappedFile,FileSize);
  776. MappedFile := nil;
  777. end;
  778. end;
  779. end;
  780. {$ENDIF}
  781. function ConsoleToUTF8(const Source: String): RawByteString;
  782. {$IFDEF MSWINDOWS}
  783. begin
  784. Result:= CeOemToUtf8(Source);
  785. end;
  786. {$ELSE}
  787. begin
  788. Result:= CeSysToUtf8(Source);
  789. end;
  790. {$ENDIF}
  791. function mbFileOpen(const FileName: String; Mode: LongWord): System.THandle;
  792. {$IFDEF MSWINDOWS}
  793. const
  794. ft: TFileTime = ( dwLowDateTime: $FFFFFFFF; dwHighDateTime: $FFFFFFFF; );
  795. begin
  796. Result:= CreateFileW(PWideChar(UTF16LongName(FileName)),
  797. AccessModes[Mode and 3] or ((Mode and fmOpenNoATime) shr 10),
  798. ShareModes[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
  799. FILE_ATTRIBUTE_NORMAL, OpenFlags[(Mode shr 16) and 3]);
  800. if (Mode and fmOpenNoATime <> 0) then
  801. begin
  802. if (Result <> feInvalidHandle) then
  803. SetFileTime(Result, nil, @ft, @ft)
  804. else if GetLastError = ERROR_ACCESS_DENIED then
  805. Result := mbFileOpen(FileName, Mode and not fmOpenNoATime);
  806. end;
  807. end;
  808. {$ELSE}
  809. begin
  810. repeat
  811. Result:= fpOpen(UTF8ToSys(FileName), AccessModes[Mode and 3] or
  812. OpenFlags[(Mode shr 16) and 3] or O_CLOEXEC);
  813. until (Result <> -1) or (fpgeterrno <> ESysEINTR);
  814. if Result <> feInvalidHandle then
  815. begin
  816. FileCloseOnExec(Result);
  817. {$IF DEFINED(DARWIN)}
  818. if (Mode and (fmOpenSync or fmOpenDirect) <> 0) then
  819. begin
  820. if (FpFcntl(Result, F_NOCACHE, 1) = -1) then
  821. begin
  822. FileClose(Result);
  823. Exit(feInvalidHandle);
  824. end;
  825. end;
  826. {$ENDIF}
  827. end;
  828. end;
  829. {$ENDIF}
  830. function mbFileCreate(const FileName: String): System.THandle;
  831. begin
  832. Result:= mbFileCreate(FileName, fmShareDenyWrite);
  833. end;
  834. function mbFileCreate(const FileName: String; Mode: LongWord): System.THandle;
  835. begin
  836. Result:= mbFileCreate(FileName, Mode, 438); // 438 = 666 octal
  837. end;
  838. function mbFileCreate(const FileName: String; Mode, Rights: LongWord): System.THandle;
  839. {$IFDEF MSWINDOWS}
  840. begin
  841. Result:= CreateFileW(PWideChar(UTF16LongName(FileName)), GENERIC_READ or GENERIC_WRITE,
  842. ShareModes[(Mode and $F0) shr 4], nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
  843. OpenFlags[(Mode shr 16) and 3]);
  844. end;
  845. {$ELSE}
  846. begin
  847. repeat
  848. Result:= fpOpen(UTF8ToSys(FileName), O_Creat or O_RdWr or O_Trunc or
  849. OpenFlags[(Mode shr 16) and 3] or O_CLOEXEC, Rights);
  850. until (Result <> -1) or (fpgeterrno <> ESysEINTR);
  851. if Result <> feInvalidHandle then
  852. begin
  853. FileCloseOnExec(Result);
  854. {$IF DEFINED(DARWIN)}
  855. if (Mode and (fmOpenSync or fmOpenDirect) <> 0) then
  856. begin
  857. if (FpFcntl(Result, F_NOCACHE, 1) = -1) then
  858. begin
  859. FileClose(Result);
  860. Exit(feInvalidHandle);
  861. end;
  862. end;
  863. {$ENDIF}
  864. end;
  865. end;
  866. {$ENDIF}
  867. function mbFileAge(const FileName: String): DCBasicTypes.TFileTime;
  868. {$IFDEF MSWINDOWS}
  869. var
  870. Handle: System.THandle;
  871. FindData: TWin32FindDataW;
  872. begin
  873. Handle := FindFirstFileW(PWideChar(UTF16LongName(FileName)), FindData);
  874. if Handle <> INVALID_HANDLE_VALUE then
  875. begin
  876. Windows.FindClose(Handle);
  877. if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  878. Exit(DCBasicTypes.TWinFileTime(FindData.ftLastWriteTime));
  879. end;
  880. Result:= DCBasicTypes.TFileTime(-1);
  881. end;
  882. {$ELSE}
  883. var
  884. Info: BaseUnix.Stat;
  885. begin
  886. Result:= DCBasicTypes.TFileTime(-1);
  887. if fpStat(UTF8ToSys(FileName), Info) >= 0 then
  888. {$PUSH}{$R-}
  889. Result := Info.st_mtime;
  890. {$POP}
  891. end;
  892. {$ENDIF}
  893. function mbFileGetTime(const FileName: String): DCBasicTypes.TFileTimeEx;
  894. var
  895. CreationTime, LastAccessTime: DCBasicTypes.TFileTimeEx;
  896. begin
  897. if not mbFileGetTime(FileName, Result, CreationTime, LastAccessTime) then
  898. Result:= TFileTimeExNull;
  899. end;
  900. function mbFileGetTime(const FileName: String;
  901. var ModificationTime: DCBasicTypes.TFileTimeEx;
  902. var CreationTime : DCBasicTypes.TFileTimeEx;
  903. var LastAccessTime : DCBasicTypes.TFileTimeEx): Boolean;
  904. {$IFDEF MSWINDOWS}
  905. var
  906. Handle: System.THandle;
  907. begin
  908. Handle := CreateFileW(PWideChar(UTF16LongName(FileName)),
  909. FILE_READ_ATTRIBUTES,
  910. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  911. nil,
  912. OPEN_EXISTING,
  913. FILE_FLAG_BACKUP_SEMANTICS, // needed for opening directories
  914. 0);
  915. if Handle <> INVALID_HANDLE_VALUE then
  916. begin
  917. Result := Windows.GetFileTime(Handle,
  918. @CreationTime,
  919. @LastAccessTime,
  920. @ModificationTime);
  921. CloseHandle(Handle);
  922. end
  923. else
  924. Result := False;
  925. end;
  926. {$ELSE}
  927. var
  928. StatInfo : TDCStat;
  929. begin
  930. Result := DC_fpLStat(UTF8ToSys(FileName), StatInfo) >= 0;
  931. if Result then
  932. begin
  933. ModificationTime:= StatInfo.mtime;
  934. LastAccessTime:= StatInfo.atime;
  935. {$IF DEFINED(DARWIN)}
  936. CreationTime:= StatInfo.birthtime;
  937. {$ELSE}
  938. CreationTime:= StatInfo.ctime;
  939. {$ENDIF}
  940. end;
  941. end;
  942. {$ENDIF}
  943. function mbFileSetTime(const FileName: String;
  944. ModificationTime: DCBasicTypes.TFileTime;
  945. CreationTime : DCBasicTypes.TFileTime = 0;
  946. LastAccessTime : DCBasicTypes.TFileTime = 0): Boolean;
  947. {$IFDEF MSWINDOWS}
  948. begin
  949. Result:= mbFileSetTimeEx(FileName, ModificationTime, CreationTime, LastAccessTime);
  950. end;
  951. {$ELSE}
  952. var
  953. NewModificationTime: DCBasicTypes.TFileTimeEx;
  954. NewCreationTime : DCBasicTypes.TFileTimeEx;
  955. NewLastAccessTime : DCBasicTypes.TFileTimeEx;
  956. begin
  957. NewModificationTime:= specialize IfThen<TFileTimeEx>(ModificationTime<>0, TFileTimeEx.create(ModificationTime), TFileTimeExNull);
  958. NewCreationTime:= specialize IfThen<TFileTimeEx>(CreationTime<>0, TFileTimeEx.create(CreationTime), TFileTimeExNull);
  959. NewLastAccessTime:= specialize IfThen<TFileTimeEx>(LastAccessTime<>0, TFileTimeEx.create(LastAccessTime), TFileTimeExNull);
  960. Result:= mbFileSetTimeEx(FileName, NewModificationTime, NewCreationTime, NewLastAccessTime);
  961. end;
  962. {$ENDIF}
  963. function mbFileSetTimeEx(const FileName: String;
  964. ModificationTime: DCBasicTypes.TFileTimeEx;
  965. CreationTime : DCBasicTypes.TFileTimeEx;
  966. LastAccessTime : DCBasicTypes.TFileTimeEx): Boolean;
  967. {$IFDEF MSWINDOWS}
  968. var
  969. Handle: System.THandle;
  970. PWinModificationTime: Windows.LPFILETIME = nil;
  971. PWinCreationTime: Windows.LPFILETIME = nil;
  972. PWinLastAccessTime: Windows.LPFILETIME = nil;
  973. begin
  974. Handle := CreateFileW(PWideChar(UTF16LongName(FileName)),
  975. FILE_WRITE_ATTRIBUTES,
  976. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  977. nil,
  978. OPEN_EXISTING,
  979. FILE_FLAG_BACKUP_SEMANTICS, // needed for opening directories
  980. 0);
  981. if Handle <> INVALID_HANDLE_VALUE then
  982. begin
  983. if ModificationTime <> 0 then
  984. begin
  985. PWinModificationTime := @ModificationTime;
  986. end;
  987. if CreationTime <> 0 then
  988. begin
  989. PWinCreationTime := @CreationTime;
  990. end;
  991. if LastAccessTime <> 0 then
  992. begin
  993. PWinLastAccessTime := @LastAccessTime;
  994. end;
  995. Result := Windows.SetFileTime(Handle,
  996. PWinCreationTime,
  997. PWinLastAccessTime,
  998. PWinModificationTime);
  999. CloseHandle(Handle);
  1000. end
  1001. else
  1002. Result := False;
  1003. end;
  1004. {$ELSE}
  1005. var
  1006. CurrentModificationTime, CurrentCreationTime, CurrentLastAccessTime: DCBasicTypes.TFileTimeEx;
  1007. begin
  1008. if mbFileGetTime(FileName, CurrentModificationTime, CurrentCreationTime, CurrentLastAccessTime) then
  1009. begin
  1010. if ModificationTime<>TFileTimeExNull then CurrentModificationTime:= ModificationTime;
  1011. if CreationTime<>TFileTimeExNull then CurrentCreationTime:= CreationTime;
  1012. if LastAccessTime<>TFileTimeExNull then CurrentLastAccessTime:= LastAccessTime;
  1013. Result := DC_FileSetTime(FileName, CurrentModificationTime, CurrentCreationTime, CurrentLastAccessTime);
  1014. end
  1015. else
  1016. begin
  1017. Result:=False;
  1018. end;
  1019. end;
  1020. {$ENDIF}
  1021. function mbFileExists(const FileName: String) : Boolean;
  1022. {$IFDEF MSWINDOWS}
  1023. var
  1024. Attr: DWORD;
  1025. begin
  1026. Attr:= GetFileAttributesW(PWideChar(UTF16LongName(FileName)));
  1027. if Attr <> DWORD(-1) then
  1028. Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) = 0
  1029. else
  1030. Result:=False;
  1031. end;
  1032. {$ELSE}
  1033. var
  1034. Info: BaseUnix.Stat;
  1035. begin
  1036. // Can use fpStat, because link to an existing filename can be opened as if it were a real file.
  1037. if fpStat(UTF8ToSys(FileName), Info) >= 0 then
  1038. Result:= fpS_ISREG(Info.st_mode)
  1039. else
  1040. Result:= False;
  1041. end;
  1042. {$ENDIF}
  1043. function mbFileAccess(const FileName: String; Mode: Word): Boolean;
  1044. {$IFDEF MSWINDOWS}
  1045. const
  1046. AccessMode: array[0..2] of DWORD = (
  1047. GENERIC_READ,
  1048. GENERIC_WRITE,
  1049. GENERIC_READ or GENERIC_WRITE);
  1050. var
  1051. hFile: System.THandle;
  1052. dwDesiredAccess: DWORD;
  1053. dwShareMode: DWORD = 0;
  1054. begin
  1055. dwDesiredAccess := AccessMode[Mode and 3];
  1056. if Mode = fmOpenRead then // If checking Read mode no sharing mode given
  1057. Mode := Mode or fmShareDenyNone;
  1058. dwShareMode := ShareModes[(Mode and $F0) shr 4];
  1059. hFile:= CreateFileW(PWideChar(UTF16LongName(FileName)), dwDesiredAccess, dwShareMode,
  1060. nil, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
  1061. Result := hFile <> INVALID_HANDLE_VALUE;
  1062. if Result then
  1063. FileClose(hFile);
  1064. end;
  1065. {$ELSE}
  1066. const
  1067. AccessMode: array[0..2] of LongInt = (
  1068. R_OK,
  1069. W_OK,
  1070. R_OK or W_OK);
  1071. begin
  1072. Result:= fpAccess(UTF8ToSys(FileName), AccessMode[Mode and 3]) = 0;
  1073. end;
  1074. {$ENDIF}
  1075. {$IFOPT R+}
  1076. {$DEFINE uOSUtilsRangeCheckOn}
  1077. {$R-}
  1078. {$ENDIF}
  1079. function mbFileGetAttr(const FileName: String): TFileAttrs;
  1080. {$IFDEF MSWINDOWS}
  1081. begin
  1082. Result := GetFileAttributesW(PWideChar(UTF16LongName(FileName)));
  1083. end;
  1084. {$ELSE}
  1085. var
  1086. Info: BaseUnix.Stat;
  1087. begin
  1088. if fpLStat(UTF8ToSys(FileName), @Info) >= 0 then
  1089. Result:= Info.st_mode
  1090. else
  1091. Result:= faInvalidAttributes;
  1092. end;
  1093. {$ENDIF}
  1094. function mbFileGetAttr(const FileName: String; out Attr: TFileAttributeData): Boolean;
  1095. {$IFDEF MSWINDOWS}
  1096. var
  1097. Handle: THandle;
  1098. fInfoLevelId: FINDEX_INFO_LEVELS;
  1099. FileInfo: Windows.TWin32FindDataW;
  1100. begin
  1101. if CheckWin32Version(6, 1) then
  1102. fInfoLevelId:= FindExInfoBasic
  1103. else begin
  1104. fInfoLevelId:= FindExInfoStandard;
  1105. end;
  1106. Handle:= FindFirstFileExW(PWideChar(UTF16LongName(FileName)), fInfoLevelId,
  1107. @FileInfo, FindExSearchNameMatch, nil, 0);
  1108. Result:= Handle <> INVALID_HANDLE_VALUE;
  1109. if Result then
  1110. begin
  1111. FindClose(Handle);
  1112. // If a reparse point tag is not a name surrogate then remove reparse point attribute
  1113. // Fixes bug: http://doublecmd.sourceforge.net/mantisbt/view.php?id=531
  1114. if (FileInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT <> 0) then
  1115. begin
  1116. if (FileInfo.dwReserved0 and $20000000 = 0) then
  1117. FileInfo.dwFileAttributes-= FILE_ATTRIBUTE_REPARSE_POINT;
  1118. end;
  1119. Int64Rec(Attr.Size).Lo:= FileInfo.nFileSizeLow;
  1120. Int64Rec(Attr.Size).Hi:= FileInfo.nFileSizeHigh;
  1121. Move(FileInfo, Attr.FindData, SizeOf(TWin32FileAttributeData));
  1122. end;
  1123. end;
  1124. {$ELSE}
  1125. begin
  1126. Result:= fpLStat(UTF8ToSys(FileName), Attr.FindData) >= 0;
  1127. if Result then
  1128. begin
  1129. Attr.Size:= Attr.FindData.st_size;
  1130. end;
  1131. end;
  1132. {$ENDIF}
  1133. function mbFileSetAttr(const FileName: String; Attr: TFileAttrs): Boolean;
  1134. {$IFDEF MSWINDOWS}
  1135. begin
  1136. Result:= SetFileAttributesW(PWideChar(UTF16LongName(FileName)), Attr);
  1137. end;
  1138. {$ELSE}
  1139. begin
  1140. Result:= fpchmod(UTF8ToSys(FileName), Attr) = 0;
  1141. end;
  1142. {$ENDIF}
  1143. {$IFDEF uOSUtilsRangeCheckOn}
  1144. {$R+}
  1145. {$UNDEF uOSUtilsRangeCheckOn}
  1146. {$ENDIF}
  1147. function mbFileSetReadOnly(const FileName: String; ReadOnly: Boolean): Boolean;
  1148. {$IFDEF MSWINDOWS}
  1149. var
  1150. iAttr: DWORD;
  1151. wFileName: UnicodeString;
  1152. begin
  1153. wFileName:= UTF16LongName(FileName);
  1154. iAttr := GetFileAttributesW(PWideChar(wFileName));
  1155. if iAttr = DWORD(-1) then Exit(False);
  1156. if ReadOnly then
  1157. iAttr:= iAttr or faReadOnly
  1158. else
  1159. iAttr:= iAttr and not (faReadOnly or faHidden or faSysFile);
  1160. Result:= SetFileAttributesW(PWideChar(wFileName), iAttr) = True;
  1161. end;
  1162. {$ELSE}
  1163. var
  1164. StatInfo: BaseUnix.Stat;
  1165. mode: TMode;
  1166. begin
  1167. if fpStat(UTF8ToSys(FileName), StatInfo) <> 0 then Exit(False);
  1168. mode := SetModeReadOnly(StatInfo.st_mode, ReadOnly);
  1169. Result:= fpchmod(UTF8ToSys(FileName), mode) = 0;
  1170. end;
  1171. {$ENDIF}
  1172. function mbDeleteFile(const FileName: String): Boolean;
  1173. {$IFDEF MSWINDOWS}
  1174. begin
  1175. Result:= Windows.DeleteFileW(PWideChar(UTF16LongName(FileName)));
  1176. if not Result then Result:= (GetLastError = ERROR_FILE_NOT_FOUND);
  1177. end;
  1178. {$ELSE}
  1179. begin
  1180. Result:= fpUnLink(UTF8ToSys(FileName)) = 0;
  1181. if not Result then Result:= (fpgetErrNo = ESysENOENT);
  1182. end;
  1183. {$ENDIF}
  1184. function mbRenameFile(const OldName: String; NewName: String): Boolean;
  1185. {$IFDEF MSWINDOWS}
  1186. var
  1187. wTmpName,
  1188. wOldName, wNewName: UnicodeString;
  1189. begin
  1190. wNewName:= UTF16LongName(NewName);
  1191. wOldName:= UTF16LongName(OldName);
  1192. // Workaround: Windows >= 10 can't change only filename case on the FAT
  1193. if (Win32MajorVersion >= 10) and UnicodeSameText(wOldName, wNewName) then
  1194. begin
  1195. wTmpName:= GetFileSystemType(OldName);
  1196. if UnicodeSameText('FAT32', wTmpName) or UnicodeSameText('exFAT', wTmpName) then
  1197. begin
  1198. wTmpName:= UTF16LongName(GetTempName(OldName));
  1199. Result:= MoveFileExW(PWChar(wOldName), PWChar(wTmpName), 0);
  1200. if Result then
  1201. begin
  1202. Result:= MoveFileExW(PWChar(wTmpName), PWChar(wNewName), 0);
  1203. if not Result then MoveFileExW(PWChar(wTmpName), PWChar(wOldName), 0);
  1204. end;
  1205. Exit;
  1206. end;
  1207. end;
  1208. Result:= MoveFileExW(PWChar(wOldName), PWChar(wNewName), MOVEFILE_REPLACE_EXISTING);
  1209. end;
  1210. {$ELSE}
  1211. var
  1212. tmpFileName: String;
  1213. OldFileStat, NewFileStat: stat;
  1214. begin
  1215. if GetPathType(NewName) <> ptAbsolute then
  1216. NewName := ExtractFilePath(OldName) + NewName;
  1217. if OldName = NewName then
  1218. Exit(True);
  1219. if fpLstat(UTF8ToSys(OldName), OldFileStat) <> 0 then
  1220. Exit(False);
  1221. // Check if target file exists.
  1222. if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
  1223. begin
  1224. // Check if source and target are the same files (same inode and same device).
  1225. if (OldFileStat.st_ino = NewFileStat.st_ino) and
  1226. (OldFileStat.st_dev = NewFileStat.st_dev) then
  1227. begin
  1228. // Check number of links.
  1229. // If it is 1 then source and target names most probably differ only
  1230. // by case on a case-insensitive filesystem. Direct rename() in such case
  1231. // fails on Linux, so we use a temporary file name and rename in two stages.
  1232. // If number of links is more than 1 then it's enough to simply unlink
  1233. // the source file, since both files are technically identical.
  1234. // (On Linux rename() returns success but doesn't do anything
  1235. // if renaming a file to its hard link.)
  1236. // We cannot use st_nlink for directories because it means "number of
  1237. // subdirectories" ("number of all entries" under macOS) in that directory,
  1238. // plus its special entries '.' and '..';
  1239. // hard links to directories are not supported on Linux
  1240. // or Windows anyway (on macOS they are). Therefore we always treat
  1241. // directories as if they were a single link and rename them using temporary name.
  1242. if (NewFileStat.st_nlink = 1) or BaseUnix.fpS_ISDIR(NewFileStat.st_mode) then
  1243. begin
  1244. tmpFileName := GetTempName(OldName);
  1245. if FpRename(UTF8ToSys(OldName), UTF8ToSys(tmpFileName)) = 0 then
  1246. begin
  1247. if fpLstat(UTF8ToSys(NewName), NewFileStat) = 0 then
  1248. begin
  1249. // We have renamed the old file but the new file name still exists,
  1250. // so this wasn't a single file on a case-insensitive filesystem
  1251. // accessible by two names that differ by case.
  1252. FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName)); // Restore old file.
  1253. Result := False;
  1254. end
  1255. else if FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(NewName)) = 0 then
  1256. begin
  1257. Result := True;
  1258. end
  1259. else
  1260. begin
  1261. FpRename(UTF8ToSys(tmpFileName), UTF8ToSys(OldName)); // Restore old file.
  1262. Result := False;
  1263. end;
  1264. end
  1265. else
  1266. Result := False;
  1267. end
  1268. else
  1269. begin
  1270. // Multiple links - simply unlink the source file.
  1271. Result := (fpUnLink(UTF8ToSys(OldName)) = 0);
  1272. end;
  1273. Exit;
  1274. end;
  1275. end;
  1276. Result := FpRename(UTF8ToSys(OldName), UTF8ToSys(NewName)) = 0;
  1277. end;
  1278. {$ENDIF}
  1279. function mbFileSize(const FileName: String): Int64;
  1280. {$IFDEF MSWINDOWS}
  1281. var
  1282. Handle: System.THandle;
  1283. FindData: TWin32FindDataW;
  1284. begin
  1285. Result:= 0;
  1286. Handle := FindFirstFileW(PWideChar(UTF16LongName(FileName)), FindData);
  1287. if Handle <> INVALID_HANDLE_VALUE then
  1288. begin
  1289. Windows.FindClose(Handle);
  1290. if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  1291. begin
  1292. Int64Rec(Result).Lo:= FindData.nFileSizeLow;
  1293. Int64Rec(Result).Hi:= FindData.nFileSizeHigh;
  1294. end;
  1295. end;
  1296. end;
  1297. {$ELSE}
  1298. var
  1299. Info: BaseUnix.Stat;
  1300. begin
  1301. Result:= 0;
  1302. if fpStat(UTF8ToSys(FileName), Info) >= 0 then
  1303. Result:= Info.st_size;
  1304. end;
  1305. {$ENDIF}
  1306. function FileGetSize(Handle: System.THandle): Int64;
  1307. {$IFDEF MSWINDOWS}
  1308. begin
  1309. Int64Rec(Result).Lo := GetFileSize(Handle, @Int64Rec(Result).Hi);
  1310. end;
  1311. {$ELSE}
  1312. var
  1313. Info: BaseUnix.Stat;
  1314. begin
  1315. if fpFStat(Handle, Info) < 0 then
  1316. Result := -1
  1317. else
  1318. Result := Info.st_size;
  1319. end;
  1320. {$ENDIF}
  1321. function FileFlush(Handle: System.THandle): Boolean; inline;
  1322. {$IFDEF MSWINDOWS}
  1323. begin
  1324. Result:= FlushFileBuffers(Handle);
  1325. end;
  1326. {$ELSE}
  1327. begin
  1328. Result:= (fpfsync(Handle) = 0);
  1329. end;
  1330. {$ENDIF}
  1331. function FileFlushData(Handle: System.THandle): Boolean; inline;
  1332. {$IF DEFINED(LINUX)}
  1333. begin
  1334. Result:= (fpFDataSync(Handle) = 0);
  1335. end;
  1336. {$ELSE}
  1337. begin
  1338. Result:= FileFlush(Handle);
  1339. end;
  1340. {$ENDIF}
  1341. function FileIsReadOnlyEx(Handle: System.THandle): Boolean;
  1342. {$IF DEFINED(MSWINDOWS)}
  1343. var
  1344. Info: BY_HANDLE_FILE_INFORMATION;
  1345. begin
  1346. if GetFileInformationByHandle(Handle, Info) then
  1347. Result:= (Info.dwFileAttributes and (faReadOnly or faHidden or faSysFile) <> 0)
  1348. else
  1349. Result:= False;
  1350. end;
  1351. {$ELSEIF DEFINED(LINUX)}
  1352. var
  1353. Flags: UInt32;
  1354. begin
  1355. if FileGetFlags(Handle, Flags) then
  1356. begin
  1357. if (Flags and (FS_IMMUTABLE_FL or FS_APPEND_FL) <> 0) then
  1358. Exit(True);
  1359. end;
  1360. Result:= False;
  1361. end;
  1362. {$ELSE}
  1363. begin
  1364. Result:= False;
  1365. end;
  1366. {$ENDIF}
  1367. function FileAllocate(Handle: System.THandle; Size: Int64): Boolean;
  1368. {$IF DEFINED(LINUX)}
  1369. var
  1370. Ret: cint;
  1371. Sta: TStat;
  1372. StaFS: TStatFS;
  1373. begin
  1374. if (Size > 0) then
  1375. begin
  1376. repeat
  1377. Ret:= fpfStatFS(Handle, @StaFS);
  1378. until (Ret <> -1) or (fpgeterrno <> ESysEINTR);
  1379. // FAT32 does not support a fast allocation
  1380. if (StaFS.fstype = MSDOS_SUPER_MAGIC) then
  1381. Exit(False);
  1382. repeat
  1383. Ret:= fpFStat(Handle, Sta);
  1384. until (Ret <> -1) or (fpgeterrno <> ESysEINTR);
  1385. if (Ret = 0) and (Sta.st_size < Size) then
  1386. begin
  1387. // New size should be aligned to block size
  1388. Sta.st_size:= (Size + Sta.st_blksize - 1) and not (Sta.st_blksize - 1);
  1389. repeat
  1390. Ret:= fpFAllocate(Handle, 0, 0, Sta.st_size);
  1391. until (Ret <> -1) or (fpgeterrno <> ESysEINTR);
  1392. end;
  1393. end;
  1394. Result:= FileTruncate(Handle, Size);
  1395. end;
  1396. {$ELSE}
  1397. begin
  1398. Result:= FileTruncate(Handle, Size);
  1399. end;
  1400. {$ENDIF}
  1401. function mbGetCurrentDir: String;
  1402. {$IFDEF MSWINDOWS}
  1403. var
  1404. dwSize: DWORD;
  1405. wsDir: UnicodeString;
  1406. begin
  1407. if Length(CurrentDirectory) > 0 then
  1408. Result:= CurrentDirectory
  1409. else
  1410. begin
  1411. dwSize:= GetCurrentDirectoryW(0, nil);
  1412. if dwSize = 0 then
  1413. Result:= EmptyStr
  1414. else begin
  1415. SetLength(wsDir, dwSize + 1);
  1416. SetLength(wsDir, GetCurrentDirectoryW(dwSize, PWideChar(wsDir)));
  1417. Result:= UTF16ToUTF8(wsDir);
  1418. end;
  1419. end;
  1420. end;
  1421. {$ELSE}
  1422. begin
  1423. GetDir(0, Result);
  1424. Result := SysToUTF8(Result);
  1425. end;
  1426. {$ENDIF}
  1427. function mbSetCurrentDir(const NewDir: String): Boolean;
  1428. {$IFDEF MSWINDOWS}
  1429. var
  1430. Handle: THandle;
  1431. wsNewDir: UnicodeString;
  1432. FindData: TWin32FindDataW;
  1433. begin
  1434. if (Pos('\\', NewDir) = 1) then
  1435. Result:= True
  1436. else begin
  1437. wsNewDir:= UTF16LongName(IncludeTrailingBackslash(NewDir)) + '*';
  1438. Handle:= FindFirstFileW(PWideChar(wsNewDir), FindData);
  1439. Result:= (Handle <> INVALID_HANDLE_VALUE) or (GetLastError = ERROR_FILE_NOT_FOUND);
  1440. if (Handle <> INVALID_HANDLE_VALUE) then FindClose(Handle);
  1441. end;
  1442. if Result then CurrentDirectory:= NewDir;
  1443. end;
  1444. {$ELSE}
  1445. begin
  1446. Result:= fpChDir(UTF8ToSys(NewDir)) = 0;
  1447. end;
  1448. {$ENDIF}
  1449. function mbDirectoryExists(const Directory: String) : Boolean;
  1450. {$IFDEF MSWINDOWS}
  1451. var
  1452. Attr: DWORD;
  1453. begin
  1454. Attr:= GetFileAttributesW(PWideChar(UTF16LongName(Directory)));
  1455. if Attr <> DWORD(-1) then
  1456. Result:= (Attr and FILE_ATTRIBUTE_DIRECTORY) > 0
  1457. else
  1458. Result:= False;
  1459. end;
  1460. {$ELSE}
  1461. var
  1462. Info: BaseUnix.Stat;
  1463. begin
  1464. // We can use fpStat here instead of fpLstat, so that True is returned
  1465. // when target is a directory or a link to an existing directory.
  1466. // Note that same behaviour would be achieved by passing paths
  1467. // that end with path delimiter to fpLstat.
  1468. // Paths with links can be used the same way as if they were real directories.
  1469. if fpStat(UTF8ToSys(Directory), Info) >= 0 then
  1470. Result:= fpS_ISDIR(Info.st_mode)
  1471. else
  1472. Result:= False;
  1473. end;
  1474. {$ENDIF}
  1475. function mbCreateDir(const NewDir: String): Boolean;
  1476. {$IFDEF MSWINDOWS}
  1477. begin
  1478. Result:= CreateDirectoryW(PWideChar(UTF16LongName(NewDir)), nil);
  1479. end;
  1480. {$ELSE}
  1481. begin
  1482. Result:= fpMkDir(UTF8ToSys(NewDir), $1FF) = 0; // $1FF = &0777
  1483. end;
  1484. {$ENDIF}
  1485. function mbRemoveDir(const Dir: String): Boolean;
  1486. {$IFDEF MSWINDOWS}
  1487. begin
  1488. Result:= RemoveDirectoryW(PWideChar(UTF16LongName(Dir)));
  1489. if not Result then Result:= (GetLastError = ERROR_FILE_NOT_FOUND);
  1490. end;
  1491. {$ELSE}
  1492. begin
  1493. Result:= fpRmDir(UTF8ToSys(Dir)) = 0;
  1494. if not Result then Result:= (fpgetErrNo = ESysENOENT);
  1495. end;
  1496. {$ENDIF}
  1497. function mbFileSystemEntryExists(const Path: String): Boolean;
  1498. begin
  1499. Result := mbFileGetAttr(Path) <> faInvalidAttributes;
  1500. end;
  1501. function mbCompareFileNames(const FileName1, FileName2: String): Boolean; inline;
  1502. {$IF DEFINED(WINDOWS) OR DEFINED(DARWIN)}
  1503. begin
  1504. Result:= (UnicodeCompareText(CeUtf8ToUtf16(FileName1), CeUtf8ToUtf16(FileName2)) = 0);
  1505. end;
  1506. {$ELSE}
  1507. begin
  1508. Result:= (UnicodeCompareStr(CeUtf8ToUtf16(FileName1), CeUtf8ToUtf16(FileName2)) = 0);
  1509. end;
  1510. {$ENDIF}
  1511. function mbFileSame(const FileName1, FileName2: String): Boolean;
  1512. {$IF DEFINED(MSWINDOWS)}
  1513. var
  1514. Device1, Device2: TStringArray;
  1515. FileHandle1, FileHandle2: System.THandle;
  1516. FileInfo1, FileInfo2: BY_HANDLE_FILE_INFORMATION;
  1517. begin
  1518. Result := mbCompareFileNames(FileName1, FileName2);
  1519. if not Result then
  1520. begin
  1521. FileHandle1 := CreateFileW(PWideChar(UTF16LongName(FileName1)), FILE_READ_ATTRIBUTES,
  1522. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  1523. nil, OPEN_EXISTING, 0, 0);
  1524. if FileHandle1 <> INVALID_HANDLE_VALUE then
  1525. begin
  1526. FileHandle2 := CreateFileW(PWideChar(UTF16LongName(FileName2)), FILE_READ_ATTRIBUTES,
  1527. FILE_SHARE_READ or FILE_SHARE_WRITE or FILE_SHARE_DELETE,
  1528. nil, OPEN_EXISTING, 0, 0);
  1529. if FileHandle2 <> INVALID_HANDLE_VALUE then
  1530. begin
  1531. if GetFileInformationByHandle(FileHandle1, FileInfo1) and
  1532. GetFileInformationByHandle(FileHandle2, FileInfo2) then
  1533. begin
  1534. // Check if both files have the same index on the same volume.
  1535. // This check is valid only while both files are open.
  1536. Result := (FileInfo1.dwVolumeSerialNumber = FileInfo2.dwVolumeSerialNumber) and
  1537. (FileInfo1.nFileIndexHigh = FileInfo2.nFileIndexHigh) and
  1538. (FileInfo1.nFileIndexLow = FileInfo2.nFileIndexLow);
  1539. // Check that both files on the same physical drive (bug 0001774)
  1540. if Result then
  1541. begin
  1542. Device1:= AnsiString(GetFinalPathNameByHandle(FileHandle1)).Split([PathDelim]);
  1543. Device2:= AnsiString(GetFinalPathNameByHandle(FileHandle2)).Split([PathDelim]);
  1544. Result:= (Length(Device1) > 2) and (Length(Device2) > 2) and (Device1[2] = Device2[2]);
  1545. end;
  1546. end;
  1547. CloseHandle(FileHandle2);
  1548. end;
  1549. CloseHandle(FileHandle1);
  1550. end
  1551. end;
  1552. end;
  1553. {$ELSEIF DEFINED(UNIX)}
  1554. var
  1555. File1Stat, File2Stat: stat;
  1556. begin
  1557. Result := mbCompareFileNames(FileName1, FileName2) or
  1558. (
  1559. (fpLstat(UTF8ToSys(FileName1), File1Stat) = 0) and
  1560. (fpLstat(UTF8ToSys(FileName2), File2Stat) = 0) and
  1561. (File1Stat.st_ino = File2Stat.st_ino) and
  1562. (File1Stat.st_dev = File2Stat.st_dev)
  1563. );
  1564. end;
  1565. {$ENDIF}
  1566. function mbFileSameVolume(const FileName1, FileName2: String): Boolean;
  1567. {$IF DEFINED(MSWINDOWS)}
  1568. var
  1569. lpszVolumePathName1: array[0..maxSmallint] of WideChar;
  1570. lpszVolumePathName2: array[0..maxSmallint] of WideChar;
  1571. begin
  1572. Result:= GetVolumePathNameW(PWideChar(UTF16LongName(FileName1)), PWideChar(lpszVolumePathName1), maxSmallint) and
  1573. GetVolumePathNameW(PWideChar(UTF16LongName(FileName2)), PWideChar(lpszVolumePathName2), maxSmallint) and
  1574. WideSameText(ExtractFileDrive(lpszVolumePathName1), ExtractFileDrive(lpszVolumePathName2));
  1575. end;
  1576. {$ELSE}
  1577. var
  1578. Stat1, Stat2: Stat;
  1579. begin
  1580. Result:= (fpLStat(UTF8ToSys(FileName1), Stat1) = 0) and
  1581. (fpLStat(UTF8ToSys(FileName2), Stat2) = 0) and
  1582. (Stat1.st_dev = Stat2.st_dev);
  1583. end;
  1584. {$ENDIF}
  1585. function mbGetEnvironmentString(Index: Integer): String;
  1586. {$IFDEF MSWINDOWS}
  1587. var
  1588. hp, p: PWideChar;
  1589. begin
  1590. Result:= '';
  1591. p:= GetEnvironmentStringsW;
  1592. hp:= p;
  1593. if (hp <> nil) then
  1594. begin
  1595. while (hp^ <> #0) and (Index > 1) do
  1596. begin
  1597. Dec(Index);
  1598. hp:= hp + lstrlenW(hp) + 1;
  1599. end;
  1600. if (hp^ <> #0) then
  1601. Result:= UTF16ToUTF8(UnicodeString(hp));
  1602. end;
  1603. FreeEnvironmentStringsW(p);
  1604. end;
  1605. {$ELSE}
  1606. begin
  1607. Result:= SysToUTF8(GetEnvironmentString(Index));
  1608. end;
  1609. {$ENDIF}
  1610. function mbExpandEnvironmentStrings(const FileName: String): String;
  1611. {$IF DEFINED(MSWINDOWS)}
  1612. var
  1613. dwSize: DWORD;
  1614. wsResult: UnicodeString;
  1615. begin
  1616. SetLength(wsResult, MaxSmallInt + 1);
  1617. dwSize:= ExpandEnvironmentStringsW(PWideChar(CeUtf8ToUtf16(FileName)), PWideChar(wsResult), MaxSmallInt);
  1618. if (dwSize = 0) or (dwSize > MaxSmallInt) then
  1619. Result:= FileName
  1620. else begin
  1621. SetLength(wsResult, dwSize - 1);
  1622. Result:= UTF16ToUTF8(wsResult);
  1623. end;
  1624. end;
  1625. {$ELSE}
  1626. var
  1627. Index: Integer = 1;
  1628. EnvCnt, EqualPos: Integer;
  1629. EnvVar, EnvName, EnvValue: String;
  1630. begin
  1631. Result:= FileName;
  1632. EnvCnt:= GetEnvironmentVariableCount;
  1633. while (Index <= EnvCnt) and (Pos('$', Result) > 0) do
  1634. begin
  1635. EnvVar:= mbGetEnvironmentString(Index);
  1636. EqualPos:= Pos('=', EnvVar);
  1637. if EqualPos = 0 then Continue;
  1638. EnvName:= Copy(EnvVar, 1, EqualPos - 1);
  1639. EnvValue:= Copy(EnvVar, EqualPos + 1, MaxInt);
  1640. Result:= StringReplace(Result, '$' + EnvName, EnvValue, [rfReplaceAll]);
  1641. Inc(Index);
  1642. end;
  1643. end;
  1644. {$ENDIF}
  1645. function mbGetEnvironmentVariable(const sName: String): String;
  1646. {$IFDEF MSWINDOWS}
  1647. var
  1648. wsName: UnicodeString;
  1649. smallBuf: array[0..1023] of WideChar;
  1650. largeBuf: PWideChar;
  1651. dwResult: DWORD;
  1652. begin
  1653. Result := EmptyStr;
  1654. wsName := CeUtf8ToUtf16(sName);
  1655. dwResult := GetEnvironmentVariableW(PWideChar(wsName), @smallBuf[0], Length(smallBuf));
  1656. if dwResult > Length(smallBuf) then
  1657. begin
  1658. // Buffer not large enough.
  1659. largeBuf := GetMem(SizeOf(WideChar) * dwResult);
  1660. if Assigned(largeBuf) then
  1661. try
  1662. dwResult := GetEnvironmentVariableW(PWideChar(wsName), largeBuf, dwResult);
  1663. if dwResult > 0 then
  1664. Result := UTF16ToUTF8(UnicodeString(largeBuf));
  1665. finally
  1666. FreeMem(largeBuf);
  1667. end;
  1668. end
  1669. else if dwResult > 0 then
  1670. Result := UTF16ToUTF8(UnicodeString(smallBuf));
  1671. end;
  1672. {$ELSE}
  1673. begin
  1674. Result:= CeSysToUtf8(getenv(PAnsiChar(CeUtf8ToSys(sName))));
  1675. end;
  1676. {$ENDIF}
  1677. function mbSetEnvironmentVariable(const sName, sValue: String): Boolean;
  1678. {$IFDEF MSWINDOWS}
  1679. var
  1680. wsName,
  1681. wsValue: UnicodeString;
  1682. begin
  1683. wsName:= CeUtf8ToUtf16(sName);
  1684. wsValue:= CeUtf8ToUtf16(sValue);
  1685. Result:= SetEnvironmentVariableW(PWideChar(wsName), PWideChar(wsValue));
  1686. end;
  1687. {$ELSE}
  1688. begin
  1689. Result:= (setenv(PAnsiChar(CeUtf8ToSys(sName)), PAnsiChar(CeUtf8ToSys(sValue)), 1) = 0);
  1690. end;
  1691. {$ENDIF}
  1692. function mbUnsetEnvironmentVariable(const sName: String): Boolean;
  1693. {$IFDEF MSWINDOWS}
  1694. var
  1695. wsName: UnicodeString;
  1696. begin
  1697. wsName:= CeUtf8ToUtf16(sName);
  1698. Result:= SetEnvironmentVariableW(PWideChar(wsName), NIL);
  1699. end;
  1700. {$ELSE}
  1701. begin
  1702. Result:= (unsetenv(PAnsiChar(CeUtf8ToSys(sName))) = 0);
  1703. end;
  1704. {$ENDIF}
  1705. function mbSysErrorMessage: String;
  1706. begin
  1707. Result := mbSysErrorMessage(GetLastOSError);
  1708. end;
  1709. function mbSysErrorMessage(ErrorCode: Integer): String;
  1710. begin
  1711. Result := SysErrorMessage(ErrorCode);
  1712. {$IF (FPC_FULLVERSION < 30004)}
  1713. Result := CeSysToUtf8(Result);
  1714. {$ENDIF}
  1715. end;
  1716. function mbGetModuleName(Address: Pointer): String;
  1717. const
  1718. Dummy: Boolean = False;
  1719. {$IFDEF UNIX}
  1720. var
  1721. dlinfo: dl_info;
  1722. begin
  1723. if Address = nil then Address:= @Dummy;
  1724. FillChar({%H-}dlinfo, SizeOf(dlinfo), #0);
  1725. if dladdr(Address, @dlinfo) = 0 then
  1726. Result:= EmptyStr
  1727. else begin
  1728. Result:= CeSysToUtf8(dlinfo.dli_fname);
  1729. end;
  1730. end;
  1731. {$ELSE}
  1732. var
  1733. ModuleName: UnicodeString;
  1734. lpBuffer: TMemoryBasicInformation;
  1735. begin
  1736. if Address = nil then Address:= @Dummy;
  1737. if VirtualQuery(Address, @lpBuffer, SizeOf(lpBuffer)) <> SizeOf(lpBuffer) then
  1738. Result:= EmptyStr
  1739. else begin
  1740. SetLength(ModuleName, MAX_PATH + 1);
  1741. SetLength(ModuleName, GetModuleFileNameW({%H-}THandle(lpBuffer.AllocationBase),
  1742. PWideChar(ModuleName), MAX_PATH));
  1743. Result:= UTF16ToUTF8(ModuleName);
  1744. end;
  1745. end;
  1746. {$ENDIF}
  1747. function mbLoadLibrary(const Name: String): TLibHandle;
  1748. {$IFDEF MSWINDOWS}
  1749. var
  1750. dwMode: DWORD;
  1751. dwErrCode: DWORD;
  1752. sRememberPath: String;
  1753. begin
  1754. dwMode:= SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
  1755. try
  1756. // Some plugins using DLL(s) in their directory are loaded correctly only if "CurrentDir" is poining their location.
  1757. // Also, TC switch "CurrentDir" to their directory when loading them. So let's do the same.
  1758. sRememberPath:= GetCurrentDir;
  1759. SetCurrentDir(ExtractFileDir(Name));
  1760. Result:= SafeLoadLibrary(CeUtf8ToUtf16(Name));
  1761. dwErrCode:= GetLastError;
  1762. finally
  1763. SetErrorMode(dwMode);
  1764. SetCurrentDir(sRememberPath);
  1765. SetLastError(dwErrCode);
  1766. end;
  1767. end;
  1768. {$ELSE}
  1769. begin
  1770. Result:= SafeLoadLibrary(CeUtf8ToSys(Name));
  1771. end;
  1772. {$ENDIF}
  1773. function mbLoadLibraryEx(const Name: String): TLibHandle;
  1774. {$IF DEFINED(MSWINDOWS)}
  1775. const
  1776. PATH_ENV = 'PATH';
  1777. var
  1778. dwFlags:DWORD;
  1779. APath: String;
  1780. APathType: TPathType;
  1781. usName: UnicodeString;
  1782. begin
  1783. usName:= CeUtf8ToUtf16(Name);
  1784. APathType:= GetPathType(Name);
  1785. if CheckWin32Version(10) or (GetProcAddress(GetModuleHandleW(Kernel32), 'AddDllDirectory') <> nil) then
  1786. begin
  1787. if APathType <> ptAbsolute then
  1788. dwFlags:= 0
  1789. else begin
  1790. dwFlags:= LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
  1791. end;
  1792. Result:= LoadLibraryExW(PWideChar(usName), 0, dwFlags or LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
  1793. end
  1794. else begin
  1795. APath:= mbGetEnvironmentVariable(PATH_ENV);
  1796. try
  1797. if APathType <> ptAbsolute then
  1798. SetDllDirectoryW(PWideChar(''))
  1799. else begin
  1800. SetDllDirectoryW(PWideChar(ExtractFileDir(usName)));
  1801. end;
  1802. try
  1803. SetEnvironmentVariableW(PATH_ENV, nil);
  1804. Result:= LoadLibraryW(PWideChar(usName));
  1805. finally
  1806. SetDllDirectoryW(nil);
  1807. end;
  1808. finally
  1809. mbSetEnvironmentVariable(PATH_ENV, APath);
  1810. end;
  1811. end;
  1812. end;
  1813. {$ELSE}
  1814. begin
  1815. Result:= SafeLoadLibrary(CeUtf8ToSys(Name));
  1816. end;
  1817. {$ENDIF}
  1818. function SafeGetProcAddress(Lib: TLibHandle; const ProcName: AnsiString): Pointer;
  1819. begin
  1820. Result:= GetProcedureAddress(Lib, ProcName);
  1821. if (Result = nil) then raise Exception.Create(ProcName);
  1822. end;
  1823. function mbReadAllLinks(const PathToLink: String) : String;
  1824. var
  1825. Attrs: TFileAttrs;
  1826. LinkTargets: TStringList; // A list of encountered filenames (for detecting cycles)
  1827. function mbReadAllLinksRec(const PathToLink: String): String;
  1828. begin
  1829. Result := ReadSymLink(PathToLink);
  1830. if Result <> '' then
  1831. begin
  1832. if GetPathType(Result) <> ptAbsolute then
  1833. Result := GetAbsoluteFileName(ExtractFilePath(PathToLink), Result);
  1834. if LinkTargets.IndexOf(Result) >= 0 then
  1835. begin
  1836. // Link already encountered - links form a cycle.
  1837. Result := '';
  1838. {$IFDEF UNIX}
  1839. fpseterrno(ESysELOOP);
  1840. {$ENDIF}
  1841. Exit;
  1842. end;
  1843. Attrs := mbFileGetAttr(Result);
  1844. if (Attrs <> faInvalidAttributes) then
  1845. begin
  1846. if FPS_ISLNK(Attrs) then
  1847. begin
  1848. // Points to a link - read recursively.
  1849. LinkTargets.Add(Result);
  1850. Result := mbReadAllLinksRec(Result);
  1851. end;
  1852. // else points to a file/dir
  1853. end
  1854. else
  1855. begin
  1856. Result := ''; // Target of link doesn't exist
  1857. {$IFDEF UNIX}
  1858. fpseterrno(ESysENOENT);
  1859. {$ENDIF}
  1860. end;
  1861. end;
  1862. end;
  1863. begin
  1864. LinkTargets := TStringList.Create;
  1865. try
  1866. Result := mbReadAllLinksRec(PathToLink);
  1867. finally
  1868. FreeAndNil(LinkTargets);
  1869. end;
  1870. end;
  1871. function mbCheckReadLinks(const PathToLink : String): String;
  1872. var
  1873. Attrs: TFileAttrs;
  1874. begin
  1875. Attrs := mbFileGetAttr(PathToLink);
  1876. if (Attrs <> faInvalidAttributes) and FPS_ISLNK(Attrs) then
  1877. Result := mbReadAllLinks(PathToLink)
  1878. else
  1879. Result := PathToLink;
  1880. end;
  1881. function mbFileGetAttrNoLinks(const FileName: String): TFileAttrs;
  1882. {$IFDEF UNIX}
  1883. var
  1884. Info: BaseUnix.Stat;
  1885. begin
  1886. if fpStat(UTF8ToSys(FileName), Info) >= 0 then
  1887. Result := Info.st_mode
  1888. else
  1889. Result := faInvalidAttributes;
  1890. end;
  1891. {$ELSE}
  1892. var
  1893. LinkTarget: String;
  1894. begin
  1895. LinkTarget := mbReadAllLinks(FileName);
  1896. if LinkTarget <> '' then
  1897. Result := mbFileGetAttr(LinkTarget)
  1898. else
  1899. Result := faInvalidAttributes;
  1900. end;
  1901. {$ENDIF}
  1902. function CreateHardLink(const Path, LinkName: String) : Boolean;
  1903. {$IFDEF MSWINDOWS}
  1904. var
  1905. wsPath, wsLinkName: UnicodeString;
  1906. begin
  1907. wsPath:= UTF16LongName(Path);
  1908. wsLinkName:= UTF16LongName(LinkName);
  1909. Result:= DCNtfsLinks.CreateHardlink(wsPath, wsLinkName);
  1910. end;
  1911. {$ELSE}
  1912. begin
  1913. Result := (fplink(PAnsiChar(CeUtf8ToSys(Path)),PAnsiChar(CeUtf8ToSys(LinkName)))=0);
  1914. end;
  1915. {$ENDIF}
  1916. function CreateSymLink(const Path, LinkName: string; Attr: UInt32): Boolean;
  1917. {$IFDEF MSWINDOWS}
  1918. var
  1919. wsPath, wsLinkName: UnicodeString;
  1920. begin
  1921. wsPath:= CeUtf8ToUtf16(Path);
  1922. wsLinkName:= UTF16LongName(LinkName);
  1923. Result:= DCNtfsLinks.CreateSymlink(wsPath, wsLinkName, Attr);
  1924. end;
  1925. {$ELSE}
  1926. begin
  1927. Result := (fpsymlink(PAnsiChar(CeUtf8ToSys(Path)), PAnsiChar(CeUtf8ToSys(LinkName)))=0);
  1928. end;
  1929. {$ENDIF}
  1930. function ReadSymLink(const LinkName : String) : String;
  1931. {$IFDEF MSWINDOWS}
  1932. var
  1933. wsLinkName, wsTarget: UnicodeString;
  1934. begin
  1935. wsLinkName:= UTF16LongName(LinkName);
  1936. if DCNtfsLinks.ReadSymLink(wsLinkName, wsTarget) then
  1937. Result := UTF16ToUTF8(wsTarget)
  1938. else
  1939. Result := EmptyStr;
  1940. end;
  1941. {$ELSE}
  1942. begin
  1943. Result := SysToUTF8(fpReadlink(UTF8ToSys(LinkName)));
  1944. end;
  1945. {$ENDIF}
  1946. procedure SetLastOSError(LastError: Integer);
  1947. {$IFDEF MSWINDOWS}
  1948. begin
  1949. SetLastError(UInt32(LastError));
  1950. end;
  1951. {$ELSE}
  1952. begin
  1953. fpseterrno(LastError);
  1954. end;
  1955. {$ENDIF}
  1956. function GetTickCountEx: UInt64;
  1957. begin
  1958. {$IF DEFINED(MSWINDOWS)}
  1959. if QueryPerformanceCounter(PLARGE_INTEGER(@Result)) then
  1960. Result:= Result div PerformanceFrequency.QuadPart
  1961. else
  1962. {$ENDIF}
  1963. begin
  1964. Result:= SysUtils.GetTickCount64;
  1965. end;
  1966. end;
  1967. {$IFDEF MSWINDOWS}
  1968. initialization
  1969. if QueryPerformanceFrequency(@PerformanceFrequency) then
  1970. PerformanceFrequency.QuadPart := PerformanceFrequency.QuadPart div 1000;
  1971. {$ENDIF}
  1972. end.