123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 2020 Karoly Balogh, Free Pascal Development team
- Amiga dos.library legacy (OS 1.x/2.x) support functions
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {
- This unit implements some missing functions of OS 1.x (and some OS 2.x)
- dos.library, so the legacy OS support can be implemented with minimal
- changes to the normal system unit and common Amiga-like code
- Please note that this code doesn't aim to be API feature complete, just
- functional enough for the RTL code.
- }
- procedure NextTag(var Tag: PTagItem); inline;
- begin
- if Tag^.ti_Tag = TAG_END then
- Exit;
- Inc(Tag);
- repeat
- case Tag^.ti_Tag of
- TAG_IGNORE: Inc(Tag);
- TAG_SKIP: Inc(Tag, Tag^.ti_Data);
- TAG_MORE: Tag := PTagItem(Tag^.ti_Data);
- else
- Break;
- end;
- until False;
- end;
- {$PACKRECORDS 2}
- type
- TAmigaLegacyFakeSegList = record
- length: DWord;
- next: DWord;
- jump: Word;
- entry: Pointer;
- pad: Word;
- end;
- {$PACKRECORDS DEFAULT}
- var
- __amiga_fake_seglist: TAmigaLegacyFakeSegList;
- __amiga_fake_seglist_lock: TSignalSemaphore;
- __amiga_fake_seglist_lock_inited: boolean = false;
- function CreateNewProc(tags: PTagItem): PProcess; public name '_fpc_amiga_createproc';
- var
- seglistbptr: dword;
- name: pchar;
- entryfunc: pointer;
- stacksize: dword;
- m: pmsgport;
- tag: ptagitem;
- begin
- CreateNewProc:=nil;
- entryfunc:=nil;
- stacksize:=4000;
- name:='New Process';
- tag := Tags;
- if Assigned(tag) then
- begin
- repeat
- case Tag^.ti_Tag of
- NP_Entry: entryfunc := Pointer(Tag^.ti_Data);
- NP_StackSize: stacksize := Tag^.ti_Data;
- end;
- NextTag(Tag);
- until tag^.ti_Tag = TAG_END;
- end;
- if entryfunc = nil then
- exit;
- { This is a gigantic hack, and probably only works, because AThreads will always
- feed the same function pointer in here (i.e. starts the same function multiple
- times, which is a wrapper for FPC threads), and also waits for the subprocess
- to properly start before trying to start a new one, but just in case, lets
- still have proper-ish locking here, in case one spawns a subthread from a
- subthread... (KB) }
- if not __amiga_fake_seglist_lock_inited then
- begin
- InitSemaphore(@__amiga_fake_seglist_lock);
- __amiga_fake_seglist_lock_inited:=true;
- end;
- ObtainSemaphore(@__amiga_fake_seglist_lock);
- with __amiga_fake_seglist do
- begin
- length:=16;
- next:=0;
- jump:=$4ef9; { JMP }
- entry:=entryfunc;
- pad:=$4e71; { NOP }
- end;
- seglistbptr:=ptruint(@__amiga_fake_seglist) shr 2;
- m:=CreateProc(name, 0, seglistbptr, stacksize);
- if m <> nil then
- { CreateProc returns the MsgPort inside the process structure.
- recalculate to the address of the process instead... *yuck* (KB) }
- CreateNewProc:=PProcess(pointer(m)-ptruint(@PProcess(nil)^.pr_MsgPort));
- ReleaseSemaphore(@__amiga_fake_seglist_lock);
- end;
- function NameFromLock(lock : LongInt;
- buffer: PChar;
- len : LongInt): LongBool; public name '_fpc_amiga_namefromlock';
- var
- fib_area: array[1..sizeof(TFileInfoBlock) + sizeof(longint)] of byte;
- fib: pfileinfoblock;
- namelen: longint;
- blen: longint;
- begin
- NameFromLock:=false;
- if len <= 0 then
- exit;
- if (lock = 0) and (len >= 5) then
- begin
- buffer:='SYS:';
- NameFromLock:=true;
- exit;
- end;
- fib:=align(@fib_area[1],sizeof(longint));
- buffer[0]:=#0;
- dec(len); // always preserve one byte for zero term
- blen:=0;
- repeat
- if Examine(lock,fib) <> 0 then
- begin
- namelen:=strlen(@fib^.fib_FileName[0]);
- if (namelen+1) > (len-blen) then
- exit;
- move(buffer[0],buffer[namelen+1],blen);
- move(fib^.fib_FileName[0],buffer[0],namelen);
- lock:=ParentDir(lock);
- if lock = 0 then
- buffer[namelen]:=':'
- else
- buffer[namelen]:='/';
- inc(blen,namelen+1);
- buffer[blen]:=#0;
- end
- else
- exit;
- until lock = 0;
- if buffer[blen-1]='/' then
- buffer[blen-1]:=#0;
- NameFromLock:=true;
- end;
- function NameFromFH(fh : BPTR;
- buffer: PChar;
- len : LongInt): LongBool; public name '_fpc_amiga_namefromfh';
- begin
- {$warning NameFromFH unimplemented!}
- { note that this is only used in sysutils/FileSetDate, but because SetFileDate() (see below)
- is not easily possible on KS1.x, so it might not be needed to implement this at all (KB) }
- NameFromFH:=false;
- end;
- function ExamineFH(fh : BPTR;
- fib: PFileInfoBlock): LongBool; public name '_fpc_amiga_examinefh';
- begin
- {$warning ExamineFH unimplemented!}
- { ExamineFH is only used to determine file size, in sysfile.inc/do_filesize(),
- but this code is already always falling back to double-seek method on KS1.x, and in
- other location is sysutils/FileGetDate(), which deals with this function returning
- false. Note that ExamineFH can fail on newer Amiga systems as well, because the
- underlying FS needs to support ACTION_EXAMINE_FH which some FSes known not to do,
- so the only difference is right now that it always fails on KS1.x... }
- ExamineFH:=false;
- end;
- function LockDosList(flags: Cardinal): PDosList; public name '_fpc_amiga_lockdoslist';
- var
- dosInfo: PDosInfo;
- begin
- dosInfo:=PDosInfo(PRootNode(PDosLibrary(AOS_DOSBase)^.dl_Root)^.rn_Info shl 2);
- { Actually, DOS v36+ also does Forbid(); in its LockDosList for
- compatibility with old programs (KB) }
- Forbid();
- LockDosList:=PDosList(dosInfo^.di_DevInfo shl 2);
- end;
- procedure UnLockDosList(flags: Cardinal); public name '_fpc_amiga_unlockdoslist';
- begin
- { To pair with the Forbid(); in LockDosList, see comment there (KB) }
- Permit();
- end;
- function NextDosEntry(dlist: PDosList;
- flags: Cardinal): PDosList; public name '_fpc_amiga_nextdosentry';
- begin
- while true do
- begin
- dlist:=PDosList(dlist^.dol_Next shl 2);
- if dlist = nil then
- break;
- { Again, this only supports what's really needed for the RTL at the time of writing
- this code, feel free to extend (KB) }
- if (((flags and LDF_VOLUMES) = LDF_VOLUMES) and (dlist^.dol_Type = DLT_VOLUME)) or
- (((flags and LDF_DEVICES) = LDF_DEVICES) and (dlist^.dol_Type = DLT_DEVICE)) then
- break;
- end;
- NextDosEntry:=dlist;
- end;
- // Very first dirty version of MatchFirst/Next/End)
- //TODO: pattern detection, for now only simple "*" or "#?" or full name (without patterns) is supported
- function MatchFirst(pat : PChar;
- anchor: PAnchorPath): LongInt; public name '_fpc_amiga_matchfirst';
- var
- p: PChar;
- len: LongInt;
- Path,FileN: AnsiString;
- LastSeparatorPos: Integer;
- i: Integer;
- DirLock: BPTR;
- ib: TFileInfoBlock;
- Res: LongInt;
- NChain: PAChain;
- begin
- MatchFirst := -1;
- if not Assigned(Anchor) then
- Exit;
- // Search for last '/' or ':' and determine length
- Len := strlen(Pat);
- P := Pat;
- LastSeparatorPos := 0;
- for i := 1 to Len do
- begin
- if (P^ = '/') or (P^ = ':') then
- begin
- LastSeparatorPos := i;
- end;
- Inc(P);
- end;
- // copy Directory name
- SetLength(Path, LastSeparatorPos);
- Move(Pat^, Path[1], LastSeparatorPos);
- // copy filename
- SetLength(FileN, Len - LastSeparatorPos);
- P := Pat;
- Inc(P, LastSeparatorPos);
- Move(P^, FileN[1], Len - LastSeparatorPos);
- // searchpattern lowercase
- FileN := LowerCase(FileN);
- // if no path is given use the current working dir, or try to lock the dir
- if Path = '' then
- DirLock := CurrentDir(0)
- else
- DirLock := Lock(PChar(Path), ACCESS_READ);
- //
- // no dirlock found -> dir not found
- if DirLock = 0 then
- begin
- MatchFirst := -1;
- Exit;
- end;
- // examine the dir to get the fib for ExNext
- if Examine(DirLock, @ib) = 0 then
- begin
- MatchFirst := -1;
- Exit;
- end;
- // we search here directly what we need to find
- // guess it's not meant that way but works
- repeat
- // get next dir entry
- Res := ExNext(DirLock, @ib);
- // nothing nore found -> exit
- if Res = 0 then
- break;
- // include some nifty pattern compare here? later maybe!
- if (FileN = '*') or (FileN = '#?') or (FileN = lowercase(AnsiString(ib.fib_FileName))) then
- begin
- // Match found
- // new chain
- NChain := AllocMem(SizeOf(TAChain));
- if Assigned(Anchor^.ap_First) then
- begin
- // put chain entry to the list
- Anchor^.ap_Last^.an_Child := NChain;
- NChain^.an_Parent := Anchor^.ap_Last;
- Anchor^.ap_Last := NChain;
- end
- else
- begin
- // first chain Entry
- Anchor^.ap_Last := NChain;
- Anchor^.ap_First := NChain;
- NChain^.an_Parent := Pointer(Anchor);
- end;
- // copy the fileinfoblock into the chain
- Move(ib, NChain^.an_Info, SizeOf(TFileInfoBlock));
- end;
- until Res = 0; // useless... we jump out earlier
- //
- // if we found something
- if Assigned(Anchor^.ap_Last) then
- begin
- // set current to the first entry we found
- Anchor^.ap_Last := Anchor^.ap_First;
- // we only copy the file info block, rest is not needed for freepascal stuff
- Move(Anchor^.ap_First^.an_Info, Anchor^.ap_Info, SizeOf(TFileInfoBlock));
- // most importantly set the return code
- MatchFirst := 0;
- end;
- Unlock(DirLock);
- end;
- function MatchNext(anchor: PAnchorPath): LongInt; public name '_fpc_amiga_matchnext';
- begin
- MatchNext := -1;
- if not Assigned(Anchor) then
- Exit;
- // was already last entry?
- if not Assigned(Anchor^.ap_Last) then
- Exit;
- // Get the next Chain Entry
- anchor^.ap_Last := anchor^.ap_Last^.an_Child;
- // check if next one is valid and copy the file infoblock, or just set the error code ;)
- if Assigned(anchor^.ap_Last) then
- begin
- Move(Anchor^.ap_Last^.an_Info, Anchor^.ap_Info, SizeOf(TFileInfoBlock));
- MatchNext := 0;
- end
- else
- MatchNext := ERROR_NO_MORE_ENTRIES;
- end;
- procedure MatchEnd(anchor: PAnchorPath); public name '_fpc_amiga_matchend';
- var
- p, nextp: PAChain;
- begin
- if Assigned(Anchor) then
- begin
- // destroy all the chain entries we created before
- p := Anchor^.ap_First;
- while Assigned(p) do
- begin
- Nextp := p^.an_Child;
- FreeMem(P);
- P := NextP;
- end;
- // reset the contents (is this needed?)
- Anchor^.ap_First := nil;
- Anchor^.ap_Last := nil;
- end;
- end;
- // we emulate that by the old execute command, should be enough for most cases
- function SystemTagList(command: PChar;
- tags : PTagItem): LongInt; public name '_fpc_amiga_systemtaglist';
- var
- I,O: BPTR; // in / ouput handles
- tag: PTagItem;
- begin
- i := 0;
- O := 0;
- tag := Tags;
- if Assigned(tag) then
- begin
- repeat
- case Tag^.ti_Tag of
- SYS_Input: I := Tag^.ti_Data;
- SYS_Output: O := Tag^.ti_Data;
- end;
- NextTag(Tag);
- until tag^.ti_Tag = TAG_END;
- end;
- if Execute(command, I, O) then
- SystemTagList := 0
- else
- SystemTagList := -1;
- end;
- function GetVar(name : PChar;
- buffer: PChar;
- size : LongInt;
- flags : LongInt): LongInt; public name '_fpc_amiga_getvar';
- begin
- {$warning GetVar unimplemented!}
- GetVar:=-1;
- end;
- function SetFileDate(name: PChar;
- date: PDateStamp): LongBool; public name '_fpc_amiga_setfiledate';
- begin
- {$warning SetFileDate unimplemented!}
- { Might not be possible to implement, or implement with a reasonable effort on KS1.x (KS) }
- { Used in: dos/SetFTime, sysutils/FileSetDate }
- SetFileDate:=false;
- end;
- function SetFileSize(fh : LongInt;
- pos : LongInt;
- mode: LongInt): LongInt; public name '_fpc_amiga_setfilesize';
- begin
- {$warning SetFileSize unimplemented!}
- { Might not be possible to implement, or implement with a reasonable effort on KS1.x (KS) }
- { Used in: sysfile.inc/do_truncate, sysutils/FileCreate, sysutils/FileTruncate }
- SetFileSize:=-1;
- end;
- function GetProgramName(buf: PChar;
- len: LongInt): LongBool; public name '_fpc_amiga_getprogramname';
- var
- pr: PProcess;
- pn: PChar;
- pl: longint;
- pcli: PCommandLineInterface;
- begin
- GetProgramName:=false;
- pl:=0;
- if len > 0 then
- begin
- pr:=PProcess(FindTask(nil));
- pcli:=PCommandLineInterface(pr^.pr_CLI shl 2);
- if (pcli <> nil) and (pcli^.cli_CommandName <> 0) then
- begin
- pn:=PChar(pcli^.cli_CommandName shl 2) + 1;
- pl:=Byte(pn[-1]);
- if pl > len-1 then
- pl:=len-1;
- move(pn[0],buf[0],pl);
- GetProgramName:=true;
- end;
- buf[pl]:=#0;
- end;
- end;
- function GetProgramDir: LongInt; public name '_fpc_amiga_getprogramdir';
- var
- cmd: array[0..255] of char;
- prglock: LongInt;
- begin
- { this is quite minimalistic and only covers the simplest cases }
- if GetProgramName(cmd,length(cmd)) then
- begin
- prglock:=Lock(cmd,SHARED_LOCK);
- GetProgramDir:=ParentDir(prglock);
- Unlock(prglock);
- end
- else
- GetProgramDir:=0;
- end;
- var
- __fpc_global_args: pchar; external name '__fpc_args';
- __fpc_global_arglen: dword; external name '__fpc_arglen';
- __fpc_args_buffer: pchar;
- function GetArgStr: PChar; public name '_fpc_amiga_getargstr';
- var
- len: dword;
- begin
- { the string we get from pre-v2.0 OS is not empty
- or zero terminated on start, so we need to copy it
- to an alternate buffer, and zero terminate according
- to the length. This allocation will be freed on exit
- by the memory pool. }
- if __fpc_args_buffer = nil then
- begin
- len:=__fpc_global_arglen-1;
- __fpc_args_buffer:=SysAllocMem(len+1);
- if len > 0 then
- move(__fpc_global_args^,__fpc_args_buffer^,len);
- __fpc_args_buffer[len]:=#0;
- end;
- GetArgStr:=__fpc_args_buffer;
- end;
|