sysutils.pp 50 KB

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