system.pp 11 KB

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