system.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2020,2021 by the Free Pascal development team.
  4. System unit for The WebAssembly System Interface (WASI).
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. unit system;
  12. interface
  13. {$define FPC_IS_SYSTEM}
  14. {$define USE_NOTHREADMANAGER}
  15. {$I systemh.inc}
  16. const
  17. LineEnding = #10;
  18. LFNSupport = true;
  19. DirectorySeparator = '/';
  20. DriveSeparator = '';
  21. ExtensionSeparator = '.';
  22. PathSeparator = ':';
  23. AllowDirectorySeparators : set of char = ['\','/'];
  24. AllowDriveSeparators : set of char = [];
  25. { FileNameCaseSensitive and FileNameCasePreserving are defined below! }
  26. maxExitCode = 65535;
  27. MaxPathLen = 4096;
  28. AllFilesMask = '*';
  29. const
  30. UnusedHandle = -1;
  31. StdInputHandle = 0;
  32. StdOutputHandle = 1;
  33. StdErrorHandle = 2;
  34. FileNameCaseSensitive : boolean = true;
  35. FileNameCasePreserving: boolean = true;
  36. CtrlZMarksEOF: boolean = false; (* #26 not considered as end of file *)
  37. sLineBreak = LineEnding;
  38. DefaultTextLineBreakStyle : TTextLineBreakStyle = tlbsLF;
  39. var
  40. argc: longint;
  41. argv: PPChar;
  42. envp: PPChar;
  43. implementation
  44. {$I wasitypes.inc}
  45. {$I wasiprocs.inc}
  46. function WasiAlloc (aLength : Longint) : Pointer; [public, alias: 'wasiAlloc'];
  47. begin
  48. Result:=GetMem(aLength);
  49. end;
  50. procedure WasiFree (aMem : pointer); [public, alias: 'wasiFree'];
  51. begin
  52. FreeMem(aMem);
  53. end;
  54. exports
  55. WasiAlloc,WasiFree;
  56. function ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word; forward;
  57. function fpc_wasi_path_readlink_ansistring(
  58. fd: __wasi_fd_t;
  59. const path: PChar;
  60. path_len: size_t;
  61. out link: rawbytestring): __wasi_errno_t;[Public, Alias : 'FPC_WASI_PATH_READLINK_ANSISTRING'];
  62. const
  63. InitialBufLen=64;
  64. MaximumBufLen=16384;
  65. var
  66. CurrentBufLen: __wasi_size_t;
  67. BufUsed: __wasi_size_t;
  68. begin
  69. CurrentBufLen:=InitialBufLen div 2;
  70. repeat
  71. CurrentBufLen:=CurrentBufLen*2;
  72. SetLength(link,CurrentBufLen);
  73. result:=__wasi_path_readlink(fd,path,path_len,@(link[1]),CurrentBufLen,@BufUsed);
  74. until (result<>__WASI_ERRNO_SUCCESS) or ((result=__WASI_ERRNO_SUCCESS) and (BufUsed<CurrentBufLen)) or (CurrentBufLen>MaximumBufLen);
  75. if result=__WASI_ERRNO_SUCCESS then
  76. begin
  77. SetLength(link,BufUsed);
  78. setcodepage(link,DefaultRTLFileSystemCodePage,true);
  79. end
  80. else
  81. link:='';
  82. end;
  83. function HasDriveLetter(const path: rawbytestring): Boolean;
  84. begin
  85. HasDriveLetter:=(Length(path)>=2) and (UpCase(path[1]) in ['A'..'Z']) and (path[2] = ':');
  86. end;
  87. type
  88. PPreopenedDir = ^TPreopenedDir;
  89. TPreopenedDir = record
  90. dir_name: RawByteString;
  91. drive_str: RawByteString;
  92. fd: longint;
  93. end;
  94. PCurrentDir = ^TCurrentDir;
  95. TCurrentDir = record
  96. dir_name: RawByteString;
  97. drive_str: RawByteString;
  98. end;
  99. var
  100. preopened_dirs_count: longint;
  101. preopened_dirs: PPreopenedDir;
  102. drives_count: longint;
  103. current_dirs: PCurrentDir;
  104. current_drive: longint;
  105. {$I system.inc}
  106. var
  107. argv_size,
  108. argv_buf_size: __wasi_size_t;
  109. argv_buf: Pointer;
  110. environc,environ_buf_size,envp_size: __wasi_size_t;
  111. environ_buf: Pointer;
  112. function GetProcessID: SizeUInt;
  113. begin
  114. end;
  115. Procedure Randomize;
  116. Begin
  117. __wasi_random_get(@RandSeed,SizeOf(RandSeed));
  118. End;
  119. procedure System_exit;
  120. begin
  121. if ExitCode>=126 then
  122. begin
  123. writeln(stderr,'##WASI-EXITCODE: ',ExitCode,' -> 125##');
  124. ExitCode:=125;
  125. end;
  126. __wasi_proc_exit(ExitCode);
  127. End;
  128. function Do_ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word;
  129. var
  130. drive_nr,I,pdir_drive,longest_match,chridx: longint;
  131. IsAbsolutePath: Boolean;
  132. pdir: RawByteString;
  133. begin
  134. fd:=0;
  135. relfd_path:='';
  136. if HasDriveLetter(path) then
  137. begin
  138. drive_nr:=Ord(UpCase(path[1]))-(Ord('A')-1);
  139. delete(path,1,2);
  140. end
  141. else
  142. drive_nr:=current_drive;
  143. { path is relative to a current directory? }
  144. if (path='') or not (path[1] in AllowDirectorySeparators) then
  145. begin
  146. { if so, convert to absolute }
  147. if (drive_nr>=drives_count) or (current_dirs[drive_nr].dir_name='') then
  148. begin
  149. Do_ConvertToFdRelativePath:=15;
  150. exit;
  151. end;
  152. if current_dirs[drive_nr].dir_name[Length(current_dirs[drive_nr].dir_name)] in AllowDirectorySeparators then
  153. path:=current_dirs[drive_nr].dir_name+path
  154. else
  155. path:=current_dirs[drive_nr].dir_name+DirectorySeparator+path;
  156. end;
  157. { path is now absolute. Try to find it in the preopened dirs array }
  158. longest_match:=0;
  159. for I:=0 to preopened_dirs_count-1 do
  160. begin
  161. pdir:=preopened_dirs[I].dir_name;
  162. if preopened_dirs[I].drive_str<>'' then
  163. pdir_drive:=Ord(UpCase(preopened_dirs[I].drive_str[1]))-(Ord('A')-1)
  164. else
  165. pdir_drive:=0;
  166. if pdir_drive<>drive_nr then
  167. continue;
  168. if Length(pdir)>Length(path) then
  169. continue;
  170. if Copy(path,1,Length(pdir))<>pdir then
  171. continue;
  172. chridx:=Length(pdir)+1;
  173. if ((Length(pdir)<>1) or ((Length(pdir)=1) and not (pdir[1] in AllowDirectorySeparators))) and
  174. ((chridx>Length(path)) or not (path[chridx] in AllowDirectorySeparators)) then
  175. continue;
  176. if Length(pdir)>longest_match then
  177. begin
  178. longest_match:=Length(pdir);
  179. while (chridx<=Length(path)) and (path[chridx] in AllowDirectorySeparators) do
  180. Inc(chridx);
  181. fd:=preopened_dirs[I].fd;
  182. relfd_path:=Copy(path,chridx,Length(path)-chridx+1);
  183. end;
  184. end;
  185. if longest_match>0 then
  186. Do_ConvertToFdRelativePath:=0
  187. else
  188. Do_ConvertToFdRelativePath:=3;
  189. end;
  190. function ConvertToFdRelativePath(path: RawByteString; out fd: LongInt; out relfd_path: RawByteString): Word;[Public, Alias : 'FPC_WASI_CONVERTTOFDRELATIVEPATH'];
  191. begin
  192. ConvertToFdRelativePath:=Do_ConvertToFdRelativePath(ToSingleByteFileSystemEncodedFileName(path),fd,relfd_path);
  193. setcodepage(relfd_path,DefaultRTLFileSystemCodePage,true);
  194. end;
  195. procedure Setup_PreopenedDirs;
  196. var
  197. fd: __wasi_fd_t;
  198. prestat: __wasi_prestat_t;
  199. res: __wasi_errno_t;
  200. prestat_dir_name: RawByteString;
  201. drive_nr: longint;
  202. begin
  203. preopened_dirs_count:=0;
  204. preopened_dirs:=nil;
  205. drives_count:=0;
  206. current_dirs:=nil;
  207. current_drive:=0;
  208. fd:=3;
  209. repeat
  210. res:=__wasi_fd_prestat_get(fd, @prestat);
  211. if res=__WASI_ERRNO_SUCCESS then
  212. begin
  213. if (prestat.tag=__WASI_PREOPENTYPE_DIR) and (prestat.u.dir.pr_name_len>0) then
  214. begin
  215. SetLength(prestat_dir_name,prestat.u.dir.pr_name_len);
  216. if __wasi_fd_prestat_dir_name(fd,PByte(prestat_dir_name),prestat.u.dir.pr_name_len)=__WASI_ERRNO_SUCCESS then
  217. begin
  218. setcodepage(prestat_dir_name,DefaultRTLFileSystemCodePage,true);
  219. Inc(preopened_dirs_count);
  220. if preopened_dirs=nil then
  221. preopened_dirs:=AllocMem(preopened_dirs_count*SizeOf(TPreopenedDir))
  222. else
  223. ReAllocMem(preopened_dirs, preopened_dirs_count*SizeOf(TPreopenedDir));
  224. preopened_dirs[preopened_dirs_count-1].fd:=fd;
  225. if HasDriveLetter(prestat_dir_name) then
  226. begin
  227. drive_nr:=Ord(UpCase(prestat_dir_name[1]))-(Ord('A')-1);
  228. preopened_dirs[preopened_dirs_count-1].drive_str:=Copy(prestat_dir_name,1,2);
  229. preopened_dirs[preopened_dirs_count-1].dir_name:=Copy(prestat_dir_name,2,Length(prestat_dir_name)-2);
  230. end
  231. else
  232. begin
  233. drive_nr:=0;
  234. preopened_dirs[preopened_dirs_count-1].drive_str:='';
  235. preopened_dirs[preopened_dirs_count-1].dir_name:=prestat_dir_name;
  236. end;
  237. if (drive_nr+1)>drives_count then
  238. begin
  239. drives_count:=drive_nr+1;
  240. if current_dirs=nil then
  241. current_dirs:=AllocMem(drives_count*SizeOf(TCurrentDir))
  242. else
  243. ReAllocMem(current_dirs,drives_count*SizeOf(TCurrentDir));
  244. end;
  245. if current_dirs[drive_nr].dir_name='' then
  246. current_dirs[drive_nr].dir_name:=prestat_dir_name;
  247. end;
  248. end;
  249. end;
  250. Inc(fd);
  251. until res<>__WASI_ERRNO_SUCCESS;
  252. while (current_drive<(drives_count-1)) and (current_dirs[current_drive].dir_name='') do
  253. Inc(current_drive);
  254. for drive_nr:=0 to drives_count-1 do
  255. begin
  256. if drive_nr>0 then
  257. current_dirs[drive_nr].drive_str:=Chr(Ord('A')+drive_nr-1)+':';
  258. if current_dirs[drive_nr].dir_name='' then
  259. current_dirs[drive_nr].dir_name:=DirectorySeparator;
  260. end;
  261. end;
  262. procedure Setup_Environment;
  263. begin
  264. if envp<>nil then
  265. exit;
  266. if __wasi_environ_sizes_get(@environc, @environ_buf_size)<>__WASI_ERRNO_SUCCESS then
  267. begin
  268. envp:=nil;
  269. exit;
  270. end;
  271. envp_size:=(environc+1)*SizeOf(PChar);
  272. GetMem(envp, envp_size);
  273. GetMem(environ_buf, environ_buf_size);
  274. envp[environc]:=nil;
  275. if __wasi_environ_get(Pointer(envp), environ_buf)<>__WASI_ERRNO_SUCCESS then
  276. begin
  277. FreeMem(envp, envp_size);
  278. FreeMem(environ_buf, environ_buf_size);
  279. envp:=nil;
  280. end;
  281. end;
  282. procedure setup_arguments;
  283. begin
  284. if argv<>nil then
  285. exit;
  286. if __wasi_args_sizes_get(@argc, @argv_buf_size)<>__WASI_ERRNO_SUCCESS then
  287. begin
  288. argc:=0;
  289. argv:=nil;
  290. exit;
  291. end;
  292. argv_size:=(argc+1)*SizeOf(PChar);
  293. GetMem(argv, argv_size);
  294. GetMem(argv_buf, argv_buf_size);
  295. if __wasi_args_get(Pointer(argv), argv_buf)<>__WASI_ERRNO_SUCCESS then
  296. begin
  297. FreeMem(argv, argv_size);
  298. FreeMem(argv_buf, argv_buf_size);
  299. argc:=0;
  300. argv:=nil;
  301. end;
  302. end;
  303. Function ParamCount: Longint;
  304. Begin
  305. if argv=nil then
  306. setup_arguments;
  307. paramcount := argc - 1;
  308. End;
  309. function paramstr(l: longint) : string;
  310. begin
  311. if argv=nil then
  312. setup_arguments;
  313. if (l>=0) and (l+1<=argc) then
  314. paramstr:=strpas(argv[l])
  315. else
  316. paramstr:='';
  317. end;
  318. procedure SysInitStdIO;
  319. begin
  320. OpenStdIO(Input,fmInput,StdInputHandle);
  321. OpenStdIO(Output,fmOutput,StdOutputHandle);
  322. OpenStdIO(ErrOutput,fmOutput,StdErrorHandle);
  323. OpenStdIO(StdOut,fmOutput,StdOutputHandle);
  324. OpenStdIO(StdErr,fmOutput,StdErrorHandle);
  325. end;
  326. function CheckInitialStkLen(stklen : SizeUInt) : SizeUInt;
  327. begin
  328. end;
  329. begin
  330. { To be set if this is a GUI or console application }
  331. IsConsole := TRUE;
  332. {$ifdef FPC_HAS_FEATURE_DYNLIBS}
  333. { If dynlibs feature is disabled,
  334. IsLibrary is a constant, which can thus not be set to a value }
  335. { To be set if this is a library and not a program }
  336. IsLibrary := FALSE;
  337. {$endif def FPC_HAS_FEATURE_DYNLIBS}
  338. { Setup heap }
  339. InitHeap;
  340. SysInitExceptions;
  341. initunicodestringmanager;
  342. { Setup stdin, stdout and stderr }
  343. SysInitStdIO;
  344. { Reset IO Error }
  345. InOutRes:=0;
  346. {$ifdef FPC_HAS_FEATURE_THREADING}
  347. InitSystemThreads;
  348. {$endif}
  349. Setup_Environment;
  350. Setup_PreopenedDirs;
  351. end.