sysutils.pp 45 KB

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