system.pp 10 KB

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