system.pp 11 KB

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