legacydos.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2020 Karoly Balogh, Free Pascal Development team
  4. Amiga dos.library legacy (OS 1.x/2.x) support functions
  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. {
  12. This unit implements some missing functions of OS 1.x (and some OS 2.x)
  13. dos.library, so the legacy OS support can be implemented with minimal
  14. changes to the normal system unit and common Amiga-like code
  15. Please note that this code doesn't aim to be API feature complete, just
  16. functional enough for the RTL code.
  17. }
  18. procedure NextTag(var Tag: PTagItem); inline;
  19. begin
  20. if Tag^.ti_Tag = TAG_END then
  21. Exit;
  22. Inc(Tag);
  23. repeat
  24. case Tag^.ti_Tag of
  25. TAG_IGNORE: Inc(Tag);
  26. TAG_SKIP: Inc(Tag, Tag^.ti_Data);
  27. TAG_MORE: Tag := PTagItem(Tag^.ti_Data);
  28. else
  29. Break;
  30. end;
  31. until False;
  32. end;
  33. {$PACKRECORDS 2}
  34. type
  35. TAmigaLegacyFakeSegList = record
  36. length: DWord;
  37. next: DWord;
  38. jump: Word;
  39. entry: Pointer;
  40. pad: Word;
  41. end;
  42. {$PACKRECORDS DEFAULT}
  43. var
  44. __amiga_fake_seglist: TAmigaLegacyFakeSegList;
  45. __amiga_fake_seglist_lock: TSignalSemaphore;
  46. __amiga_fake_seglist_lock_inited: boolean = false;
  47. function CreateNewProc(tags: PTagItem): PProcess; public name '_fpc_amiga_createproc';
  48. var
  49. seglistbptr: dword;
  50. name: pchar;
  51. entryfunc: pointer;
  52. stacksize: dword;
  53. m: pmsgport;
  54. tag: ptagitem;
  55. begin
  56. CreateNewProc:=nil;
  57. entryfunc:=nil;
  58. stacksize:=4000;
  59. name:='New Process';
  60. tag := Tags;
  61. if Assigned(tag) then
  62. begin
  63. repeat
  64. case Tag^.ti_Tag of
  65. NP_Entry: entryfunc := Pointer(Tag^.ti_Data);
  66. NP_StackSize: stacksize := Tag^.ti_Data;
  67. end;
  68. NextTag(Tag);
  69. until tag^.ti_Tag = TAG_END;
  70. end;
  71. if entryfunc = nil then
  72. exit;
  73. { This is a gigantic hack, and probably only works, because AThreads will always
  74. feed the same function pointer in here (i.e. starts the same function multiple
  75. times, which is a wrapper for FPC threads), and also waits for the subprocess
  76. to properly start before trying to start a new one, but just in case, lets
  77. still have proper-ish locking here, in case one spawns a subthread from a
  78. subthread... (KB) }
  79. if not __amiga_fake_seglist_lock_inited then
  80. begin
  81. InitSemaphore(@__amiga_fake_seglist_lock);
  82. __amiga_fake_seglist_lock_inited:=true;
  83. end;
  84. ObtainSemaphore(@__amiga_fake_seglist_lock);
  85. with __amiga_fake_seglist do
  86. begin
  87. length:=16;
  88. next:=0;
  89. jump:=$4ef9; { JMP }
  90. entry:=entryfunc;
  91. pad:=$4e71; { NOP }
  92. end;
  93. seglistbptr:=ptruint(@__amiga_fake_seglist) shr 2;
  94. m:=CreateProc(name, 0, seglistbptr, stacksize);
  95. if m <> nil then
  96. { CreateProc returns the MsgPort inside the process structure.
  97. recalculate to the address of the process instead... *yuck* (KB) }
  98. CreateNewProc:=PProcess(pointer(m)-ptruint(@PProcess(nil)^.pr_MsgPort));
  99. ReleaseSemaphore(@__amiga_fake_seglist_lock);
  100. end;
  101. function NameFromLock(lock : LongInt;
  102. buffer: PChar;
  103. len : LongInt): LongBool; public name '_fpc_amiga_namefromlock';
  104. var
  105. fib_area: array[1..sizeof(TFileInfoBlock) + sizeof(longint)] of byte;
  106. fib: pfileinfoblock;
  107. namelen: longint;
  108. blen: longint;
  109. begin
  110. NameFromLock:=false;
  111. if len <= 0 then
  112. exit;
  113. if (lock = 0) and (len >= 5) then
  114. begin
  115. buffer:='SYS:';
  116. NameFromLock:=true;
  117. exit;
  118. end;
  119. fib:=align(@fib_area[1],sizeof(longint));
  120. buffer[0]:=#0;
  121. dec(len); // always preserve one byte for zero term
  122. blen:=0;
  123. repeat
  124. if Examine(lock,fib) <> 0 then
  125. begin
  126. namelen:=strlen(@fib^.fib_FileName[0]);
  127. if (namelen+1) > (len-blen) then
  128. exit;
  129. move(buffer[0],buffer[namelen+1],blen);
  130. move(fib^.fib_FileName[0],buffer[0],namelen);
  131. lock:=ParentDir(lock);
  132. if lock = 0 then
  133. buffer[namelen]:=':'
  134. else
  135. buffer[namelen]:='/';
  136. inc(blen,namelen+1);
  137. buffer[blen]:=#0;
  138. end
  139. else
  140. exit;
  141. until lock = 0;
  142. if buffer[blen-1]='/' then
  143. buffer[blen-1]:=#0;
  144. NameFromLock:=true;
  145. end;
  146. function NameFromFH(fh : BPTR;
  147. buffer: PChar;
  148. len : LongInt): LongBool; public name '_fpc_amiga_namefromfh';
  149. begin
  150. {$warning NameFromFH unimplemented!}
  151. { note that this is only used in sysutils/FileSetDate, but because SetFileDate() (see below)
  152. is not easily possible on KS1.x, so it might not be needed to implement this at all (KB) }
  153. NameFromFH:=false;
  154. end;
  155. function ExamineFH(fh : BPTR;
  156. fib: PFileInfoBlock): LongBool; public name '_fpc_amiga_examinefh';
  157. begin
  158. {$warning ExamineFH unimplemented!}
  159. { ExamineFH is only used to determine file size, in sysfile.inc/do_filesize(),
  160. but this code is already always falling back to double-seek method on KS1.x, and in
  161. other location is sysutils/FileGetDate(), which deals with this function returning
  162. false. Note that ExamineFH can fail on newer Amiga systems as well, because the
  163. underlying FS needs to support ACTION_EXAMINE_FH which some FSes known not to do,
  164. so the only difference is right now that it always fails on KS1.x... }
  165. ExamineFH:=false;
  166. end;
  167. function LockDosList(flags: Cardinal): PDosList; public name '_fpc_amiga_lockdoslist';
  168. var
  169. dosInfo: PDosInfo;
  170. begin
  171. dosInfo:=PDosInfo(PRootNode(PDosLibrary(AOS_DOSBase)^.dl_Root)^.rn_Info shl 2);
  172. { Actually, DOS v36+ also does Forbid(); in its LockDosList for
  173. compatibility with old programs (KB) }
  174. Forbid();
  175. LockDosList:=PDosList(dosInfo^.di_DevInfo shl 2);
  176. end;
  177. procedure UnLockDosList(flags: Cardinal); public name '_fpc_amiga_unlockdoslist';
  178. begin
  179. { To pair with the Forbid(); in LockDosList, see comment there (KB) }
  180. Permit();
  181. end;
  182. function NextDosEntry(dlist: PDosList;
  183. flags: Cardinal): PDosList; public name '_fpc_amiga_nextdosentry';
  184. begin
  185. while true do
  186. begin
  187. dlist:=PDosList(dlist^.dol_Next shl 2);
  188. if dlist = nil then
  189. break;
  190. { Again, this only supports what's really needed for the RTL at the time of writing
  191. this code, feel free to extend (KB) }
  192. if (((flags and LDF_VOLUMES) = LDF_VOLUMES) and (dlist^.dol_Type = DLT_VOLUME)) or
  193. (((flags and LDF_DEVICES) = LDF_DEVICES) and (dlist^.dol_Type = DLT_DEVICE)) then
  194. break;
  195. end;
  196. NextDosEntry:=dlist;
  197. end;
  198. // Very first dirty version of MatchFirst/Next/End)
  199. //TODO: pattern detection, for now only simple "*" or "#?" or full name (without patterns) is supported
  200. function MatchFirst(pat : PChar;
  201. anchor: PAnchorPath): LongInt; public name '_fpc_amiga_matchfirst';
  202. var
  203. p: PChar;
  204. len: LongInt;
  205. Path,FileN: AnsiString;
  206. LastSeparatorPos: Integer;
  207. i: Integer;
  208. DirLock: BPTR;
  209. ib: TFileInfoBlock;
  210. Res: LongInt;
  211. NChain: PAChain;
  212. begin
  213. MatchFirst := -1;
  214. if not Assigned(Anchor) then
  215. Exit;
  216. // Search for last '/' or ':' and determine length
  217. Len := strlen(Pat);
  218. P := Pat;
  219. LastSeparatorPos := 0;
  220. for i := 1 to Len do
  221. begin
  222. if (P^ = '/') or (P^ = ':') then
  223. begin
  224. LastSeparatorPos := i;
  225. end;
  226. Inc(P);
  227. end;
  228. // copy Directory name
  229. SetLength(Path, LastSeparatorPos);
  230. Move(Pat^, Path[1], LastSeparatorPos);
  231. // copy filename
  232. SetLength(FileN, Len - LastSeparatorPos);
  233. P := Pat;
  234. Inc(P, LastSeparatorPos);
  235. Move(P^, FileN[1], Len - LastSeparatorPos);
  236. // searchpattern lowercase
  237. FileN := LowerCase(FileN);
  238. // if no path is given use the current working dir, or try to lock the dir
  239. if Path = '' then
  240. DirLock := CurrentDir(0)
  241. else
  242. DirLock := Lock(PChar(Path), ACCESS_READ);
  243. //
  244. // no dirlock found -> dir not found
  245. if DirLock = 0 then
  246. begin
  247. MatchFirst := -1;
  248. Exit;
  249. end;
  250. // examine the dir to get the fib for ExNext
  251. if Examine(DirLock, @ib) = 0 then
  252. begin
  253. MatchFirst := -1;
  254. Exit;
  255. end;
  256. // we search here directly what we need to find
  257. // guess it's not meant that way but works
  258. repeat
  259. // get next dir entry
  260. Res := ExNext(DirLock, @ib);
  261. // nothing nore found -> exit
  262. if Res = 0 then
  263. break;
  264. // include some nifty pattern compare here? later maybe!
  265. if (FileN = '*') or (FileN = '#?') or (FileN = lowercase(AnsiString(ib.fib_FileName))) then
  266. begin
  267. // Match found
  268. // new chain
  269. NChain := AllocMem(SizeOf(TAChain));
  270. if Assigned(Anchor^.ap_First) then
  271. begin
  272. // put chain entry to the list
  273. Anchor^.ap_Last^.an_Child := NChain;
  274. NChain^.an_Parent := Anchor^.ap_Last;
  275. Anchor^.ap_Last := NChain;
  276. end
  277. else
  278. begin
  279. // first chain Entry
  280. Anchor^.ap_Last := NChain;
  281. Anchor^.ap_First := NChain;
  282. NChain^.an_Parent := Pointer(Anchor);
  283. end;
  284. // copy the fileinfoblock into the chain
  285. Move(ib, NChain^.an_Info, SizeOf(TFileInfoBlock));
  286. end;
  287. until Res = 0; // useless... we jump out earlier
  288. //
  289. // if we found something
  290. if Assigned(Anchor^.ap_Last) then
  291. begin
  292. // set current to the first entry we found
  293. Anchor^.ap_Last := Anchor^.ap_First;
  294. // we only copy the file info block, rest is not needed for freepascal stuff
  295. Move(Anchor^.ap_First^.an_Info, Anchor^.ap_Info, SizeOf(TFileInfoBlock));
  296. // most importantly set the return code
  297. MatchFirst := 0;
  298. end;
  299. Unlock(DirLock);
  300. end;
  301. function MatchNext(anchor: PAnchorPath): LongInt; public name '_fpc_amiga_matchnext';
  302. begin
  303. MatchNext := -1;
  304. if not Assigned(Anchor) then
  305. Exit;
  306. // was already last entry?
  307. if not Assigned(Anchor^.ap_Last) then
  308. Exit;
  309. // Get the next Chain Entry
  310. anchor^.ap_Last := anchor^.ap_Last^.an_Child;
  311. // check if next one is valid and copy the file infoblock, or just set the error code ;)
  312. if Assigned(anchor^.ap_Last) then
  313. begin
  314. Move(Anchor^.ap_Last^.an_Info, Anchor^.ap_Info, SizeOf(TFileInfoBlock));
  315. MatchNext := 0;
  316. end
  317. else
  318. MatchNext := ERROR_NO_MORE_ENTRIES;
  319. end;
  320. procedure MatchEnd(anchor: PAnchorPath); public name '_fpc_amiga_matchend';
  321. var
  322. p, nextp: PAChain;
  323. begin
  324. if Assigned(Anchor) then
  325. begin
  326. // destroy all the chain entries we created before
  327. p := Anchor^.ap_First;
  328. while Assigned(p) do
  329. begin
  330. Nextp := p^.an_Child;
  331. FreeMem(P);
  332. P := NextP;
  333. end;
  334. // reset the contents (is this needed?)
  335. Anchor^.ap_First := nil;
  336. Anchor^.ap_Last := nil;
  337. end;
  338. end;
  339. // we emulate that by the old execute command, should be enough for most cases
  340. function SystemTagList(command: PChar;
  341. tags : PTagItem): LongInt; public name '_fpc_amiga_systemtaglist';
  342. var
  343. I,O: BPTR; // in / ouput handles
  344. tag: PTagItem;
  345. begin
  346. i := 0;
  347. O := 0;
  348. tag := Tags;
  349. if Assigned(tag) then
  350. begin
  351. repeat
  352. case Tag^.ti_Tag of
  353. SYS_Input: I := Tag^.ti_Data;
  354. SYS_Output: O := Tag^.ti_Data;
  355. end;
  356. NextTag(Tag);
  357. until tag^.ti_Tag = TAG_END;
  358. end;
  359. if Execute(command, I, O) then
  360. SystemTagList := 0
  361. else
  362. SystemTagList := -1;
  363. end;
  364. function GetVar(name : PChar;
  365. buffer: PChar;
  366. size : LongInt;
  367. flags : LongInt): LongInt; public name '_fpc_amiga_getvar';
  368. begin
  369. {$warning GetVar unimplemented!}
  370. GetVar:=-1;
  371. end;
  372. function SetFileDate(name: PChar;
  373. date: PDateStamp): LongBool; public name '_fpc_amiga_setfiledate';
  374. begin
  375. {$warning SetFileDate unimplemented!}
  376. { Might not be possible to implement, or implement with a reasonable effort on KS1.x (KS) }
  377. { Used in: dos/SetFTime, sysutils/FileSetDate }
  378. SetFileDate:=false;
  379. end;
  380. function SetFileSize(fh : LongInt;
  381. pos : LongInt;
  382. mode: LongInt): LongInt; public name '_fpc_amiga_setfilesize';
  383. begin
  384. {$warning SetFileSize unimplemented!}
  385. { Might not be possible to implement, or implement with a reasonable effort on KS1.x (KS) }
  386. { Used in: sysfile.inc/do_truncate, sysutils/FileCreate, sysutils/FileTruncate }
  387. SetFileSize:=-1;
  388. end;
  389. function GetProgramName(buf: PChar;
  390. len: LongInt): LongBool; public name '_fpc_amiga_getprogramname';
  391. var
  392. pr: PProcess;
  393. pn: PChar;
  394. pl: longint;
  395. pcli: PCommandLineInterface;
  396. begin
  397. GetProgramName:=false;
  398. pl:=0;
  399. if len > 0 then
  400. begin
  401. pr:=PProcess(FindTask(nil));
  402. pcli:=PCommandLineInterface(pr^.pr_CLI shl 2);
  403. if (pcli <> nil) and (pcli^.cli_CommandName <> 0) then
  404. begin
  405. pn:=PChar(pcli^.cli_CommandName shl 2) + 1;
  406. pl:=Byte(pn[-1]);
  407. if pl > len-1 then
  408. pl:=len-1;
  409. move(pn[0],buf[0],pl);
  410. GetProgramName:=true;
  411. end;
  412. buf[pl]:=#0;
  413. end;
  414. end;
  415. function GetProgramDir: LongInt; public name '_fpc_amiga_getprogramdir';
  416. var
  417. cmd: array[0..255] of char;
  418. prglock: LongInt;
  419. begin
  420. { this is quite minimalistic and only covers the simplest cases }
  421. if GetProgramName(cmd,length(cmd)) then
  422. begin
  423. prglock:=Lock(cmd,SHARED_LOCK);
  424. GetProgramDir:=ParentDir(prglock);
  425. Unlock(prglock);
  426. end
  427. else
  428. GetProgramDir:=0;
  429. end;
  430. var
  431. __fpc_global_args: pchar; external name '__fpc_args';
  432. __fpc_global_arglen: dword; external name '__fpc_arglen';
  433. __fpc_args_buffer: pchar;
  434. function GetArgStr: PChar; public name '_fpc_amiga_getargstr';
  435. var
  436. len: dword;
  437. begin
  438. { the string we get from pre-v2.0 OS is not empty
  439. or zero terminated on start, so we need to copy it
  440. to an alternate buffer, and zero terminate according
  441. to the length. This allocation will be freed on exit
  442. by the memory pool. }
  443. if __fpc_args_buffer = nil then
  444. begin
  445. len:=__fpc_global_arglen-1;
  446. __fpc_args_buffer:=SysAllocMem(len+1);
  447. if len > 0 then
  448. move(__fpc_global_args^,__fpc_args_buffer^,len);
  449. __fpc_args_buffer[len]:=#0;
  450. end;
  451. GetArgStr:=__fpc_args_buffer;
  452. end;