sysutils.pp 45 KB

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