unix.pp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt,
  4. BSD parts (c) 2000 by Marco van de Voort
  5. members of the Free Pascal development team.
  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 Unix;
  13. Interface
  14. Uses
  15. BaseUnix,UnixType,
  16. UnixUtil // tzseconds
  17. ;
  18. // If you deprecated new symbols, please annotate the version.
  19. // this makes it easier to decide if they can already be removed.
  20. {$if (defined(BSD) or defined(SUNOS)) and defined(FPC_USE_LIBC)}
  21. {$define USE_VFORK}
  22. {$endif}
  23. {$i aliasptp.inc}
  24. {$i unxconst.inc} { Get Types and Constants only exported in this unit }
  25. {** File handling **}
  26. Const
  27. P_IN = 1; // pipes (?)
  28. P_OUT = 2;
  29. LOCK_SH = 1; // flock constants ?
  30. LOCK_EX = 2;
  31. LOCK_UN = 8;
  32. LOCK_NB = 4;
  33. // The portable MAP_* and PROT_ constants are exported from unit Unix for compability.
  34. PROT_READ = baseunix.PROT_READ; { page can be read }
  35. PROT_WRITE = baseunix.PROT_WRITE; { page can be written }
  36. PROT_EXEC = baseunix.PROT_EXEC; { page can be executed }
  37. PROT_NONE = baseunix.PROT_NONE; { page can not be accessed }
  38. MAP_FAILED = baseunix.MAP_FAILED; { mmap() failed }
  39. MAP_SHARED = baseunix.MAP_SHARED; { Share changes }
  40. MAP_PRIVATE = baseunix.MAP_PRIVATE; { Changes are private }
  41. MAP_TYPE = baseunix.MAP_TYPE; { Mask for type of mapping }
  42. MAP_FIXED = baseunix.MAP_FIXED; { Interpret addr exactly }
  43. {** Time/Date Handling **}
  44. function Gettzdaylight : boolean;
  45. function Gettzname(const b : boolean) : pchar;
  46. property tzdaylight : boolean read Gettzdaylight;
  47. property tzname[b : boolean] : pchar read Gettzname;
  48. {************ Procedure/Functions ************}
  49. {$ifdef android}
  50. {$define DONT_READ_TIMEZONE}
  51. {$endif android}
  52. {$IFNDEF DONT_READ_TIMEZONE} // allows to disable linking in and trying for platforms
  53. // it doesn't (yet) work for.
  54. { timezone support }
  55. function GetLocalTimezone(timer:cint;var ATZInfo:TTZInfo;FullInfo:Boolean):Boolean;
  56. procedure GetLocalTimezone(timer:cint);
  57. procedure ReadTimezoneFile(fn:string);
  58. function GetTimezoneFile:string;
  59. Procedure ReReadLocalTime;
  60. {$ENDIF}
  61. {** Process Handling **}
  62. function FpExecLE (Const PathName:RawByteString;const S:Array Of RawByteString;MyEnv:ppchar):cint;
  63. function FpExecL (Const PathName:RawByteString;const S:Array Of RawByteString):cint;
  64. function FpExecLP (Const PathName:RawByteString;const S:Array Of RawByteString):cint;
  65. function FpExecLPE(Const PathName:RawByteString;const S:Array Of RawByteString;env:ppchar):cint;
  66. function FpExecV (Const PathName:RawByteString;args:ppchar):cint;
  67. function FpExecVP (Const PathName:RawByteString;args:ppchar):cint;
  68. function FpExecVPE(Const PathName:RawByteString;args,env:ppchar):cint;
  69. Function fpSystem(const Command:RawByteString):cint;
  70. Function WaitProcess (Pid:cint):cint;
  71. Function WIFSTOPPED (Status: Integer): Boolean;
  72. Function W_EXITCODE (ReturnCode, Signal: Integer): Integer;
  73. Function W_STOPCODE (Signal: Integer): Integer;
  74. {** File Handling **}
  75. Function fpFlock (var T : text;mode : cint) : cint;
  76. Function fpFlock (var F : File;mode : cint) : cint;
  77. {** Directory Handling **}
  78. procedure SeekDir(p:pdir;loc:clong);
  79. function TellDir(p:pdir):TOff;
  80. {** Pipe/Fifo/Stream **}
  81. Function AssignPipe (var pipe_in,pipe_out:cint):cint;
  82. Function AssignPipe (var pipe_in,pipe_out:text):cint;
  83. Function AssignPipe (var pipe_in,pipe_out:file):cint;
  84. Function POpen (var F:text;const Prog:RawByteString;rw:char):cint;
  85. Function POpen (var F:file;const Prog:RawByteString;rw:char):cint;
  86. Function POpen (var F:text;const Prog:UnicodeString;rw:char):cint;
  87. Function POpen (var F:file;const Prog:UnicodeString;rw:char):cint;
  88. Function AssignStream(Var StreamIn,Streamout:text;Const Prog:ansiString;const args : array of ansistring) : cint;
  89. Function AssignStream(Var StreamIn,Streamout,streamerr:text;Const Prog:ansiString;const args : array of ansistring) : cint;
  90. Function GetDomainName:String; deprecated; // because linux only.
  91. Function GetHostName:String;
  92. {** Utility functions **}
  93. Type
  94. TFSearchOption = (NoCurrentDirectory,
  95. CurrentDirectoryFirst,
  96. CurrentDirectoryLast);
  97. Function FSearch (const path:RawByteString;dirlist:RawByteString;CurrentDirStrategy:TFSearchOption):RawByteString;
  98. Function FSearch (const path:RawByteString;dirlist:RawByteString):RawByteString;
  99. Function FSearch (const path:UnicodeString;dirlist:UnicodeString;CurrentDirStrategy:TFSearchOption):UnicodeString;
  100. Function FSearch (const path:UnicodeString;dirlist:UnicodeString):UnicodeString;
  101. {$ifdef FPC_USE_LIBC}
  102. const clib = 'c';
  103. {$i unxdeclh.inc}
  104. {$else}
  105. {$i unxsysch.inc} // calls used in system and not reexported from baseunix
  106. {$endif}
  107. {******************************************************************************
  108. Implementation
  109. ******************************************************************************}
  110. {$i unxovlh.inc}
  111. Implementation
  112. {$ifndef FPC_USE_LIBC}
  113. Uses
  114. Syscall;
  115. {$endif}
  116. {$i unxovl.inc}
  117. {$ifndef FPC_USE_LIBC}
  118. {$i syscallh.inc}
  119. {$i unxsysc.inc}
  120. {$endif}
  121. {$i unxfunc.inc} { Platform specific implementations }
  122. Function getenv(name:string):Pchar; external name 'FPC_SYSC_FPGETENV';
  123. {******************************************************************************
  124. timezone support
  125. ******************************************************************************}
  126. function Gettzdaylight : boolean;
  127. begin
  128. Gettzdaylight:=Tzinfo.daylight;
  129. end;
  130. function Gettzname(const b : boolean) : pchar;
  131. begin
  132. Gettzname:=Tzinfo.name[b];
  133. end;
  134. {******************************************************************************
  135. Process related calls
  136. ******************************************************************************}
  137. { Most calls of WaitPID do not handle the result correctly, this funktion treats errors more correctly }
  138. Function WaitProcess(Pid:cint):cint; { like WaitPid(PID,@result,0) Handling of Signal interrupts (errno=EINTR), returning the Exitcode of Process (>=0) or -Status if terminated}
  139. var
  140. r,s : cint;
  141. begin
  142. s:=$7F00;
  143. repeat
  144. r:=fpWaitPid(Pid,@s,0);
  145. if (r=-1) and (fpgeterrno=ESysEIntr) Then
  146. r:=0;
  147. until (r<>0);
  148. if (r=-1) or (r=0) then // 0 is not a valid return and should never occur (it means status invalid when using WNOHANG)
  149. WaitProcess:=-1 // return -1 to indicate an error. fpwaitpid updated it.
  150. else
  151. begin
  152. if wifexited(s) then
  153. WaitProcess:=wexitstatus(s)
  154. else if (s>0) then // Until now there is not use of the highest bit , but check this for the future
  155. WaitProcess:=-s // normal case
  156. else
  157. WaitProcess:=s; // s<0 should not occur, but wie return also a negativ value
  158. end;
  159. end;
  160. function intFpExecVEMaybeP (Const PathName:RawByteString;Args,MyEnv:ppchar;SearchPath:Boolean):cint;
  161. // does an ExecVE, but still has to handle P
  162. // execv variants call this directly, execl variants indirectly via
  163. // intfpexecl
  164. Var
  165. NewCmd : RawByteString;
  166. ThePath : RawByteString;
  167. Begin
  168. If SearchPath and (pos('/',pathname)=0) Then
  169. Begin
  170. // The above could be better. (check if not escaped/quoted '/'s) ?
  171. // (Jilles says this is ok)
  172. // Stevens says only search if newcmd contains no '/'
  173. // fsearch is not ansistring clean yet.
  174. ThePath:=fpgetenv('PATH');
  175. SetCodePage(ThePath,DefaultSystemCodePage,false);
  176. SetCodePage(ThePath,DefaultFileSystemCodePage,true);
  177. if thepath='' then
  178. thepath:='.'; // FreeBSD uses _PATH_DEFPATH = /usr/bin:/bin
  179. // but a quick check showed that _PATH_DEFPATH
  180. // varied from OS to OS
  181. newcmd:=ToSingleByteFileSystemEncodedFileName(FSearch(pathname,thepath,NoCurrentDirectory));
  182. // FreeBSD libc keeps on trying till a file is successfully run.
  183. // Stevens says "try each path prefix"
  184. // execp puts newcmd here.
  185. args^:=pchar(newcmd);
  186. End else
  187. newcmd:=ToSingleByteFileSystemEncodedFileName(pathname);
  188. // repeat
  189. // if searchpath then args^:=pchar(commandtorun)
  190. IntFpExecVEMaybeP:=fpExecVE(newcmd,Args,MyEnv);
  191. {
  192. // Code that if exec fails due to permissions, tries to run it with sh
  193. // Should we deallocate p on fail? -> no fpexit is run no matter what
  194. //
  195. }
  196. // if intfpexecvemaybep=-1 then seach next file.
  197. // until (Goexit) or SearchExit;
  198. {
  199. If IntFpExec=-1 Then
  200. Begin
  201. Error:=fpGetErrno
  202. Case Error of
  203. ESysE2Big : Exit(-1);
  204. ESysELoop,
  205. : Exit(-1);
  206. }
  207. end;
  208. function intFpExecl (Const PathName:RawByteString;const s:array of RawByteString;MyEnv:ppchar;SearchPath:Boolean):cint;
  209. { Handles the array of ansistring -> ppchar conversion.
  210. Base for the the "l" variants.
  211. }
  212. var p:ppchar;
  213. i:integer;
  214. s2:array of Rawbytestring;
  215. begin
  216. If PathName='' Then
  217. Begin
  218. fpsetErrno(ESysEnoEnt);
  219. Exit(-1); // Errno?
  220. End;
  221. setlength(s2,high(s)+1);
  222. for i:=low(s) to high(s) do
  223. s2[i]:=ToSingleByteFileSystemEncodedFileName(s[i]);
  224. p:=ArrayStringToPPchar(s2,1);
  225. if p=NIL Then
  226. Begin
  227. GetMem(p,2*sizeof(pchar));
  228. if p=nil then
  229. begin
  230. {$ifdef xunix}
  231. fpseterrno(ESysEnoMem);
  232. {$endif}
  233. fpseterrno(ESysEnoEnt);
  234. exit(-1);
  235. end;
  236. p[1]:=nil;
  237. End;
  238. p^:=pchar(PathName);
  239. IntFPExecL:=intFpExecVEMaybeP(PathName,p,MyEnv,SearchPath);
  240. // If we come here, no attempts were executed successfully.
  241. Freemem(p);
  242. end;
  243. function FpExecLE (Const PathName:RawByteString;const S:Array Of RawByteString;MyEnv:ppchar):cint;
  244. Begin
  245. FpExecLE:=intFPExecl(PathName,s,MyEnv,false);
  246. End;
  247. function FpExecL(Const PathName:RawByteString;const S:Array Of RawByteString):cint;
  248. Begin
  249. FpExecL:=intFPExecl(PathName,S,EnvP,false);
  250. End;
  251. function FpExecLP(Const PathName:RawByteString;const S:Array Of RawByteString):cint;
  252. Begin
  253. FpExecLP:=intFPExecl(PathName,S,EnvP,True);
  254. End;
  255. function FpExecLPE(Const PathName:RawByteString;const S:Array Of RawByteString;env:ppchar):cint;
  256. Begin
  257. FpExecLPE:=intFPExecl(PathName,S,Env,True);
  258. End;
  259. function FpExecV(Const PathName:RawByteString;args:ppchar):cint;
  260. Begin
  261. fpexecV:=intFpExecVEMaybeP (PathName,args,envp,false);
  262. End;
  263. function FpExecVP(Const PathName:RawByteString;args:ppchar):cint;
  264. Begin
  265. fpexecVP:=intFpExecVEMaybeP (PathName,args,envp,true);
  266. End;
  267. function FpExecVPE(Const PathName:RawByteString;args,env:ppchar):cint;
  268. Begin
  269. fpexecVPE:=intFpExecVEMaybeP (PathName,args,env,true);
  270. End;
  271. // exect and execvP (ExecCapitalP) are not implement
  272. // Non POSIX anyway.
  273. // Exect turns on tracing for the process
  274. // execvP has the searchpath as array of ansistring ( const char *search_path)
  275. {$define FPC_USE_FPEXEC}
  276. {$if defined(FPC_USE_FPEXEC) and not defined(USE_VFORK)}
  277. {$define SHELL_USE_FPEXEC}
  278. {$endif}
  279. {$ifdef FPC_USE_LIBC}
  280. function xfpsystem(p:pchar):cint; cdecl; external clib name 'system';
  281. Function fpSystem(const Command:RawByteString):cint;
  282. var
  283. cmd: RawByteString;
  284. begin
  285. cmd:=ToSingleByteFileSystemEncodedFileName(Command);
  286. fpsystem:=xfpsystem(pchar(cmd));
  287. end;
  288. {$else}
  289. Function fpSystem(const Command:RawByteString):cint;
  290. var
  291. pid,savedpid : cint;
  292. pstat : cint;
  293. ign,intact,
  294. quitact : SigactionRec;
  295. newsigblock,
  296. oldsigblock : tsigset;
  297. {$ifndef SHELL_USE_FPEXEC}
  298. p : ppchar;
  299. {$endif}
  300. cmd : RawByteString;
  301. begin { Changes as above }
  302. { fpexec* take care of converting the command to the right code page }
  303. if command='' then exit(1);
  304. {$ifndef SHELL_USE_FPEXEC}
  305. p:=CreateShellArgv(command);
  306. {$endif}
  307. ign.sa_handler:=SigActionHandler(SIG_IGN);
  308. fpsigemptyset(ign.sa_mask);
  309. ign.sa_flags:=0;
  310. fpsigaction(SIGINT, @ign, @intact);
  311. fpsigaction(SIGQUIT, @ign, @quitact);
  312. fpsigemptyset(newsigblock);
  313. fpsigaddset(newsigblock,SIGCHLD);
  314. fpsigprocmask(SIG_BLOCK,newsigblock,oldsigblock);
  315. {$ifdef USE_VFORK}
  316. pid:=fpvfork;
  317. {$else USE_VFORK}
  318. pid:=fpfork;
  319. {$endif USE_VFORK}
  320. if pid=0 then // We are in the Child
  321. begin
  322. fpsigaction(SIGINT,@intact,NIL);
  323. fpsigaction(SIGQUIT,@quitact,NIL);
  324. fpsigprocmask(SIG_SETMASK,@oldsigblock,NIL);
  325. {$ifndef SHELL_USE_FPEXEC}
  326. fpExecve(p^,p,envp);
  327. {$else}
  328. fpexecl('/bin/sh',['-c',Command]);
  329. {$endif}
  330. fpExit(127); // was exit(127)!! We must exit the Process, not the function
  331. end
  332. else if (pid<>-1) then // Successfull started
  333. begin
  334. savedpid:=pid;
  335. repeat
  336. pid:=fpwaitpid(savedpid,@pstat,0);
  337. until (pid<>-1) and (fpgeterrno()<>ESysEintr);
  338. if pid=-1 Then
  339. fpsystem:=-1
  340. else
  341. fpsystem:=pstat;
  342. end
  343. else // no success
  344. fpsystem:=-1;
  345. fpsigaction(SIGINT,@intact,NIL);
  346. fpsigaction(SIGQUIT,@quitact,NIL);
  347. fpsigprocmask(SIG_SETMASK,@oldsigblock,NIL);
  348. {$ifndef SHELL_USE_FPEXEC}
  349. FreeShellArgV(p);
  350. {$endif}
  351. end;
  352. {$endif}
  353. Function WIFSTOPPED(Status: Integer): Boolean;
  354. begin
  355. WIFSTOPPED:=((Status and $FF)=$7F);
  356. end;
  357. Function W_EXITCODE(ReturnCode, Signal: Integer): Integer;
  358. begin
  359. W_EXITCODE:=(ReturnCode shl 8) or Signal;
  360. end;
  361. Function W_STOPCODE(Signal: Integer): Integer;
  362. begin
  363. W_STOPCODE:=(Signal shl 8) or $7F;
  364. end;
  365. {$IFNDEF DONT_READ_TIMEZONE}
  366. { Include timezone handling routines which use /usr/share/timezone info }
  367. {$i timezone.inc}
  368. {$endif}
  369. {******************************************************************************
  370. FileSystem calls
  371. ******************************************************************************}
  372. Function fpFlock (var T : text;mode : cint) : cint;
  373. begin
  374. {$ifndef beos}
  375. fpFlock:=fpFlock(TextRec(T).Handle,mode);
  376. {$endif}
  377. end;
  378. Function fpFlock (var F : File;mode : cint) :cint;
  379. begin
  380. {$ifndef beos}
  381. fpFlock:=fpFlock(FileRec(F).Handle,mode);
  382. {$endif}
  383. end;
  384. Function SelectText(var T:Text;TimeOut :PTimeval):cint;
  385. Var
  386. F:TfdSet;
  387. begin
  388. if textrec(t).mode=fmclosed then
  389. begin
  390. fpseterrno(ESysEBADF);
  391. exit(-1);
  392. end;
  393. FpFD_ZERO(f);
  394. fpFD_SET(textrec(T).handle,f);
  395. if textrec(T).mode=fminput then
  396. SelectText:=fpselect(textrec(T).handle+1,@f,nil,nil,TimeOut)
  397. else
  398. SelectText:=fpselect(textrec(T).handle+1,nil,@f,nil,TimeOut);
  399. end;
  400. Function SelectText(var T:Text;TimeOut :cint):cint;
  401. var
  402. p : PTimeVal;
  403. tv : TimeVal;
  404. begin
  405. if TimeOut=-1 then
  406. p:=nil
  407. else
  408. begin
  409. tv.tv_Sec:=Timeout div 1000;
  410. tv.tv_Usec:=(Timeout mod 1000)*1000;
  411. p:=@tv;
  412. end;
  413. SelectText:=SelectText(T,p);
  414. end;
  415. {******************************************************************************
  416. Directory
  417. ******************************************************************************}
  418. procedure SeekDir(p:pdir;loc:clong);
  419. begin
  420. if p=nil then
  421. begin
  422. fpseterrno(ESysEBADF);
  423. exit;
  424. end;
  425. {$if not(defined(bsd)) and not(defined(solaris)) and not(defined(beos)) and not(defined(aix)) }
  426. p^.dd_nextoff:=fplseek(p^.dd_fd,loc,seek_set);
  427. {$endif}
  428. {$if not(defined(beos))}
  429. p^.dd_size:=0;
  430. p^.dd_loc:=0;
  431. {$endif}
  432. end;
  433. function TellDir(p:pdir):TOff;
  434. begin
  435. if p=nil then
  436. begin
  437. fpseterrno(ESysEBADF);
  438. telldir:=-1;
  439. exit;
  440. end;
  441. {$ifndef beos}
  442. telldir:=fplseek(p^.dd_fd,0,seek_cur)
  443. {$endif}
  444. { We could try to use the nextoff field here, but on my 1.2.13
  445. kernel, this gives nothing... This may have to do with
  446. the readdir implementation of libc... I also didn't find any trace of
  447. the field in the kernel code itself, So I suspect it is an artifact of libc.
  448. Michael. }
  449. end;
  450. {******************************************************************************
  451. Pipes/Fifo
  452. ******************************************************************************}
  453. Procedure OpenPipe(var F:Text);
  454. begin
  455. case textrec(f).mode of
  456. fmoutput :
  457. if textrec(f).userdata[1]<>P_OUT then
  458. textrec(f).mode:=fmclosed;
  459. fminput :
  460. if textrec(f).userdata[1]<>P_IN then
  461. textrec(f).mode:=fmclosed;
  462. else
  463. textrec(f).mode:=fmclosed;
  464. end;
  465. end;
  466. Function IOPipe(var F:text):cint;
  467. begin
  468. IOPipe:=0;
  469. case textrec(f).mode of
  470. fmoutput :
  471. begin
  472. { first check if we need something to write, else we may
  473. get a SigPipe when Close() is called (PFV) }
  474. if textrec(f).bufpos>0 then
  475. IOPipe:=fpwrite(textrec(f).handle,pchar(textrec(f).bufptr),textrec(f).bufpos);
  476. end;
  477. fminput : Begin
  478. textrec(f).bufend:=fpread(textrec(f).handle,pchar(textrec(f).bufptr),textrec(f).bufsize);
  479. IOPipe:=textrec(f).bufend;
  480. End;
  481. end;
  482. textrec(f).bufpos:=0;
  483. end;
  484. Function FlushPipe(var F:Text):cint;
  485. begin
  486. FlushPipe:=0;
  487. if (textrec(f).mode=fmoutput) and (textrec(f).bufpos<>0) then
  488. FlushPipe:=IOPipe(f);
  489. textrec(f).bufpos:=0;
  490. end;
  491. Function ClosePipe(var F:text):cint;
  492. begin
  493. textrec(f).mode:=fmclosed;
  494. ClosePipe:=fpclose(textrec(f).handle);
  495. end;
  496. Function AssignPipe(var pipe_in,pipe_out:text):cint;
  497. {
  498. Sets up a pair of file variables, which act as a pipe. The first one can
  499. be read from, the second one can be written to.
  500. }
  501. var
  502. f_in,f_out : cint;
  503. begin
  504. if AssignPipe(f_in,f_out)=-1 then
  505. exit(-1);
  506. { Set up input }
  507. Assign(Pipe_in,'');
  508. Textrec(Pipe_in).Handle:=f_in;
  509. Textrec(Pipe_in).Mode:=fmInput;
  510. Textrec(Pipe_in).userdata[1]:=P_IN;
  511. TextRec(Pipe_in).OpenFunc:=@OpenPipe;
  512. TextRec(Pipe_in).InOutFunc:=@IOPipe;
  513. TextRec(Pipe_in).FlushFunc:=@FlushPipe;
  514. TextRec(Pipe_in).CloseFunc:=@ClosePipe;
  515. { Set up output }
  516. Assign(Pipe_out,'');
  517. Textrec(Pipe_out).Handle:=f_out;
  518. Textrec(Pipe_out).Mode:=fmOutput;
  519. Textrec(Pipe_out).userdata[1]:=P_OUT;
  520. TextRec(Pipe_out).OpenFunc:=@OpenPipe;
  521. TextRec(Pipe_out).InOutFunc:=@IOPipe;
  522. TextRec(Pipe_out).FlushFunc:=@FlushPipe;
  523. TextRec(Pipe_out).CloseFunc:=@ClosePipe;
  524. AssignPipe:=0;
  525. end;
  526. Function AssignPipe(var pipe_in,pipe_out:file):cint;
  527. {
  528. Sets up a pair of file variables, which act as a pipe. The first one can
  529. be read from, the second one can be written to.
  530. If the operation was unsuccesful,
  531. }
  532. var
  533. f_in,f_out : cint;
  534. begin
  535. if AssignPipe(f_in,f_out)=-1 then
  536. exit(-1);
  537. { Set up input }
  538. Assign(Pipe_in,'');
  539. Filerec(Pipe_in).Handle:=f_in;
  540. Filerec(Pipe_in).Mode:=fmInput;
  541. Filerec(Pipe_in).recsize:=1;
  542. Filerec(Pipe_in).userdata[1]:=P_IN;
  543. { Set up output }
  544. Assign(Pipe_out,'');
  545. Filerec(Pipe_out).Handle:=f_out;
  546. Filerec(Pipe_out).Mode:=fmoutput;
  547. Filerec(Pipe_out).recsize:=1;
  548. Filerec(Pipe_out).userdata[1]:=P_OUT;
  549. AssignPipe:=0;
  550. end;
  551. Function PCloseText(Var F:text):cint;
  552. {
  553. May not use @PClose due overloading
  554. }
  555. begin
  556. PCloseText:=PClose(f);
  557. end;
  558. Function POpen_internal(var F:text;const Prog:RawByteString;rw:char):cint;
  559. {
  560. Starts the program in 'Prog' and makes it's input or out put the
  561. other end of a pipe. If rw is 'w' or 'W', then whatever is written to
  562. F, will be read from stdin by the program in 'Prog'. The inverse is true
  563. for 'r' or 'R' : whatever the program in 'Prog' writes to stdout, can be
  564. read from 'f'.
  565. }
  566. var
  567. pipi,
  568. pipo : text;
  569. pid : cint;
  570. pl : ^cint;
  571. {$if not defined(FPC_USE_FPEXEC) or defined(USE_VFORK)}
  572. pp : array[0..3] of pchar;
  573. temp : string[255];
  574. {$endif not FPC_USE_FPEXEC or USE_VFORK}
  575. ret : cint;
  576. begin
  577. rw:=upcase(rw);
  578. if not (rw in ['R','W']) then
  579. begin
  580. FpSetErrno(ESysEnoent);
  581. exit(-1);
  582. end;
  583. ret:=AssignPipe(pipi,pipo);
  584. if ret=-1 then
  585. exit(-1);
  586. {$ifdef USE_VFORK}
  587. pid:=fpvfork;
  588. {$else USE_VFORK}
  589. pid:=fpfork;
  590. {$endif USE_VFORK}
  591. if pid=-1 then
  592. begin
  593. close(pipi);
  594. close(pipo);
  595. exit(-1);
  596. end;
  597. if pid=0 then
  598. begin
  599. { We're in the child }
  600. if rw='W' then
  601. begin
  602. if (textrec(pipi).handle <> stdinputhandle) then
  603. begin
  604. ret:=fpdup2(pipi,input);
  605. {$ifdef USE_VFORK}
  606. fpclose(textrec(pipi).handle);
  607. {$else USE_VFORK}
  608. close(pipi);
  609. {$endif USE_VFORK}
  610. end;
  611. {$ifdef USE_VFORK}
  612. fpclose(textrec(pipo).handle);
  613. {$else USE_VFORK}
  614. close(pipo);
  615. {$endif USE_VFORK}
  616. if ret=-1 then
  617. fpexit(127);
  618. end
  619. else
  620. begin
  621. {$ifdef USE_VFORK}
  622. fpclose(textrec(pipi).handle);
  623. {$else USE_VFORK}
  624. close(pipi);
  625. {$endif USE_VFORK}
  626. if (textrec(pipo).handle <> stdoutputhandle) then
  627. begin
  628. ret:=fpdup2(pipo,output);
  629. {$ifdef USE_VFORK}
  630. fpclose(textrec(pipo).handle);
  631. {$else USE_VFORK}
  632. close(pipo);
  633. {$endif USE_VFORK}
  634. end;
  635. if ret=-1 then
  636. fpexit(127);
  637. end;
  638. {$if defined(FPC_USE_FPEXEC) and not defined(USE_VFORK)}
  639. fpexecl(pchar('/bin/sh'),['-c',Prog]);
  640. {$else}
  641. temp:='/bin/sh'#0'-c'#0;
  642. pp[0]:=@temp[1];
  643. pp[1]:=@temp[9];
  644. pp[2]:=@prog[1];
  645. pp[3]:=Nil;
  646. fpExecve('/bin/sh',@pp,envp);
  647. {$endif}
  648. fpexit(127);
  649. end
  650. else
  651. begin
  652. { We're in the parent }
  653. if rw='W' then
  654. begin
  655. close(pipi);
  656. f:=pipo;
  657. end
  658. else
  659. begin
  660. close(pipo);
  661. f:=pipi;
  662. end;
  663. textrec(f).bufptr:=@textrec(f).buffer;
  664. {Save the process ID - needed when closing }
  665. pl:=pcint(@textrec(f).userdata[2]);
  666. { avoid alignment error on sparc }
  667. move(pid,pl^,sizeof(pid));
  668. textrec(f).closefunc:=@PCloseText;
  669. end;
  670. POpen_internal:=0;
  671. end;
  672. Function POpen_internal(var F:file;const Prog:RawByteString;rw:char):cint;
  673. {
  674. Starts the program in 'Prog' and makes it's input or out put the
  675. other end of a pipe. If rw is 'w' or 'W', then whatever is written to
  676. F, will be read from stdin by the program in 'Prog'. The inverse is true
  677. for 'r' or 'R' : whatever the program in 'Prog' writes to stdout, can be
  678. read from 'f'.
  679. }
  680. var
  681. pipi,
  682. pipo : file;
  683. pid : cint;
  684. pl : ^cint;
  685. {$if not defined(FPC_USE_FPEXEC) or defined(USE_VFORK)}
  686. pp : array[0..3] of pchar;
  687. temp : string[255];
  688. {$endif not FPC_USE_FPEXEC or USE_VFORK}
  689. ret : cint;
  690. begin
  691. rw:=upcase(rw);
  692. if not (rw in ['R','W']) then
  693. begin
  694. FpSetErrno(ESysEnoent);
  695. exit(-1);
  696. end;
  697. ret:=AssignPipe(pipi,pipo);
  698. if ret=-1 then
  699. exit(-1);
  700. {$ifdef USE_VFORK}
  701. pid:=fpvfork;
  702. {$else USE_VFORK}
  703. pid:=fpfork;
  704. {$endif USE_VFORK}
  705. if pid=-1 then
  706. begin
  707. close(pipi);
  708. close(pipo);
  709. exit(-1);
  710. end;
  711. if pid=0 then
  712. begin
  713. { We're in the child }
  714. if rw='W' then
  715. begin
  716. if (filerec(pipi).handle <> stdinputhandle) then
  717. begin
  718. ret:=fpdup2(filerec(pipi).handle,stdinputhandle);
  719. {$ifdef USE_VFORK}
  720. fpclose(filerec(pipi).handle);
  721. {$else USE_VFORK}
  722. close(pipi);
  723. {$endif USE_VFORK}
  724. end;
  725. {$ifdef USE_VFORK}
  726. fpclose(filerec(pipo).handle);
  727. {$else USE_VFORK}
  728. close(pipo);
  729. {$endif USE_VFORK}
  730. if ret=-1 then
  731. fpexit(127);
  732. end
  733. else
  734. begin
  735. {$ifdef USE_VFORK}
  736. fpclose(filerec(pipi).handle);
  737. {$else USE_VFORK}
  738. close(pipi);
  739. {$endif USE_VFORK}
  740. if (filerec(pipo).handle <> stdoutputhandle) then
  741. begin
  742. ret:=fpdup2(filerec(pipo).handle,stdoutputhandle);
  743. {$ifdef USE_VFORK}
  744. fpclose(filerec(pipo).handle);
  745. {$else USE_VFORK}
  746. close(pipo);
  747. {$endif USE_VFORK}
  748. end;
  749. if ret=-1 then
  750. fpexit(127);
  751. end;
  752. {$if defined(FPC_USE_FPEXEC) and not defined(USE_VFORK)}
  753. fpexecl(pchar('/bin/sh'),['-c',Prog]);
  754. {$else}
  755. temp:='/bin/sh'#0'-c'#0;
  756. pp[0]:=@temp[1];
  757. pp[1]:=@temp[9];
  758. pp[2]:=@prog[1];
  759. pp[3]:=Nil;
  760. fpExecve('/bin/sh',@pp,envp);
  761. {$endif}
  762. fpexit(127);
  763. end
  764. else
  765. begin
  766. { We're in the parent }
  767. if rw='W' then
  768. begin
  769. close(pipi);
  770. f:=pipo;
  771. end
  772. else
  773. begin
  774. close(pipo);
  775. f:=pipi;
  776. end;
  777. {Save the process ID - needed when closing }
  778. pl:=pcint(@filerec(f).userdata[2]);
  779. { avoid alignment error on sparc }
  780. move(pid,pl^,sizeof(pid));
  781. end;
  782. POpen_internal:=0;
  783. end;
  784. Function POpen(var F:text;const Prog:RawByteString;rw:char):cint;
  785. begin
  786. { can't do the ToSingleByteFileSystemEncodedFileName() conversion inside
  787. POpen_internal, because this may destroy the temp rawbytestring result
  788. of that function in the parent before the child is finished with it }
  789. POpen:=POpen_internal(F,ToSingleByteFileSystemEncodedFileName(Prog),rw);
  790. end;
  791. Function POpen(var F:file;const Prog:RawByteString;rw:char):cint;
  792. begin
  793. { can't do the ToSingleByteFileSystemEncodedFileName() conversion inside
  794. POpen_internal, because this may destroy the temp rawbytestring result
  795. of that function in the parent before the child is finished with it }
  796. POpen:=POpen_internal(F,ToSingleByteFileSystemEncodedFileName(Prog),rw);
  797. end;
  798. function POpen(var F: text; const Prog: UnicodeString; rw: char): cint;
  799. begin
  800. POpen:=POpen_internal(F,ToSingleByteFileSystemEncodedFileName(Prog),rw);
  801. end;
  802. function POpen(var F: file; const Prog: UnicodeString; rw: char): cint;
  803. begin
  804. POpen:=POpen_internal(F,ToSingleByteFileSystemEncodedFileName(Prog),rw);
  805. end;
  806. Function AssignStream(Var StreamIn,Streamout:text;Const Prog:ansiString;const args : array of ansistring) : cint;
  807. {
  808. Starts the program in 'Prog' and makes its input and output the
  809. other end of two pipes, which are the stdin and stdout of a program
  810. specified in 'Prog'.
  811. streamout can be used to write to the program, streamin can be used to read
  812. the output of the program. See the following diagram :
  813. Parent Child
  814. STreamout --> Input
  815. Streamin <-- Output
  816. Return value is the process ID of the process being spawned, or -1 in case of failure.
  817. }
  818. var
  819. pipi,
  820. pipo : text;
  821. pid : cint;
  822. pl : ^cint;
  823. begin
  824. AssignStream:=-1;
  825. if fpAccess(prog,X_OK)<>0 then
  826. exit(-1);
  827. if AssignPipe(streamin,pipo)=-1 Then
  828. exit(-1);
  829. if AssignPipe(pipi,streamout)=-1 Then
  830. begin
  831. close(streamin);
  832. close(pipo);
  833. exit(-1);
  834. end;
  835. pid:=fpfork;
  836. if pid=-1 then
  837. begin
  838. close(pipi);
  839. close(pipo);
  840. close (streamin);
  841. close (streamout);
  842. exit;
  843. end;
  844. if pid=0 then
  845. begin
  846. { We're in the child }
  847. { Close what we don't need }
  848. close(streamout);
  849. close(streamin);
  850. if fpdup2(pipi,input)=-1 Then
  851. halt(127);
  852. close(pipi);
  853. If fpdup2(pipo,output)=-1 Then
  854. halt (127);
  855. close(pipo);
  856. fpExecl(Prog,args);
  857. halt(127);
  858. end
  859. else
  860. begin
  861. { we're in the parent}
  862. close(pipo);
  863. close(pipi);
  864. {Save the process ID - needed when closing }
  865. pl:=pcint(@textrec(StreamIn).userdata[2]);
  866. { avoid alignment error on sparc }
  867. move(pid,pl^,sizeof(pid));
  868. textrec(StreamIn).closefunc:=@PCloseText;
  869. {Save the process ID - needed when closing }
  870. pl:=pcint(@textrec(StreamOut).userdata[2]);
  871. { avoid alignment error on sparc }
  872. move(pid,pl^,sizeof(pid));
  873. textrec(StreamOut).closefunc:=@PCloseText;
  874. AssignStream:=Pid;
  875. end;
  876. end;
  877. Function AssignStream(Var StreamIn,Streamout,streamerr:text;Const Prog:ansiString;const args : array of ansistring) : cint;
  878. {
  879. Starts the program in 'prog' and makes its input, output and error output the
  880. other end of three pipes, which are the stdin, stdout and stderr of a program
  881. specified in 'prog'.
  882. StreamOut can be used to write to the program, StreamIn can be used to read
  883. the output of the program, StreamErr reads the error output of the program.
  884. See the following diagram :
  885. Parent Child
  886. StreamOut --> StdIn (input)
  887. StreamIn <-- StdOut (output)
  888. StreamErr <-- StdErr (error output)
  889. }
  890. var
  891. PipeIn, PipeOut, PipeErr: text;
  892. pid: cint;
  893. pl: ^cint;
  894. begin
  895. AssignStream := -1;
  896. if fpAccess(prog,X_OK)<>0 then
  897. exit(-1);
  898. // Assign pipes
  899. if AssignPipe(StreamIn, PipeOut)=-1 Then
  900. Exit(-1);
  901. If AssignPipe(StreamErr, PipeErr)=-1 Then
  902. begin
  903. Close(StreamIn);
  904. Close(PipeOut);
  905. exit(-1);
  906. end;
  907. if AssignPipe(PipeIn, StreamOut)=-1 Then
  908. begin
  909. Close(StreamIn);
  910. Close(PipeOut);
  911. Close(StreamErr);
  912. Close(PipeErr);
  913. exit(-1);
  914. end;
  915. // Fork
  916. pid := fpFork;
  917. if pid=-1 then begin
  918. Close(StreamIn);
  919. Close(PipeOut);
  920. Close(StreamErr);
  921. Close(PipeErr);
  922. Close(PipeIn);
  923. Close(StreamOut);
  924. exit(-1);
  925. end;
  926. if pid = 0 then begin
  927. // *** We are in the child ***
  928. // Close what we don not need
  929. Close(StreamOut);
  930. Close(StreamIn);
  931. Close(StreamErr);
  932. // Connect pipes
  933. if fpdup2(PipeIn, Input)=-1 Then
  934. Halt(127);
  935. Close(PipeIn);
  936. if fpdup2(PipeOut, Output)=-1 Then
  937. Halt(127);
  938. Close(PipeOut);
  939. if fpdup2(PipeErr, StdErr)=-1 Then
  940. Halt(127);
  941. Close(PipeErr);
  942. // Execute program
  943. fpExecl(Prog,args);
  944. Halt(127);
  945. end else begin
  946. // *** We are in the parent ***
  947. Close(PipeErr);
  948. Close(PipeOut);
  949. Close(PipeIn);
  950. // Save the process ID - needed when closing
  951. pl := pcint(@TextRec(StreamIn).userdata[2]);
  952. { avoid alignment error on sparc }
  953. move(pid,pl^,sizeof(pid));
  954. TextRec(StreamIn).closefunc := @PCloseText;
  955. // Save the process ID - needed when closing
  956. pl := pcint(@TextRec(StreamOut).userdata[2]);
  957. { avoid alignment error on sparc }
  958. move(pid,pl^,sizeof(pid));
  959. TextRec(StreamOut).closefunc := @PCloseText;
  960. // Save the process ID - needed when closing
  961. pl := pcint(@TextRec(StreamErr).userdata[2]);
  962. { avoid alignment error on sparc }
  963. move(pid,pl^,sizeof(pid));
  964. TextRec(StreamErr).closefunc := @PCloseText;
  965. AssignStream := pid;
  966. end;
  967. end;
  968. {******************************************************************************
  969. General information calls
  970. ******************************************************************************}
  971. {$if defined(Linux)}
  972. Function GetDomainName:String; { linux only!}
  973. // domainname is a glibc extension.
  974. {
  975. Get machines domain name. Returns empty string if not set.
  976. }
  977. Var
  978. Sysn : utsname;
  979. begin
  980. If fpUname(sysn)<>0 then
  981. getdomainname:=''
  982. else
  983. getdomainname:=strpas(@Sysn.domain[0]);
  984. end;
  985. {$endif}
  986. {$ifdef sunos}
  987. { sunos doesn't support GetDomainName, see also
  988. http://www.sun.com/software/solaris/programs/abi/appcert_faq.xml#q18
  989. }
  990. Function GetDomainName:String;
  991. begin
  992. GetDomainName:='';
  993. end;
  994. {$endif sunos}
  995. {$ifdef android}
  996. { android doesn't seem to implement GetDomainName
  997. }
  998. Function GetDomainName:String;
  999. begin
  1000. GetDomainName:='';
  1001. end;
  1002. {$endif}
  1003. {$if defined(BSD) or defined(aix)}
  1004. function intGetDomainName(Name:PChar; NameLen:Cint):cint;
  1005. {$ifndef FPC_USE_LIBC}
  1006. external name 'FPC_SYSC_GETDOMAINNAME';
  1007. {$else FPC_USE_LIBC}
  1008. cdecl; external clib name 'getdomainname';
  1009. {$endif FPC_USE_LIBC}
  1010. Function GetDomainName:String; { linux only!}
  1011. // domainname is a glibc extension.
  1012. {
  1013. Get machines domain name. Returns empty string if not set.
  1014. }
  1015. begin
  1016. if intGetDomainName(@getdomainname[1],255)=-1 then
  1017. getdomainname:=''
  1018. else
  1019. getdomainname[0]:=chr(strlen(@getdomainname[1]));
  1020. end;
  1021. {$endif}
  1022. Function GetHostName:String;
  1023. {
  1024. Get machines name. Returns empty string if not set.
  1025. }
  1026. Var
  1027. Sysn : utsname;
  1028. begin
  1029. If fpuname(sysn)=-1 then
  1030. gethostname:=''
  1031. else
  1032. gethostname:=strpas(@Sysn.nodename[0]);
  1033. end;
  1034. {******************************************************************************
  1035. Utility calls
  1036. ******************************************************************************}
  1037. Function FSearch(const path:RawByteString;dirlist:RawByteString;CurrentDirStrategy:TFSearchOption):RawByteString;
  1038. {
  1039. Searches for a file 'path' in the list of direcories in 'dirlist'.
  1040. returns an empty string if not found. Wildcards are NOT allowed.
  1041. If dirlist is empty, it is set to '.'
  1042. This function tries to make FSearch use ansistrings, and decrease
  1043. stringhandling overhead at the same time.
  1044. }
  1045. Var
  1046. mypath,
  1047. mydir,NewDir : RawByteString;
  1048. p1 : cint;
  1049. Info : Stat;
  1050. i,j : cint;
  1051. p : pchar;
  1052. Begin
  1053. SetCodePage(dirlist,DefaultFileSystemCodePage);
  1054. if CurrentDirStrategy=CurrentDirectoryFirst Then
  1055. Dirlist:=ToSingleByteFileSystemEncodedFileName('.:')+dirlist {Make sure current dir is first to be searched.}
  1056. else if CurrentDirStrategy=CurrentDirectoryLast Then
  1057. Dirlist:=dirlist+ToSingleByteFileSystemEncodedFileName('.:'); {Make sure current dir is last to be searched.}
  1058. {Replace ':' and ';' with #0}
  1059. for p1:=1 to length(dirlist) do
  1060. if (dirlist[p1]=':') or (dirlist[p1]=';') then
  1061. dirlist[p1]:=#0;
  1062. {Check for WildCards}
  1063. If (Pos('?',Path) <> 0) or (Pos('*',Path) <> 0) Then
  1064. FSearch:='' {No wildcards allowed in these things.}
  1065. Else
  1066. Begin
  1067. mypath:=ToSingleByteFileSystemEncodedFileName(path);
  1068. p:=pchar(dirlist);
  1069. i:=length(dirlist);
  1070. j:=1;
  1071. Repeat
  1072. mydir:=RawByteString(p);
  1073. if (length(mydir)>0) and (mydir[length(mydir)]<>'/') then
  1074. begin
  1075. { concatenate character without influencing code page }
  1076. setlength(mydir,length(mydir)+1);
  1077. mydir[length(mydir)]:='/';
  1078. end;
  1079. NewDir:=mydir+mypath;
  1080. if (FpStat(NewDir,Info)>=0) and
  1081. (not fpS_ISDIR(Info.st_Mode)) then
  1082. Begin
  1083. If Pos('./',NewDir)=1 Then
  1084. Delete(NewDir,1,2);
  1085. {DOS strips off an initial .\}
  1086. End
  1087. Else
  1088. NewDir:='';
  1089. while (j<=i) and (p^<>#0) do begin inc(j); inc(p); end;
  1090. if p^=#0 then inc(p);
  1091. Until (j>=i) or (Length(NewDir) > 0);
  1092. FSearch:=NewDir;
  1093. SetCodePage(FSearch,DefaultRTLFileSystemCodePage);
  1094. End;
  1095. End;
  1096. Function FSearch(const path:RawByteString;dirlist:RawByteString):RawByteString;
  1097. Begin
  1098. FSearch:=FSearch(path,dirlist,CurrentDirectoryFirst);
  1099. End;
  1100. function FSearch(const path: UnicodeString; dirlist: UnicodeString; CurrentDirStrategy: TFSearchOption): UnicodeString;
  1101. begin
  1102. FSearch:=FSearch(ToSingleByteFileSystemEncodedFileName(path),ToSingleByteFileSystemEncodedFileName(dirlist),CurrentDirStrategy);
  1103. end;
  1104. function FSearch(const path: UnicodeString; dirlist: UnicodeString): UnicodeString;
  1105. begin
  1106. FSearch:=FSearch(ToSingleByteFileSystemEncodedFileName(path),ToSingleByteFileSystemEncodedFileName(dirlist),CurrentDirectoryFirst);
  1107. end;
  1108. {$ifdef android}
  1109. {$I unixandroid.inc}
  1110. {$endif android}
  1111. Initialization
  1112. {$IFNDEF DONT_READ_TIMEZONE}
  1113. InitLocalTime;
  1114. {$endif}
  1115. {$ifdef android}
  1116. InitLocalTime;
  1117. {$endif android}
  1118. finalization
  1119. {$IFNDEF DONT_READ_TIMEZONE}
  1120. DoneLocalTime;
  1121. {$endif}
  1122. End.