sysutils.pp 53 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Florian Klaempfl
  4. member of the Free Pascal development team
  5. Sysutils unit for linux
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  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.
  11. **********************************************************************}
  12. {$IFNDEF FPC_DOTTEDUNITS}
  13. unit sysutils;
  14. {$ENDIF FPC_DOTTEDUNITS}
  15. interface
  16. {$MODE objfpc}
  17. {$MODESWITCH OUT}
  18. {$IFDEF UNICODERTL}
  19. {$MODESWITCH UNICODESTRINGS}
  20. {$ELSE}
  21. {$H+}
  22. {$ENDIF}
  23. {$modeswitch typehelpers}
  24. {$modeswitch advancedrecords}
  25. {$if (defined(BSD) or defined(SUNOS)) and defined(FPC_USE_LIBC)}
  26. {$define USE_VFORK}
  27. {$endif}
  28. {$DEFINE HAS_FILEGETDATETIMEINFO}
  29. {$DEFINE OS_FILESETDATEBYNAME}
  30. {$DEFINE HAS_SLEEP}
  31. {$DEFINE HAS_OSERROR}
  32. {$DEFINE HAS_OSCONFIG}
  33. {$DEFINE HAS_TEMPDIR}
  34. {$DEFINE HASUNIX}
  35. {$DEFINE HASCREATEGUID}
  36. {$DEFINE HAS_OSUSERDIR}
  37. {$DEFINE HAS_LOCALTIMEZONEOFFSET}
  38. {$DEFINE HAS_GETTICKCOUNT64}
  39. // this target has an fileflush implementation, don't include dummy
  40. {$DEFINE SYSUTILS_HAS_FILEFLUSH_IMPL}
  41. { used OS file system APIs use ansistring }
  42. {$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL}
  43. { OS has an ansistring/single byte environment variable API }
  44. {$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
  45. {$IFDEF FPC_DOTTEDUNITS}
  46. uses
  47. {$IFDEF LINUX}LinuxApi,{$ENDIF}
  48. {$IFDEF FreeBSD}freebsd,{$ENDIF}
  49. UnixApi.Base, UnixApi.Unix,UnixApi.Errors,System.SysConst,UnixApi.Types;
  50. {$ELSE FPC_DOTTEDUNITS}
  51. uses
  52. {$IFDEF LINUX}linux,{$ENDIF}
  53. {$IFDEF FreeBSD}freebsd,{$ENDIF}
  54. baseunix, Unix,errors,sysconst,Unixtype;
  55. {$ENDIF FPC_DOTTEDUNITS}
  56. {$IF defined(LINUX) or defined(FreeBSD)}
  57. {$DEFINE HAVECLOCKGETTIME}
  58. {$ENDIF}
  59. {$IF defined(DARWIN)}
  60. {$DEFINE HAS_ISFILENAMECASEPRESERVING}
  61. {$DEFINE HAS_ISFILENAMECASESENSITIVE}
  62. {$ENDIF}
  63. {$if defined(LINUX)}
  64. {$if sizeof(clong)<8}
  65. {$DEFINE USE_STATX}
  66. {$DEFINE USE_UTIMENSAT}
  67. {$endif sizeof(clong)<=4}
  68. {$DEFINE USE_FUTIMES}
  69. {$endif}
  70. { Include platform independent interface part }
  71. {$i sysutilh.inc}
  72. Function AddDisk(const path:string) : Byte;
  73. { the following is Kylix compatibility stuff, it should be moved to a
  74. special compatibilty unit (FK) }
  75. const
  76. RTL_SIGINT = 0;
  77. RTL_SIGFPE = 1;
  78. RTL_SIGSEGV = 2;
  79. RTL_SIGILL = 3;
  80. RTL_SIGBUS = 4;
  81. RTL_SIGQUIT = 5;
  82. RTL_SIGLAST = RTL_SIGQUIT;
  83. RTL_SIGDEFAULT = -1;
  84. type
  85. TSignalState = (ssNotHooked, ssHooked, ssOverridden);
  86. function InquireSignal(RtlSigNum: Integer): TSignalState;
  87. procedure AbandonSignalHandler(RtlSigNum: Integer);
  88. procedure HookSignal(RtlSigNum: Integer);
  89. procedure UnhookSignal(RtlSigNum: Integer; OnlyIfHooked: Boolean = True);
  90. implementation
  91. {$IFDEF FPC_DOTTEDUNITS}
  92. Uses
  93. {$ifdef android}
  94. dl,
  95. {$endif android}
  96. {$ifdef FPC_USE_LIBC}System.InitC{$ELSE}UnixApi.SysCall{$ENDIF}, UnixApi.Utils;
  97. {$ELSE FPC_DOTTEDUNITS}
  98. Uses
  99. {$ifdef android}
  100. dl,
  101. {$endif android}
  102. {$ifdef FPC_USE_LIBC}initc{$ELSE}Syscall{$ENDIF}, unixutil;
  103. {$ENDIF FPC_DOTTEDUNITS}
  104. type
  105. tsiginfo = record
  106. oldsiginfo: sigactionrec;
  107. hooked: boolean;
  108. end;
  109. const
  110. rtlsig2ossig: array[RTL_SIGINT..RTL_SIGLAST] of byte =
  111. (SIGINT,SIGFPE,SIGSEGV,SIGILL,SIGBUS,SIGQUIT);
  112. { to avoid linking in all this stuff in every program,
  113. as it's unlikely to be used by anything but libraries
  114. }
  115. signalinfoinited: boolean = false;
  116. var
  117. siginfo: array[RTL_SIGINT..RTL_SIGLAST] of tsiginfo;
  118. oldsigfpe: SigActionRec; external name '_FPC_OLDSIGFPE';
  119. oldsigsegv: SigActionRec; external name '_FPC_OLDSIGSEGV';
  120. oldsigbus: SigActionRec; external name '_FPC_OLDSIGBUS';
  121. oldsigill: SigActionRec; external name '_FPC_OLDSIGILL';
  122. procedure defaultsighandler; external name '_FPC_DEFAULTSIGHANDLER';
  123. procedure installdefaultsignalhandler(signum: Integer; out oldact: SigActionRec); external name '_FPC_INSTALLDEFAULTSIGHANDLER';
  124. function InternalInquireSignal(RtlSigNum: Integer; out act: SigActionRec; frominit: boolean): TSignalState;
  125. begin
  126. result:=ssNotHooked;
  127. if (RtlSigNum<>RTL_SIGDEFAULT) and
  128. (RtlSigNum<RTL_SIGLAST) then
  129. begin
  130. if (frominit or
  131. siginfo[RtlSigNum].hooked) and
  132. (fpsigaction(rtlsig2ossig[RtlSigNum],nil,@act)=0) then
  133. begin
  134. if not frominit then
  135. begin
  136. { check whether the installed signal handler is still ours }
  137. {$if not defined(aix) and (not defined(linux) or not defined(cpupowerpc64) or (defined(_call_elf) and (_call_elf = 2)))}
  138. if (pointer(act.sa_handler)=pointer(@defaultsighandler)) then
  139. {$else}
  140. { on aix and linux/ppc64 (ELFv1), procedure addresses are
  141. actually descriptors -> check whether the code addresses
  142. inside the descriptors match, rather than the descriptors
  143. themselves }
  144. if (ppointer(act.sa_handler)^=ppointer(@defaultsighandler)^) then
  145. {$endif}
  146. result:=ssHooked
  147. else
  148. result:=ssOverridden;
  149. end
  150. else if IsLibrary then
  151. begin
  152. { library -> signals have not been hooked by system init code }
  153. exit
  154. end
  155. else
  156. begin
  157. { program -> signals have been hooked by system init code }
  158. if (byte(RtlSigNum) in [RTL_SIGFPE,RTL_SIGSEGV,RTL_SIGILL,RTL_SIGBUS]) then
  159. begin
  160. {$if not defined(aix) and (not defined(linux) or not defined(cpupowerpc64) or (defined(_call_elf) and (_call_elf = 2)))}
  161. if (pointer(act.sa_handler)=pointer(@defaultsighandler)) then
  162. {$else}
  163. if (ppointer(act.sa_handler)^=ppointer(@defaultsighandler)^) then
  164. {$endif}
  165. result:=ssHooked
  166. else
  167. result:=ssOverridden;
  168. { return the original handlers as saved by the system unit
  169. (the current call to sigaction simply returned our
  170. system unit's installed handlers)
  171. }
  172. case RtlSigNum of
  173. RTL_SIGFPE:
  174. act:=oldsigfpe;
  175. RTL_SIGSEGV:
  176. act:=oldsigsegv;
  177. RTL_SIGILL:
  178. act:=oldsigill;
  179. RTL_SIGBUS:
  180. act:=oldsigbus;
  181. end;
  182. end
  183. else
  184. begin
  185. { these are not hooked in the startup code }
  186. result:=ssNotHooked;
  187. end
  188. end
  189. end
  190. end;
  191. end;
  192. procedure initsignalinfo;
  193. var
  194. i: Integer;
  195. begin
  196. for i:=RTL_SIGINT to RTL_SIGLAST do
  197. siginfo[i].hooked:=(InternalInquireSignal(i,siginfo[i].oldsiginfo,true)=ssHooked);
  198. signalinfoinited:=true;
  199. end;
  200. function InquireSignal(RtlSigNum: Integer): TSignalState;
  201. var
  202. act: SigActionRec;
  203. begin
  204. if not signalinfoinited then
  205. initsignalinfo;
  206. result:=InternalInquireSignal(RtlSigNum,act,false);
  207. end;
  208. procedure AbandonSignalHandler(RtlSigNum: Integer);
  209. begin
  210. if not signalinfoinited then
  211. initsignalinfo;
  212. if (RtlSigNum<>RTL_SIGDEFAULT) and
  213. (RtlSigNum<RTL_SIGLAST) then
  214. siginfo[RtlSigNum].hooked:=false;
  215. end;
  216. procedure HookSignal(RtlSigNum: Integer);
  217. var
  218. lowsig, highsig, i: Integer;
  219. begin
  220. if not signalinfoinited then
  221. initsignalinfo;
  222. if (RtlSigNum<>RTL_SIGDEFAULT) then
  223. begin
  224. lowsig:=RtlSigNum;
  225. highsig:=RtlSigNum;
  226. end
  227. else
  228. begin
  229. { we don't hook SIGINT and SIGQUIT by default }
  230. lowsig:=RTL_SIGFPE;
  231. highsig:=RTL_SIGBUS;
  232. end;
  233. { install the default rtl signal handler for the selected signal(s) }
  234. for i:=lowsig to highsig do
  235. begin
  236. installdefaultsignalhandler(rtlsig2ossig[i],siginfo[i].oldsiginfo);
  237. siginfo[i].hooked:=true;
  238. end;
  239. end;
  240. procedure UnhookSignal(RtlSigNum: Integer; OnlyIfHooked: Boolean = True);
  241. var
  242. act: SigActionRec;
  243. lowsig, highsig, i: Integer;
  244. begin
  245. if not signalinfoinited then
  246. initsignalinfo;
  247. if (RtlSigNum<>RTL_SIGDEFAULT) then
  248. begin
  249. lowsig:=RtlSigNum;
  250. highsig:=RtlSigNum;
  251. end
  252. else
  253. begin
  254. { we don't hook SIGINT and SIGQUIT by default }
  255. lowsig:=RTL_SIGFPE;
  256. highsig:=RTL_SIGBUS;
  257. end;
  258. for i:=lowsig to highsig do
  259. begin
  260. if not OnlyIfHooked or
  261. (InquireSignal(i)=ssHooked) then
  262. begin
  263. { restore the handler that was present when we hooked the signal,
  264. if we hooked it at one time or another. If the user doesn't
  265. want this, they have to call AbandonSignalHandler() first
  266. }
  267. if siginfo[i].hooked then
  268. act:=siginfo[i].oldsiginfo
  269. else
  270. begin
  271. fillchar(act,sizeof(act),0);
  272. pointer(act.sa_handler):=pointer(SIG_DFL);
  273. end;
  274. if (fpsigaction(rtlsig2ossig[i],@act,nil)=0) then
  275. siginfo[i].hooked:=false;
  276. end;
  277. end;
  278. end;
  279. {$Define OS_FILEISREADONLY} // Specific implementation for Unix.
  280. {$DEFINE FPC_FEXPAND_TILDE} { Tilde is expanded to home }
  281. {$DEFINE FPC_FEXPAND_GETENVPCHAR} { GetEnv result is a PAnsiChar }
  282. { Include platform independent implementation part }
  283. {$define executeprocuni}
  284. {$i sysutils.inc}
  285. { Include SysCreateGUID function }
  286. {$i suuid.inc}
  287. function GetTickCount64: QWord;
  288. var
  289. tp: TTimeVal;
  290. {$IFDEF HAVECLOCKGETTIME}
  291. ts: TTimeSpec;
  292. {$ENDIF}
  293. begin
  294. {$IFDEF HAVECLOCKGETTIME}
  295. if clock_gettime(CLOCK_MONOTONIC, @ts)=0 then
  296. begin
  297. Result := (Int64(ts.tv_sec) * 1000) + (ts.tv_nsec div 1000000);
  298. exit;
  299. end;
  300. {$ENDIF}
  301. fpgettimeofday(@tp, nil);
  302. Result := (Int64(tp.tv_sec) * 1000) + (tp.tv_usec div 1000);
  303. end;
  304. {****************************************************************************
  305. File Functions
  306. ****************************************************************************}
  307. Function DoFileLocking(Handle: Longint; Mode: Integer) : Longint;
  308. var
  309. lockop: cint;
  310. lockres: cint;
  311. closeres: cint;
  312. lockerr: cint;
  313. begin
  314. DoFileLocking:=Handle;
  315. {$ifdef beos}
  316. {$else}
  317. if (Handle>=0) then
  318. begin
  319. {$if defined(solaris) or defined(aix)}
  320. { Solaris' & AIX' flock is based on top of fcntl, which does not allow
  321. exclusive locks for files only opened for reading nor shared locks
  322. for files opened only for writing.
  323. If no locking is specified, we normally need an exclusive lock.
  324. So create an exclusive lock for fmOpenWrite and fmOpenReadWrite,
  325. but only a shared lock for fmOpenRead (since an exclusive lock
  326. is not possible in that case)
  327. }
  328. if ((mode and (fmShareCompat or fmShareExclusive or fmShareDenyWrite or fmShareDenyRead or fmShareDenyNone)) = 0) then
  329. begin
  330. if ((mode and (fmOpenRead or fmOpenWrite or fmOpenReadWrite)) = fmOpenRead) then
  331. mode := mode or fmShareDenyWrite
  332. else
  333. mode := mode or fmShareExclusive;
  334. end;
  335. {$endif solaris}
  336. case (mode and (fmShareCompat or fmShareExclusive or fmShareDenyWrite or fmShareDenyRead or fmShareDenyNone)) of
  337. fmShareCompat,
  338. fmShareExclusive:
  339. lockop:=LOCK_EX or LOCK_NB;
  340. fmShareDenyWrite,
  341. fmShareDenyNone:
  342. lockop:=LOCK_SH or LOCK_NB;
  343. else
  344. begin
  345. { fmShareDenyRead does not exit under *nix, only shared access
  346. (similar to fmShareDenyWrite) and exclusive access (same as
  347. fmShareExclusive)
  348. }
  349. repeat
  350. closeres:=FpClose(Handle);
  351. until (closeres<>-1) or (fpgeterrno<>ESysEINTR);
  352. DoFileLocking:=-1;
  353. exit;
  354. end;
  355. end;
  356. repeat
  357. lockres:=fpflock(Handle,lockop);
  358. until (lockres=0) or
  359. (fpgeterrno<>ESysEIntr);
  360. lockerr:=fpgeterrno;
  361. { Only return an error if locks are working and the file was already
  362. locked. Not if locks are simply unsupported (e.g., on Angstrom Linux
  363. you always get ESysNOLCK in the default configuration) }
  364. if (lockres<>0) and
  365. ((lockerr=ESysEAGAIN) or
  366. (lockerr=EsysEDEADLK)) then
  367. begin
  368. repeat
  369. closeres:=FpClose(Handle);
  370. until (closeres<>-1) or (fpgeterrno<>ESysEINTR);
  371. DoFileLocking:=-1;
  372. exit;
  373. end;
  374. end;
  375. {$endif not beos}
  376. end;
  377. Function FileOpenNoLocking (Const FileName : RawbyteString; Mode : Integer) : Longint;
  378. Function IsHandleDirectory(Handle : Longint) : boolean;
  379. Var Info : Stat;
  380. begin
  381. Result := (fpFStat(Handle, Info)<0) or fpS_ISDIR(info.st_mode);
  382. end;
  383. Var
  384. SystemFileName: RawByteString;
  385. fd,LinuxFlags : longint;
  386. begin
  387. LinuxFlags:=0;
  388. case (Mode and (fmOpenRead or fmOpenWrite or fmOpenReadWrite)) of
  389. fmOpenRead : LinuxFlags:=LinuxFlags or O_RdOnly;
  390. fmOpenWrite : LinuxFlags:=LinuxFlags or O_WrOnly;
  391. fmOpenReadWrite : LinuxFlags:=LinuxFlags or O_RdWr;
  392. end;
  393. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  394. repeat
  395. fd:=fpOpen (pointer(SystemFileName),LinuxFlags);
  396. until (fd<>-1) or (fpgeterrno<>ESysEINTR);
  397. { Do not allow to open directories with FileOpen.
  398. This would cause weird behavior of TFileStream.Size,
  399. TMemoryStream.LoadFromFile etc. }
  400. if (fd<>-1) and IsHandleDirectory(fd) then
  401. begin
  402. fpClose(fd);
  403. fd:=feInvalidHandle;
  404. end;
  405. FileOpenNoLocking:=fd;
  406. end;
  407. Function FileOpen (Const FileName : RawbyteString; Mode : Integer) : Longint;
  408. begin
  409. FileOpen:=FileOpenNoLocking(FileName, Mode);
  410. FileOpen:=DoFileLocking(FileOpen, Mode);
  411. end;
  412. function FileFlush(Handle: THandle): Boolean;
  413. begin
  414. Result:= fpfsync(handle)=0;
  415. end;
  416. Function FileCreate (Const FileName : RawByteString) : Longint;
  417. Var
  418. SystemFileName: RawByteString;
  419. begin
  420. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  421. repeat
  422. FileCreate:=fpOpen(pointer(SystemFileName),O_RdWr or O_Creat or O_Trunc);
  423. until (FileCreate<>-1) or (fpgeterrno<>ESysEINTR);
  424. end;
  425. Function FileCreate (Const FileName : RawByteString;Rights : Longint) : Longint;
  426. Var
  427. SystemFileName: RawByteString;
  428. begin
  429. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  430. repeat
  431. FileCreate:=fpOpen(pointer(SystemFileName),O_RdWr or O_Creat or O_Trunc,Rights);
  432. until (FileCreate<>-1) or (fpgeterrno<>ESysEINTR);
  433. end;
  434. Function FileCreate (Const FileName : RawByteString; ShareMode : Longint; Rights:LongInt ) : Longint;
  435. Var
  436. fd: Longint;
  437. begin
  438. { if the file already exists and we can't open it using the requested
  439. ShareMode (e.g. exclusive sharing), exit immediately so that we don't
  440. first empty the file and then check whether we can lock this new file
  441. (which we can by definition) }
  442. fd:=FileOpenNoLocking(FileName,ShareMode);
  443. { the file exists, check whether our locking request is compatible }
  444. if fd>=0 then
  445. begin
  446. Result:=DoFileLocking(fd,ShareMode);
  447. FileClose(fd);
  448. { Can't lock -> abort }
  449. if Result<0 then
  450. exit;
  451. end;
  452. { now create the file }
  453. Result:=FileCreate(FileName,Rights);
  454. Result:=DoFileLocking(Result,ShareMode);
  455. end;
  456. Function FileRead (Handle : Longint; out Buffer; Count : longint) : Longint;
  457. begin
  458. repeat
  459. FileRead:=fpRead (Handle,Buffer,Count);
  460. until (FileRead<>-1) or (fpgeterrno<>ESysEINTR);
  461. end;
  462. Function FileWrite (Handle : Longint; const Buffer; Count : Longint) : Longint;
  463. begin
  464. repeat
  465. FileWrite:=fpWrite (Handle,Buffer,Count);
  466. until (FileWrite<>-1) or (fpgeterrno<>ESysEINTR);
  467. end;
  468. Function FileSeek (Handle,FOffset,Origin : Longint) : Longint;
  469. Var
  470. I : Int64;
  471. begin
  472. I:=FileSeek(Handle,int64(FOffset),Origin);
  473. if I>High(Longint) then
  474. Raise EInOutError.CreateFmt(SErrPosToBigForLongint,[I]);
  475. result:=I;
  476. end;
  477. Function FileSeek (Handle : Longint; FOffset : Int64; Origin : Longint) : Int64;
  478. begin
  479. FileSeek:=fplSeek (Handle,FOffset,Origin);
  480. end;
  481. Procedure FileClose (Handle : Longint);
  482. var
  483. res: cint;
  484. begin
  485. repeat
  486. res:=fpclose(Handle);
  487. until (res<>-1) or (fpgeterrno<>ESysEINTR);
  488. end;
  489. Function FileTruncate (Handle: THandle; Size: Int64) : boolean;
  490. var
  491. res: cint;
  492. begin
  493. if (SizeOf (TOff) < 8) (* fpFTruncate only supporting signed 32-bit size *)
  494. and (Size > high (longint)) then
  495. FileTruncate := false
  496. else
  497. begin
  498. repeat
  499. res:=fpftruncate(Handle,Size);
  500. until (res<>-1) or (fpgeterrno<>ESysEINTR);
  501. FileTruncate:=res>=0;
  502. end;
  503. end;
  504. Function FileAge (Const FileName : RawByteString): Int64;
  505. Var
  506. Info : Stat;
  507. SystemFileName: RawByteString;
  508. {$ifdef USE_STATX}
  509. Infox : TStatx;
  510. {$endif USE_STATX}
  511. begin
  512. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  513. {$ifdef USE_STATX}
  514. { first try statx }
  515. if {$ifdef FPC_USE_LIBC} (@statx<>nil) and {$endif}
  516. (statx(AT_FDCWD,PAnsiChar(SystemFileName),0,STATX_MTIME or STATX_MODE,Infox)>=0) and not(fpS_ISDIR(Infox.stx_mode)) then
  517. begin
  518. Result:=Infox.stx_mtime.tv_sec;
  519. exit;
  520. end;
  521. {$endif USE_STATX}
  522. If (fpstat(PAnsiChar(SystemFileName),Info)<0) or fpS_ISDIR(info.st_mode) then
  523. exit(-1)
  524. else
  525. Result:=info.st_mtime;
  526. end;
  527. function FileGetDateTimeInfo(const FileName: string; out DateTime: TDateTimeInfoRec; FollowLink: Boolean = True): Boolean;
  528. var
  529. FN : AnsiString;
  530. st: tstat;
  531. {$IFDEF USE_STATX}
  532. stx : tstatx;
  533. flags : Integer;
  534. const
  535. STATXMASK = STATX_MTIME or STATX_ATIME or STATX_CTIME;
  536. {$ENDIF}
  537. begin
  538. FN:=FileName;
  539. {$ifdef USE_STATX}
  540. flags:=0;
  541. if Not FollowLink then
  542. Flags:=AT_SYMLINK_NOFOLLOW;
  543. if {$ifdef FPC_USE_LIBC} (@statx<>nil) and {$endif}
  544. (statx(AT_FDCWD,PAnsiChar(FN),FLags,STATXMASK, stx)>=0) then
  545. begin
  546. DateTime.Data:=stx;
  547. Exit(True);
  548. end;
  549. {$else}
  550. if (FollowLink and (fpstat(FN,st) = 0)) or
  551. (not FollowLink and (fplstat(fn, st) = 0)) then
  552. begin
  553. DateTime.Data:=st;
  554. Result := True;
  555. end;
  556. {$endif}
  557. end;
  558. Function LinuxToWinAttr (const FN : RawByteString; Const Info : Stat) : Longint;
  559. Var
  560. LinkInfo : Stat;
  561. nm : RawByteString;
  562. begin
  563. Result:=faArchive;
  564. If fpS_ISDIR(Info.st_mode) then
  565. Result:=Result or faDirectory;
  566. nm:=ExtractFileName(FN);
  567. If (Length(nm)>=2) and
  568. (nm[1]='.') and
  569. (nm[2]<>'.') then
  570. Result:=Result or faHidden;
  571. If (Info.st_Mode and S_IWUSR)=0 Then
  572. Result:=Result or faReadOnly;
  573. If fpS_ISSOCK(Info.st_mode) or fpS_ISBLK(Info.st_mode) or fpS_ISCHR(Info.st_mode) or fpS_ISFIFO(Info.st_mode) Then
  574. Result:=Result or faSysFile;
  575. If fpS_ISLNK(Info.st_mode) Then
  576. begin
  577. Result:=Result or faSymLink;
  578. // Windows reports if the link points to a directory.
  579. if (fpstat(PAnsiChar(FN),LinkInfo)>=0) and fpS_ISDIR(LinkInfo.st_mode) then
  580. Result := Result or faDirectory;
  581. end;
  582. end;
  583. {$ifdef USE_STATX}
  584. Function LinuxToWinAttr (const FN : RawByteString; Const Info : TStatx) : Longint;
  585. Var
  586. LinkInfo : Stat;
  587. nm : RawByteString;
  588. begin
  589. Result:=faArchive;
  590. If fpS_ISDIR(Info.stx_mode) then
  591. Result:=Result or faDirectory;
  592. nm:=ExtractFileName(FN);
  593. If (Length(nm)>=2) and
  594. (nm[1]='.') and
  595. (nm[2]<>'.') then
  596. Result:=Result or faHidden;
  597. If (Info.stx_Mode and S_IWUSR)=0 Then
  598. Result:=Result or faReadOnly;
  599. If fpS_ISSOCK(Info.stx_mode) or fpS_ISBLK(Info.stx_mode) or fpS_ISCHR(Info.stx_mode) or fpS_ISFIFO(Info.stx_mode) Then
  600. Result:=Result or faSysFile;
  601. If fpS_ISLNK(Info.stx_mode) Then
  602. begin
  603. Result:=Result or faSymLink;
  604. // Windows reports if the link points to a directory.
  605. { as we are only interested in the st_mode field here, we do not need to use statx }
  606. if (fpstat(PAnsiChar(FN),LinkInfo)>=0) and fpS_ISDIR(LinkInfo.st_mode) then
  607. Result := Result or faDirectory;
  608. end;
  609. end;
  610. {$endif USE_STATX}
  611. function FileGetSymLinkTarget(const FileName: RawByteString; out SymLinkRec: TRawbyteSymLinkRec): Boolean;
  612. var
  613. Info : Stat;
  614. SystemFileName: RawByteString;
  615. begin
  616. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  617. if (fplstat(SystemFileName,Info)>=0) and fpS_ISLNK(Info.st_mode) then begin
  618. FillByte(SymLinkRec, SizeOf(SymLinkRec), 0);
  619. SymLinkRec.TargetName:=fpreadlink(SystemFileName);
  620. if fpstat(pointer(SystemFileName), Info) < 0 then
  621. raise EDirectoryNotFoundException.Create(SysErrorMessage(GetLastOSError));
  622. SymLinkRec.Attr := LinuxToWinAttr(SystemFileName, Info);
  623. SymLinkRec.Size := Info.st_size;
  624. SymLinkRec.Mode := Info.st_mode;
  625. Result:=True;
  626. end else
  627. Result:=False;
  628. end;
  629. Function FileExists (Const FileName : RawByteString; FollowLink : Boolean) : Boolean;
  630. var
  631. Info : Stat;
  632. SystemFileName: RawByteString;
  633. isdir: Boolean;
  634. begin
  635. // Do not call fpAccess with an empty name. (Valgrind will complain)
  636. if Filename='' then
  637. Exit(False);
  638. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  639. // Don't use stat. It fails on files >2 GB.
  640. // Access obeys the same access rules, so the result should be the same.
  641. FileExists:=fpAccess(pointer(SystemFileName),F_OK)=0;
  642. { we need to ensure however that we aren't dealing with a directory }
  643. isdir:=False;
  644. if FileExists then begin
  645. if (fpstat(pointer(SystemFileName),Info)>=0) and fpS_ISDIR(Info.st_mode) then begin
  646. FileExists:=False;
  647. isdir:=True;
  648. end;
  649. end;
  650. { if we shall not follow the link we only need to check for a symlink if the
  651. target file itself should not exist }
  652. if not FileExists and not isdir and not FollowLink then
  653. FileExists:=(fplstat(pointer(SystemFileName),Info)>=0) and fpS_ISLNK(Info.st_mode);
  654. end;
  655. Function DirectoryExists (Const Directory : RawByteString; FollowLink : Boolean) : Boolean;
  656. Var
  657. Info : Stat;
  658. SystemFileName: RawByteString;
  659. exists: Boolean;
  660. begin
  661. SystemFileName:=ToSingleByteFileSystemEncodedFileName(Directory);
  662. exists:=fpstat(pointer(SystemFileName),Info)>=0;
  663. DirectoryExists:=exists and fpS_ISDIR(Info.st_mode);
  664. { if we shall not follow the link we only need to check for a symlink if the
  665. target directory itself should not exist }
  666. if not exists and not FollowLink then
  667. DirectoryExists:=(fplstat(pointer(SystemFileName),Info)>=0) and fpS_ISLNK(Info.st_mode);
  668. end;
  669. { assumes that pattern and name have the same code page }
  670. Function FNMatch(const Pattern,Name:string):Boolean;
  671. Var
  672. LenPat,LenName : longint;
  673. function NameUtf8CodePointLen(index: longint): longint;
  674. var
  675. MaxLookAhead: longint;
  676. begin
  677. MaxLookAhead:=LenName-Index+1;
  678. { abs so that in case of an invalid sequence, we count this as one
  679. codepoint }
  680. NameUtf8CodePointLen:=abs(Utf8CodePointLen(pansichar(@Name[index]),MaxLookAhead,true));
  681. { if the sequence was incomplete, use the incomplete sequence as
  682. codepoint }
  683. if NameUtf8CodePointLen=0 then
  684. NameUtf8CodePointLen:=MaxLookAhead;
  685. end;
  686. procedure GoToLastByteOfUtf8CodePoint(var j: longint);
  687. begin
  688. inc(j,NameUtf8CodePointLen(j)-1);
  689. end;
  690. { input:
  691. i: current position in pattern (start of utf-8 code point)
  692. j: current position in name (start of utf-8 code point)
  693. update_i_j: should i and j be changed by the routine or not
  694. output:
  695. i: if update_i_j, then position of last matching part of code point in
  696. pattern, or first non-matching code point in pattern. Otherwise the
  697. same value as on input.
  698. j: if update_i_j, then position of last matching part of code point in
  699. name, or first non-matching code point in name. Otherwise the
  700. same value as on input.
  701. result: true if match, false if no match
  702. }
  703. function CompareUtf8CodePoint(var i,j: longint; update_i_j: boolean): Boolean;
  704. var
  705. bytes,
  706. new_i,
  707. new_j: longint;
  708. begin
  709. bytes:=NameUtf8CodePointLen(j);
  710. new_i:=i;
  711. new_j:=j;
  712. { ensure that a part of an UTF-8 codepoint isn't interpreted
  713. as '*' or '?' }
  714. repeat
  715. dec(bytes);
  716. Result:=
  717. (new_j<=LenName) and
  718. (new_i<=LenPat) and
  719. (Pattern[new_i]=Name[new_j]);
  720. inc(new_i);
  721. inc(new_j);
  722. until not(Result) or
  723. (bytes=0);
  724. if update_i_j then
  725. begin
  726. i:=new_i;
  727. j:=new_j;
  728. end;
  729. end;
  730. Function DoFNMatch(i,j:longint):Boolean;
  731. Var
  732. UTF8, Found : boolean;
  733. Begin
  734. Found:=true;
  735. { ensure that we don't skip partial characters in UTF-8-encoded strings }
  736. UTF8:=StringCodePage(Name)=CP_UTF8;
  737. While Found and (i<=LenPat) Do
  738. Begin
  739. Case Pattern[i] of
  740. '?' :
  741. begin
  742. Found:=(j<=LenName);
  743. if UTF8 then
  744. GoToLastByteOfUtf8CodePoint(j);
  745. end;
  746. '*' : Begin
  747. {find the next character in pattern, different of ? and *}
  748. while Found do
  749. begin
  750. inc(i);
  751. if i>LenPat then
  752. Break;
  753. case Pattern[i] of
  754. '*' : ;
  755. '?' : begin
  756. if j>LenName then
  757. begin
  758. DoFNMatch:=false;
  759. Exit;
  760. end;
  761. if UTF8 then
  762. GoToLastByteOfUtf8CodePoint(j);
  763. inc(j);
  764. end;
  765. else
  766. Found:=false;
  767. end;
  768. end;
  769. Assert((i>LenPat) or ( (Pattern[i]<>'*') and (Pattern[i]<>'?') ));
  770. { Now, find in name the character which i points to, if the * or
  771. ? wasn't the last character in the pattern, else, use up all
  772. the chars in name }
  773. Found:=false;
  774. if (i<=LenPat) then
  775. begin
  776. repeat
  777. {find a letter (not only first !) which maches pattern[i]}
  778. if UTF8 then
  779. begin
  780. while (j<=LenName) and
  781. ((name[j]<>pattern[i]) or
  782. not CompareUtf8CodePoint(i,j,false)) do
  783. begin
  784. GoToLastByteOfUtf8CodePoint(j);
  785. inc(j);
  786. end;
  787. end
  788. else
  789. begin
  790. while (j<=LenName) and (name[j]<>pattern[i]) do
  791. inc (j);
  792. end;
  793. if (j<LenName) then
  794. begin
  795. { while positions i/j have already been checked, in
  796. case of UTF-8 we have to ensure that we don't split
  797. a code point. Otherwise we can skip over comparing
  798. the same characters twice }
  799. if DoFnMatch(i+ord(not UTF8),j+ord(not UTF8)) then
  800. begin
  801. i:=LenPat;
  802. j:=LenName;{we can stop}
  803. Found:=true;
  804. Break;
  805. end
  806. { We didn't find one, need to look further }
  807. else
  808. begin
  809. if UTF8 then
  810. GoToLastByteOfUtf8CodePoint(j);
  811. inc(j);
  812. end;
  813. end
  814. else if j=LenName then
  815. begin
  816. Found:=true;
  817. Break;
  818. end;
  819. { This 'until' condition must be j>LenName, not j>=LenName.
  820. That's because when we 'need to look further' and
  821. j = LenName then loop must not terminate. }
  822. until (j>LenName);
  823. end
  824. else
  825. begin
  826. j:=LenName;{we can stop}
  827. Found:=true;
  828. end;
  829. end;
  830. #128..#255:
  831. begin
  832. Found:=(j<=LenName) and (pattern[i]=name[j]);
  833. if Found and UTF8 then
  834. begin
  835. { ensure that a part of an UTF-8 codepoint isn't matched with
  836. '*' or '?' }
  837. Found:=CompareUtf8CodePoint(i,j,true);
  838. { at this point, either Found is false (and we'll stop), or
  839. both pattern[i] and name[j] are the end of the current code
  840. point and equal }
  841. end
  842. end
  843. else {not a wildcard character in pattern}
  844. Found:=(j<=LenName) and (pattern[i]=name[j]);
  845. end;
  846. inc(i);
  847. inc(j);
  848. end;
  849. DoFnMatch:=Found and (j>LenName);
  850. end;
  851. Begin {start FNMatch}
  852. LenPat:=Length(Pattern);
  853. LenName:=Length(Name);
  854. FNMatch:=DoFNMatch(1,1);
  855. End;
  856. Type
  857. TUnixFindData = Record
  858. NamePos : LongInt; {to track which search this is}
  859. DirPtr : Pointer; {directory pointer for reading directory}
  860. SearchSpec : RawbyteString;
  861. SearchType : Byte; {0=normal, 1=open will close, 2=only 1 file}
  862. SearchAttr : Longint; {attribute we are searching for}
  863. End;
  864. PUnixFindData = ^TUnixFindData;
  865. Procedure InternalFindClose(var Handle: Pointer);
  866. var
  867. D: PUnixFindData absolute Handle;
  868. begin
  869. If D=Nil then
  870. Exit;
  871. if D^.SearchType=0 then
  872. begin
  873. if D^.dirptr<>nil then
  874. fpclosedir(pdir(D^.dirptr)^);
  875. end;
  876. Dispose(D);
  877. D:=nil;
  878. end;
  879. Function FindGetFileInfo(const s: RawByteString; var f: TAbstractSearchRec; var Name: RawByteString):boolean;
  880. Var
  881. {$ifdef USE_STATX}
  882. stx : {$ifdef FPC_DOTTEDUNITS}LinuxApi.{$else}linux.{$endif}tstatx;
  883. {$endif USE_STATX}
  884. st : BU.stat;
  885. WinAttr : longint;
  886. begin
  887. {$ifdef USE_STATX}
  888. {$ifdef FPC_USE_LIBC}
  889. if (@statx=nil) then
  890. FindGetFileInfo:=false
  891. else
  892. {$endif}
  893. if Assigned(f.FindHandle) and ( (PUnixFindData(F.FindHandle)^.searchattr and faSymlink) > 0) then
  894. FindGetFileInfo:=statx(AT_FDCWD,pointer(s),AT_SYMLINK_NOFOLLOW,STATX_ALL,stx)=0
  895. else
  896. begin
  897. FindGetFileInfo:=statx(AT_FDCWD,pointer(s),0,STATX_ALL,stx)=0;
  898. end;
  899. if FindGetFileInfo then
  900. begin
  901. WinAttr:=LinuxToWinAttr(s,stx);
  902. FindGetFileInfo:=(WinAttr and Not(PUnixFindData(f.FindHandle)^.searchattr))=0;
  903. if FindGetFileInfo then
  904. begin
  905. Name:=ExtractFileName(s);
  906. f.Attr:=WinAttr;
  907. f.Size:=stx.stx_Size;
  908. f.Mode:=stx.stx_mode;
  909. f.Time:=stx.stx_mtime.tv_sec;
  910. FindGetFileInfo:=true;
  911. end;
  912. end
  913. { no statx? try stat }
  914. else if fpgeterrno=ESysENOSYS then
  915. {$endif USE_STATX}
  916. begin
  917. if Assigned(f.FindHandle) and ( (PUnixFindData(F.FindHandle)^.searchattr and faSymlink) > 0) then
  918. FindGetFileInfo:=(fplstat(pointer(s),st)=0)
  919. else
  920. FindGetFileInfo:=(fpstat(pointer(s),st)=0);
  921. if not FindGetFileInfo then
  922. exit;
  923. WinAttr:=LinuxToWinAttr(s,st);
  924. FindGetFileInfo:=(WinAttr and Not(PUnixFindData(f.FindHandle)^.searchattr))=0;
  925. if FindGetFileInfo then
  926. begin
  927. Name:=ExtractFileName(s);
  928. f.Attr:=WinAttr;
  929. f.Size:=st.st_Size;
  930. f.Mode:=st.st_mode;
  931. f.Time:=st.st_mtime;
  932. FindGetFileInfo:=true;
  933. end;
  934. end;
  935. end;
  936. // Returns the FOUND filename. Error code <> 0 if no file found
  937. Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
  938. Var
  939. DirName : RawByteString;
  940. FName,
  941. SName : RawBytestring;
  942. Found,
  943. Finished : boolean;
  944. p : pdirent;
  945. UnixFindData : PUnixFindData;
  946. Begin
  947. Result:=-1;
  948. UnixFindData:=PUnixFindData(Rslt.FindHandle);
  949. { SearchSpec='' means that there were no wild cards, so only one file to
  950. find.
  951. }
  952. If (UnixFindData=Nil) or (UnixFindData^.SearchSpec='') then
  953. exit;
  954. if (UnixFindData^.SearchType=0) and
  955. (UnixFindData^.Dirptr=nil) then
  956. begin
  957. If UnixFindData^.NamePos = 0 Then
  958. DirName:='./'
  959. Else
  960. DirName:=Copy(UnixFindData^.SearchSpec,1,UnixFindData^.NamePos);
  961. UnixFindData^.DirPtr := fpopendir(PAnsiChar(DirName));
  962. end;
  963. SName:=Copy(UnixFindData^.SearchSpec,UnixFindData^.NamePos+1,Length(UnixFindData^.SearchSpec));
  964. Found:=False;
  965. Finished:=(UnixFindData^.dirptr=nil);
  966. While Not Finished Do
  967. Begin
  968. p:=fpreaddir(pdir(UnixFindData^.dirptr)^);
  969. if p=nil then
  970. FName:=''
  971. else
  972. FName:=p^.d_name;
  973. If FName='' Then
  974. Finished:=True
  975. Else
  976. Begin
  977. SetCodePage(FName,DefaultFileSystemCodePage,false);
  978. If FNMatch(SName,FName) Then
  979. Begin
  980. Found:=FindGetFileInfo(Copy(UnixFindData^.SearchSpec,1,UnixFindData^.NamePos)+FName,Rslt,Name);
  981. if Found then
  982. begin
  983. Result:=0;
  984. exit;
  985. end;
  986. End;
  987. End;
  988. End;
  989. End;
  990. Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
  991. {
  992. opens dir and calls FindNext if needed.
  993. }
  994. var
  995. UnixFindData : PUnixFindData;
  996. Begin
  997. Result:=-1;
  998. { this is safe even though Rslt actually contains a refcounted field, because
  999. it is declared as "out" and hence has already been initialised }
  1000. fillchar(Rslt,sizeof(Rslt),0);
  1001. if Path='' then
  1002. exit;
  1003. { Allocate UnixFindData (we always need it, for the search attributes) }
  1004. New(UnixFindData);
  1005. FillChar(UnixFindData^,sizeof(UnixFindData^),0);
  1006. Rslt.FindHandle:=UnixFindData;
  1007. {We always also search for readonly and archive, regardless of Attr:}
  1008. UnixFindData^.SearchAttr := Attr or faarchive or fareadonly;
  1009. {Wildcards?}
  1010. if (Pos('?',Path)=0) and (Pos('*',Path)=0) then
  1011. begin
  1012. if FindGetFileInfo(ToSingleByteFileSystemEncodedFileName(Path),Rslt,Name) then
  1013. Result:=0;
  1014. end
  1015. else
  1016. begin
  1017. {Create Info}
  1018. UnixFindData^.SearchSpec := ToSingleByteFileSystemEncodedFileName(Path);
  1019. UnixFindData^.NamePos := Length(UnixFindData^.SearchSpec);
  1020. while (UnixFindData^.NamePos>0) and (UnixFindData^.SearchSpec[UnixFindData^.NamePos]<>'/') do
  1021. dec(UnixFindData^.NamePos);
  1022. Result:=InternalFindNext(Rslt,Name);
  1023. end;
  1024. If (Result<>0) then
  1025. InternalFindClose(Rslt.FindHandle);
  1026. End;
  1027. Function FileGetDate (Handle : Longint) : Int64;
  1028. Var
  1029. Info : Stat;
  1030. {$ifdef USE_STATX}
  1031. Infox : TStatx;
  1032. {$endif USE_STATX}
  1033. Char0 : AnsiChar;
  1034. begin
  1035. Result:=-1;
  1036. {$ifdef USE_STATX}
  1037. Char0:=#0;
  1038. if {$ifdef FPC_USE_LIBC} (@statx<>nil) and {$endif}
  1039. (statx(Handle,@Char0,AT_EMPTY_PATH,STATX_MTIME,Infox)=0) then
  1040. Result:=Infox.stx_Mtime.tv_sec
  1041. else if fpgeterrno=ESysENOSYS then
  1042. {$endif USE_STATX}
  1043. begin
  1044. If fpFStat(Handle,Info)=0 then
  1045. Result:=Info.st_Mtime;
  1046. end;
  1047. end;
  1048. Function FileSetDate (Handle : Longint;Age : Int64) : Longint;
  1049. {$ifdef USE_FUTIMES}
  1050. var
  1051. times : tkernel_timespecs;
  1052. {$endif USE_FUTIMES}
  1053. begin
  1054. Result:=0;
  1055. {$ifdef USE_FUTIMES}
  1056. times[0].tv_sec:=Age;
  1057. times[0].tv_nsec:=0;
  1058. times[1].tv_sec:=Age;
  1059. times[1].tv_nsec:=0;
  1060. if futimens(Handle,times) = -1 then
  1061. Result:=fpgeterrno;
  1062. {$else USE_FUTIMES}
  1063. FileSetDate:=-1;
  1064. {$endif USE_FUTIMES}
  1065. end;
  1066. Function FileGetAttr (Const FileName : RawByteString) : Longint;
  1067. Var
  1068. SystemFileName: RawByteString;
  1069. Info : Stat;
  1070. res : Integer;
  1071. begin
  1072. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  1073. res:=FpLStat(pointer(SystemFileName),Info);
  1074. if res<0 then
  1075. res:=FpStat(pointer(SystemFileName),Info);
  1076. if res<0 then
  1077. Result:=-1
  1078. Else
  1079. Result:=LinuxToWinAttr(SystemFileName,Info);
  1080. end;
  1081. Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint;
  1082. begin
  1083. Result:=-1;
  1084. end;
  1085. Function DeleteFile (Const FileName : RawByteString) : Boolean;
  1086. var
  1087. SystemFileName: RawByteString;
  1088. begin
  1089. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  1090. Result:=fpUnLink (PAnsiChar(SystemFileName))>=0;
  1091. end;
  1092. Function RenameFile (Const OldName, NewName : RawByteString) : Boolean;
  1093. var
  1094. SystemOldName, SystemNewName: RawByteString;
  1095. begin
  1096. SystemOldName:=ToSingleByteFileSystemEncodedFileName(OldName);
  1097. SystemNewName:=ToSingleByteFileSystemEncodedFileName(NewName);
  1098. RenameFile:=BU.FpRename(pointer(SystemOldName),pointer(SystemNewName))>=0;
  1099. end;
  1100. Function FileIsReadOnly(const FileName: RawByteString): Boolean;
  1101. var
  1102. SystemFileName: RawByteString;
  1103. begin
  1104. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  1105. Result:=fpAccess(PAnsiChar(SystemFileName),W_OK)<>0;
  1106. end;
  1107. Function FileSetDate (Const FileName : RawByteString; Age : Int64) : Longint;
  1108. var
  1109. SystemFileName: RawByteString;
  1110. {$ifdef USE_UTIMENSAT}
  1111. times : tkernel_timespecs;
  1112. {$endif USE_UTIMENSAT}
  1113. t: TUTimBuf;
  1114. begin
  1115. SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
  1116. Result:=0;
  1117. {$ifdef USE_UTIMENSAT}
  1118. times[0].tv_sec:=Age;
  1119. times[0].tv_nsec:=0;
  1120. times[1].tv_sec:=Age;
  1121. times[1].tv_nsec:=0;
  1122. if utimensat(AT_FDCWD,PAnsiChar(SystemFileName),times,0) = -1 then
  1123. Result:=fpgeterrno;
  1124. if fpgeterrno=ESysENOSYS then
  1125. {$endif USE_UTIMENSAT}
  1126. begin
  1127. Result:=0;
  1128. t.actime:= Age;
  1129. t.modtime:=Age;
  1130. if fputime(PAnsiChar(SystemFileName), @t) = -1 then
  1131. Result:=fpgeterrno;
  1132. end
  1133. end;
  1134. {$IF defined(DARWIN)}
  1135. Function IsFileNameCaseSensitive(Const aFileName : RawByteString) : Boolean;
  1136. var
  1137. res : clong;
  1138. begin
  1139. res:=FpPathconf(PAnsiChar(aFileName),11 {_PC_CASE_SENSITIVE });
  1140. { fall back to default if path is not found }
  1141. if res<0 then
  1142. Result:=FileNameCaseSensitive
  1143. else
  1144. Result:=res<>0;
  1145. end;
  1146. Function IsFileNameCaseSensitive(Const aFileName : UnicodeString) : Boolean;
  1147. begin
  1148. Result:=IsFileNameCaseSensitive(RawByteString(aFileName));
  1149. end;
  1150. Function IsFileNameCasePreserving(Const aFileName : RawByteString) : Boolean;
  1151. var
  1152. res : clong;
  1153. begin
  1154. res:=FpPathconf(PAnsiChar(aFileName),12 { _PC_CASE_PRESERVING });
  1155. if res<0 then
  1156. { fall back to default if path is not found }
  1157. Result:=FileNameCasePreserving
  1158. else
  1159. Result:=res<>0;
  1160. end;
  1161. Function IsFileNameCasePreserving(Const aFileName : UnicodeString) : Boolean;
  1162. begin
  1163. Result:=IsFileNameCasePreserving(RawByteString(aFileName));
  1164. end;
  1165. {$ENDIF defined(DARWIN)}
  1166. {****************************************************************************
  1167. Disk Functions
  1168. ****************************************************************************}
  1169. {
  1170. The Diskfree and Disksize functions need a file on the specified drive, since this
  1171. is required for the fpstatfs system call.
  1172. These filenames are set in drivestr[0..26], and have been preset to :
  1173. 0 - '.' (default drive - hence current dir is ok.)
  1174. 1 - '/fd0/.' (floppy drive 1 - should be adapted to local system )
  1175. 2 - '/fd1/.' (floppy drive 2 - should be adapted to local system )
  1176. 3 - '/' (C: equivalent of dos is the root partition)
  1177. 4..26 (can be set by you're own applications)
  1178. ! Use AddDisk() to Add new drives !
  1179. They both return -1 when a failure occurs.
  1180. }
  1181. Const
  1182. FixDriveStr : array[0..3] of PAnsiChar=(
  1183. '.',
  1184. '/fd0/.',
  1185. '/fd1/.',
  1186. '/.'
  1187. );
  1188. var
  1189. Drives : byte = 4;
  1190. DriveStr : array[4..26] of PAnsiChar;
  1191. Function GetDriveStr(Drive : Byte) : PAnsiChar;
  1192. begin
  1193. case Drive of
  1194. Low(FixDriveStr)..High(FixDriveStr):
  1195. Result := FixDriveStr[Drive];
  1196. Low(DriveStr)..High(DriveStr):
  1197. Result := DriveStr[Drive];
  1198. else
  1199. Result := nil;
  1200. end;
  1201. end;
  1202. Function DiskFree(Drive: Byte): int64;
  1203. var
  1204. p : PAnsiChar;
  1205. fs : TStatfs;
  1206. Begin
  1207. p:=GetDriveStr(Drive);
  1208. if (p<>nil) and (fpStatFS(p, @fs)<>-1) then
  1209. DiskFree := int64(fs.bavail)*int64(fs.bsize)
  1210. else
  1211. DiskFree := -1;
  1212. End;
  1213. Function DiskSize(Drive: Byte): int64;
  1214. var
  1215. p : PAnsiChar;
  1216. fs : TStatfs;
  1217. Begin
  1218. p:=GetDriveStr(Drive);
  1219. if (p<>nil) and (fpStatFS(p, @fs)<>-1) then
  1220. DiskSize := int64(fs.blocks)*int64(fs.bsize)
  1221. else
  1222. DiskSize := -1;
  1223. End;
  1224. Function AddDisk(const path: string): Byte;
  1225. begin
  1226. if DriveStr[Drives]<>nil then
  1227. FreeMem(DriveStr[Drives]);
  1228. GetMem(DriveStr[Drives],length(Path)+1);
  1229. StrPCopy(DriveStr[Drives],path);
  1230. Result:=Drives;
  1231. inc(Drives);
  1232. if Drives>High(DriveStr) then
  1233. Drives:=Low(DriveStr);
  1234. end;
  1235. Procedure FreeDriveStr;
  1236. var
  1237. i: longint;
  1238. begin
  1239. for i:=low(drivestr) to high(drivestr) do
  1240. if assigned(drivestr[i]) then
  1241. begin
  1242. freemem(drivestr[i]);
  1243. drivestr[i]:=nil;
  1244. end;
  1245. end;
  1246. {****************************************************************************
  1247. Misc Functions
  1248. ****************************************************************************}
  1249. {****************************************************************************
  1250. Locale Functions
  1251. ****************************************************************************}
  1252. Function GetEpochTime: cint;
  1253. {
  1254. Get the number of seconds since 00:00, January 1 1970, GMT
  1255. the time NOT corrected any way
  1256. }
  1257. begin
  1258. GetEpochTime:=fptime;
  1259. end;
  1260. Procedure DoGetUniversalDateTime(var year, month, day, hour, min, sec, msec, usec : word);
  1261. var
  1262. tz:timeval;
  1263. begin
  1264. fpgettimeofday(@tz,nil);
  1265. EpochToUniversal(tz.tv_sec,year,month,day,hour,min,sec);
  1266. msec:=tz.tv_usec div 1000;
  1267. usec:=tz.tv_usec mod 1000;
  1268. end;
  1269. // Now, adjusted to local time.
  1270. Procedure DoGetLocalDateTime(var year, month, day, hour, min, sec, msec, usec : word);
  1271. var
  1272. tz:timeval;
  1273. begin
  1274. fpgettimeofday(@tz,nil);
  1275. EpochToLocal(tz.tv_sec,year,month,day,hour,min,sec);
  1276. msec:=tz.tv_usec div 1000;
  1277. usec:=tz.tv_usec mod 1000;
  1278. end;
  1279. procedure GetTime(var hour,min,sec,msec,usec:word);
  1280. Var
  1281. year,day,month:Word;
  1282. begin
  1283. DoGetLocalDateTime(year,month,day,hour,min,sec,msec,usec);
  1284. end;
  1285. procedure GetTime(var hour,min,sec,sec100:word);
  1286. {
  1287. Gets the current time, adjusted to local time
  1288. }
  1289. var
  1290. year,day,month,usec : word;
  1291. begin
  1292. DoGetLocalDateTime(year,month,day,hour,min,sec,sec100,usec);
  1293. sec100:=sec100 div 10;
  1294. end;
  1295. Procedure GetTime(Var Hour,Min,Sec:Word);
  1296. {
  1297. Gets the current time, adjusted to local time
  1298. }
  1299. var
  1300. year,day,month,msec,usec : Word;
  1301. Begin
  1302. DoGetLocalDateTime(year,month,day,hour,min,sec,msec,usec);
  1303. End;
  1304. Procedure GetDate(Var Year,Month,Day:Word);
  1305. {
  1306. Gets the current date, adjusted to local time
  1307. }
  1308. var
  1309. hour,minute,second,msec,usec : word;
  1310. Begin
  1311. DoGetLocalDateTime(year,month,day,hour,minute,second,msec,usec);
  1312. End;
  1313. Procedure GetDateTime(Var Year,Month,Day,hour,minute,second:Word);
  1314. {
  1315. Gets the current date, adjusted to local time
  1316. }
  1317. Var
  1318. usec,msec : word;
  1319. Begin
  1320. DoGetLocalDateTime(year,month,day,hour,minute,second,msec,usec);
  1321. End;
  1322. {$ifndef FPUNONE}
  1323. Procedure GetLocalTime(var SystemTime: TSystemTime);
  1324. var
  1325. usecs : Word;
  1326. begin
  1327. DoGetLocalDateTime(SystemTime.Year, SystemTime.Month, SystemTime.Day,SystemTime.Hour, SystemTime.Minute, SystemTime.Second, SystemTime.MilliSecond, usecs);
  1328. SystemTime.DayOfWeek:=DayOfWeek(EncodeDate(SystemTime.Year,SystemTime.Month,SystemTime.Day))-1;
  1329. end ;
  1330. {$endif}
  1331. Procedure InitAnsi;
  1332. Var
  1333. i : longint;
  1334. begin
  1335. { Fill table entries 0 to 127 }
  1336. for i := 0 to 96 do
  1337. UpperCaseTable[i] := chr(i);
  1338. for i := 97 to 122 do
  1339. UpperCaseTable[i] := chr(i - 32);
  1340. for i := 123 to 191 do
  1341. UpperCaseTable[i] := chr(i);
  1342. Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
  1343. for i := 0 to 64 do
  1344. LowerCaseTable[i] := chr(i);
  1345. for i := 65 to 90 do
  1346. LowerCaseTable[i] := chr(i + 32);
  1347. for i := 91 to 191 do
  1348. LowerCaseTable[i] := chr(i);
  1349. Move (CPISO88591LCT,LowerCaseTable[192],SizeOf(CPISO88591UCT));
  1350. end;
  1351. Procedure InitInternational;
  1352. begin
  1353. InitInternationalGeneric;
  1354. InitAnsi;
  1355. end;
  1356. function SysErrorMessage(ErrorCode: Integer): String;
  1357. begin
  1358. Result:=StrError(ErrorCode);
  1359. end;
  1360. {****************************************************************************
  1361. OS utility functions
  1362. ****************************************************************************}
  1363. Function GetEnvironmentVariable(Const EnvVar : AnsiString) : AnsiString;
  1364. begin
  1365. Result:=BU.FPGetenv(PAnsiChar(pointer(EnvVar)));
  1366. end;
  1367. Function GetEnvironmentVariableCount : Integer;
  1368. begin
  1369. Result:=FPCCountEnvVar(EnvP);
  1370. end;
  1371. Function GetEnvironmentString(Index : Integer) : RTLString;
  1372. begin
  1373. Result:=FPCGetEnvStrFromP(Envp,Index);
  1374. end;
  1375. function ExecuteProcess(Const Path: RawByteString; Const ComLine: RawByteString;Flags:TExecuteFlags=[]):integer;
  1376. var
  1377. pid : longint;
  1378. e : EOSError;
  1379. CommandLine: RawByteString;
  1380. LPath : RawByteString;
  1381. cmdline2 : PPAnsiChar;
  1382. Begin
  1383. { always surround the name of the application by quotes
  1384. so that long filenames will always be accepted. But don't
  1385. do it if there are already double quotes!
  1386. }
  1387. // Only place we still parse
  1388. cmdline2:=nil;
  1389. LPath:=Path;
  1390. UniqueString(LPath);
  1391. SetCodePage(LPath,DefaultFileSystemCodePage,true);
  1392. if Comline<>'' Then
  1393. begin
  1394. CommandLine:=ComLine;
  1395. { Make an unique copy because stringtoppchar modifies the
  1396. string, and force conversion to intended fscp }
  1397. UniqueString(CommandLine);
  1398. SetCodePage(CommandLine,DefaultFileSystemCodePage,true);
  1399. cmdline2:=StringtoPPChar(CommandLine,1);
  1400. cmdline2^:=PAnsiChar(pointer(LPath));
  1401. end
  1402. else
  1403. begin
  1404. getmem(cmdline2,2*sizeof(PAnsiChar));
  1405. cmdline2^:=PAnsiChar(LPath);
  1406. cmdline2[1]:=nil;
  1407. end;
  1408. {$ifdef USE_VFORK}
  1409. pid:=fpvFork;
  1410. {$else USE_VFORK}
  1411. pid:=fpFork;
  1412. {$endif USE_VFORK}
  1413. if pid=0 then
  1414. begin
  1415. {The child does the actual exec, and then exits}
  1416. fpexecve(PAnsiChar(pointer(LPath)),Cmdline2,envp);
  1417. { If the execve fails, we return an exitvalue of 127, to let it be known}
  1418. fpExit(127);
  1419. end
  1420. else
  1421. if pid=-1 then {Fork failed}
  1422. begin
  1423. e:=EOSError.CreateFmt(SExecuteProcessFailed,[LPath,-1]);
  1424. e.ErrorCode:=-1;
  1425. raise e;
  1426. end;
  1427. { We're in the parent, let's wait. }
  1428. result:=WaitProcess(pid); // WaitPid and result-convert
  1429. if Comline<>'' Then
  1430. freemem(cmdline2);
  1431. if (result<0) or (result=127) then
  1432. begin
  1433. E:=EOSError.CreateFmt(SExecuteProcessFailed,[LPath,result]);
  1434. E.ErrorCode:=result;
  1435. Raise E;
  1436. end;
  1437. End;
  1438. function ExecuteProcess(Const Path: RawByteString; Const ComLine: Array Of RawByteString;Flags:TExecuteFlags=[]):integer;
  1439. var
  1440. pid : longint;
  1441. e : EOSError;
  1442. Begin
  1443. pid:=fpFork;
  1444. if pid=0 then
  1445. begin
  1446. {The child does the actual exec, and then exits}
  1447. fpexecl(Path,Comline);
  1448. { If the execve fails, we return an exitvalue of 127, to let it be known}
  1449. fpExit(127);
  1450. end
  1451. else
  1452. if pid=-1 then {Fork failed}
  1453. begin
  1454. e:=EOSError.CreateFmt(SExecuteProcessFailed,[Path,-1]);
  1455. e.ErrorCode:=-1;
  1456. raise e;
  1457. end;
  1458. { We're in the parent, let's wait. }
  1459. result:=WaitProcess(pid); // WaitPid and result-convert
  1460. if (result<0) or (result=127) then
  1461. begin
  1462. E:=EOSError.CreateFmt(SExecuteProcessFailed,[Path,result]);
  1463. E.ErrorCode:=result;
  1464. raise E;
  1465. end;
  1466. End;
  1467. procedure Sleep(milliseconds: Cardinal);
  1468. Var
  1469. timeout,timeoutresult : TTimespec;
  1470. res: cint;
  1471. begin
  1472. timeout.tv_sec:=milliseconds div 1000;
  1473. timeout.tv_nsec:=1000*1000*(milliseconds mod 1000);
  1474. repeat
  1475. res:=fpnanosleep(@timeout,@timeoutresult);
  1476. timeout:=timeoutresult;
  1477. until (res<>-1) or (fpgeterrno<>ESysEINTR);
  1478. end;
  1479. Function GetLastOSError : Integer;
  1480. begin
  1481. Result:=fpgetErrNo;
  1482. end;
  1483. { ---------------------------------------------------------------------
  1484. Application config files
  1485. ---------------------------------------------------------------------}
  1486. {$ifdef android}
  1487. var
  1488. _HomeDir: string;
  1489. _HasPackageDataDir: boolean;
  1490. Function GetHomeDir : String;
  1491. var
  1492. h: longint;
  1493. i: longint;
  1494. begin
  1495. Result:=_HomeDir;
  1496. if Result <> '' then
  1497. exit;
  1498. if IsLibrary then
  1499. begin
  1500. // For shared library get the package name of a host Java application
  1501. h:=FileOpen('/proc/self/cmdline', fmOpenRead or fmShareDenyNone);
  1502. if h >= 0 then
  1503. begin
  1504. SetLength(Result, MAX_PATH);
  1505. SetLength(Result, FileRead(h, Result[1], Length(Result)));
  1506. SetLength(Result, strlen(PAnsiChar(Result)));
  1507. FileClose(h);
  1508. Result:='/data/data/' + Result;
  1509. _HasPackageDataDir:=DirectoryExists(Result);
  1510. if _HasPackageDataDir then
  1511. begin
  1512. Result:=Result + '/files/';
  1513. ForceDirectories(Result);
  1514. end
  1515. else
  1516. Result:=''; // No package
  1517. end;
  1518. end;
  1519. if Result = '' then
  1520. Result:='/data/local/tmp/';
  1521. _HomeDir:=Result;
  1522. end;
  1523. Function XdgConfigHome : String;
  1524. begin
  1525. Result:=GetHomeDir;
  1526. end;
  1527. {$else}
  1528. Function GetHomeDir : String;
  1529. begin
  1530. Result:=GetEnvironmentVariable('HOME');
  1531. If (Result<>'') then
  1532. Result:=IncludeTrailingPathDelimiter(Result);
  1533. end;
  1534. { Follows base-dir spec,
  1535. see [http://freedesktop.org/Standards/basedir-spec].
  1536. Always ends with PathDelim. }
  1537. Function XdgConfigHome : String;
  1538. begin
  1539. Result:=GetEnvironmentVariable('XDG_CONFIG_HOME');
  1540. if (Result='') then
  1541. Result:=GetHomeDir + '.config/'
  1542. else
  1543. Result:=IncludeTrailingPathDelimiter(Result);
  1544. end;
  1545. {$endif android}
  1546. Function GetAppConfigDir(Global : Boolean) : String;
  1547. begin
  1548. If Global then
  1549. Result:=IncludeTrailingPathDelimiter(SysConfigDir)
  1550. else
  1551. Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
  1552. {$ifdef android}
  1553. if _HasPackageDataDir then
  1554. exit;
  1555. {$endif android}
  1556. if VendorName<>'' then
  1557. Result:=IncludeTrailingPathDelimiter(Result+VendorName);
  1558. Result:=IncludeTrailingPathDelimiter(Result+ApplicationName);
  1559. end;
  1560. Function GetAppConfigFile(Global : Boolean; SubDir : Boolean) : String;
  1561. begin
  1562. If Global then
  1563. Result:=IncludeTrailingPathDelimiter(SysConfigDir)
  1564. else
  1565. Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
  1566. {$ifdef android}
  1567. if _HasPackageDataDir then
  1568. begin
  1569. Result:=Result+'config'+ConfigExtension;
  1570. exit;
  1571. end;
  1572. {$endif android}
  1573. if SubDir then
  1574. begin
  1575. if VendorName<>'' then
  1576. Result:=IncludeTrailingPathDelimiter(Result+VendorName);
  1577. Result:=IncludeTrailingPathDelimiter(Result+ApplicationName);
  1578. end;
  1579. Result:=Result+ApplicationName+ConfigExtension;
  1580. end;
  1581. {****************************************************************************
  1582. GetTempDir
  1583. ****************************************************************************}
  1584. Function GetTempDir(Global : Boolean) : String;
  1585. begin
  1586. If Assigned(OnGetTempDir) then
  1587. Result:=OnGetTempDir(Global)
  1588. else
  1589. begin
  1590. {$ifdef android}
  1591. Result:=GetHomeDir + 'tmp';
  1592. ForceDirectories(Result);
  1593. {$else}
  1594. Result:=GetEnvironmentVariable('TEMP');
  1595. If (Result='') Then
  1596. Result:=GetEnvironmentVariable('TMP');
  1597. If (Result='') Then
  1598. Result:=GetEnvironmentVariable('TMPDIR');
  1599. if (Result='') then
  1600. Result:='/tmp/'; // fallback.
  1601. {$endif android}
  1602. end;
  1603. if (Result<>'') then
  1604. Result:=IncludeTrailingPathDelimiter(Result);
  1605. end;
  1606. {****************************************************************************
  1607. GetUserDir
  1608. ****************************************************************************}
  1609. Var
  1610. TheUserDir : String;
  1611. Function GetUserDir : String;
  1612. begin
  1613. If (TheUserDir='') then
  1614. begin
  1615. {$ifdef android}
  1616. TheUserDir:=GetHomeDir;
  1617. {$else}
  1618. TheUserDir:=GetEnvironmentVariable('HOME');
  1619. {$endif android}
  1620. if (TheUserDir<>'') then
  1621. TheUserDir:=IncludeTrailingPathDelimiter(TheUserDir)
  1622. else
  1623. TheUserDir:=GetTempDir(False);
  1624. end;
  1625. Result:=TheUserDir;
  1626. end;
  1627. Procedure SysBeep;
  1628. begin
  1629. Write(#7);
  1630. Flush(Output);
  1631. end;
  1632. function GetUniversalTime(var SystemTime: TSystemTime): Boolean;
  1633. var
  1634. usecs : Word;
  1635. begin
  1636. DoGetUniversalDateTime(SystemTime.Year, SystemTime.Month, SystemTime.Day,SystemTime.Hour, SystemTime.Minute, SystemTime.Second, SystemTime.MilliSecond, usecs);
  1637. Result:=True;
  1638. end;
  1639. function GetLocalTimeOffset: Integer;
  1640. begin
  1641. Result := -Tzseconds div 60;
  1642. end;
  1643. function GetLocalTimeOffset(const DateTime: TDateTime; const InputIsUTC: Boolean; out Offset: Integer): Boolean;
  1644. var
  1645. Year, Month, Day, Hour, Minute, Second, MilliSecond: word;
  1646. UnixTime: Int64;
  1647. lTZInfo: TTZInfo;
  1648. begin
  1649. DecodeDate(DateTime, Year, Month, Day);
  1650. DecodeTime(DateTime, Hour, Minute, Second, MilliSecond);
  1651. UnixTime:=UniversalToEpoch(Year, Month, Day, Hour, Minute, Second);
  1652. {$if declared(GetLocalTimezone)}
  1653. GetLocalTimeOffset:=GetLocalTimezone(UnixTime,InputIsUTC,lTZInfo);
  1654. if GetLocalTimeOffset then
  1655. Offset:=-lTZInfo.seconds div 60;
  1656. {$else}
  1657. GetLocalTimeOffset:=False;
  1658. {$endif}
  1659. end;
  1660. {$ifdef android}
  1661. procedure InitAndroid;
  1662. var
  1663. dlinfo: dl_info;
  1664. s: string;
  1665. begin
  1666. FillChar(dlinfo, sizeof(dlinfo), 0);
  1667. dladdr(@InitAndroid, @dlinfo);
  1668. s:=dlinfo.dli_fname;
  1669. if s <> '' then
  1670. SetDefaultSysLogTag(ExtractFileName(s));
  1671. end;
  1672. {$endif android}
  1673. {****************************************************************************
  1674. Initialization code
  1675. ****************************************************************************}
  1676. Initialization
  1677. InitExceptions; { Initialize exceptions. OS independent }
  1678. InitInternational; { Initialize internationalization settings }
  1679. SysConfigDir:='/etc'; { Initialize system config dir }
  1680. OnBeep:=@SysBeep;
  1681. {$ifdef android}
  1682. InitAndroid;
  1683. {$endif android}
  1684. Finalization
  1685. FreeDriveStr;
  1686. FreeTerminateProcs;
  1687. DoneExceptions;
  1688. end.