system.pp 11 KB

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