2
0

sysutils.pp 45 KB

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