sysutils.pp 47 KB

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