unix.pp 34 KB

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