system.pp 11 KB

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