fpc/rtl/macos/sysutils.pp
florian f03a396757 * target classic MacOS builds again
git-svn-id: trunk@39156 -
2018-05-31 19:31:36 +00:00

845 lines
20 KiB
ObjectPascal

{
This file is part of the Free Pascal run time library.
Copyright (c) 2004-2005 by Olle Raab
Sysutils unit for Mac OS.
NOTE !!! THIS FILE IS UNDER CONSTRUCTION AND DOES NOT WORK CURRENLY.
THUS IT IS NOT BUILT BY THE MAKEFILES
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.
**********************************************************************}
unit sysutils;
interface
{$MODE objfpc}
{$modeswitch out}
{ force ansistrings }
{$H+}
{$modeswitch typehelpers}
{$modeswitch advancedrecords}
{OS has only 1 byte version for ExecuteProcess}
{$define executeprocuni}
uses
MacOSTP;
//{$DEFINE HAS_SLEEP} TODO
//{$DEFINE HAS_OSERROR} TODO
//{$DEFINE HAS_OSCONFIG} TODO
type
//TODO Check pad and size
//TODO unify with Dos.SearchRec
PMacOSFindData = ^TMacOSFindData;
TMacOSFindData = record
{MacOS specific params, private, do not use:}
paramBlock: CInfoPBRec;
searchFSSpec: FSSpec;
searchAttr: Byte; {attribute we are searching for}
exactMatch: Boolean;
end;
{ used OS file system APIs use ansistring }
{$define SYSUTILS_HAS_ANSISTR_FILEUTIL_IMPL}
{ OS has an ansistring/single byte environment variable API }
{$define SYSUTILS_HAS_ANSISTR_ENVVAR_IMPL}
{ Include platform independent interface part }
{$i sysutilh.inc}
implementation
uses
Dos, Sysconst, macutils; // For some included files.
{$DEFINE FPC_FEXPAND_VOLUMES}
{$DEFINE FPC_FEXPAND_NO_DEFAULT_PATHS}
{$DEFINE FPC_FEXPAND_DRIVESEP_IS_ROOT}
{$DEFINE FPC_FEXPAND_NO_DOTS_UPDIR}
{$DEFINE FPC_FEXPAND_NO_CURDIR}
{ Include platform independent implementation part }
{$i sysutils.inc}
{****************************************************************************
File Functions
****************************************************************************}
Function FileOpen (Const FileName : rawbytestring; Mode : Integer) : Longint;
Var LinuxFlags : longint;
SystemFileName: RawByteString;
begin
(* TODO fix
SystemFileName:=ToSingleByteFileSystemEncodedFileName(FileName);
LinuxFlags:=0;
Case (Mode and 3) of
0 : LinuxFlags:=LinuxFlags or Open_RdOnly;
1 : LinuxFlags:=LinuxFlags or Open_WrOnly;
2 : LinuxFlags:=LinuxFlags or Open_RdWr;
end;
FileOpen:=fdOpen (FileName,LinuxFlags);
//!! We need to set locking based on Mode !!
*)
end;
Function FileCreate (Const FileName : RawByteString) : Longint;
begin
(* TODO fix
FileCreate:=fdOpen(FileName,Open_RdWr or Open_Creat or Open_Trunc);
*)
end;
Function FileCreate (Const FileName : RawByteString;Rights : Longint) : Longint;
Var LinuxFlags : longint;
BEGIN
(* TODO fix
LinuxFlags:=0;
Case (Mode and 3) of
0 : LinuxFlags:=LinuxFlags or Open_RdOnly;
1 : LinuxFlags:=LinuxFlags or Open_WrOnly;
2 : LinuxFlags:=LinuxFlags or Open_RdWr;
end;
FileCreate:=fdOpen(FileName,LinuxFlags or Open_Creat or Open_Trunc);
*)
end;
Function FileCreate (Const FileName : RawByteString;ShareMode : Longint; Rights : Longint) : Longint;
Var LinuxFlags : longint;
BEGIN
(* TODO fix
LinuxFlags:=0;
Case (Mode and 3) of
0 : LinuxFlags:=LinuxFlags or Open_RdOnly;
1 : LinuxFlags:=LinuxFlags or Open_WrOnly;
2 : LinuxFlags:=LinuxFlags or Open_RdWr;
end;
FileCreate:=fdOpen(FileName,LinuxFlags or Open_Creat or Open_Trunc);
*)
end;
Function FileRead (Handle : Longint; out Buffer; Count : longint) : Longint;
begin
(* TODO fix
FileRead:=fdRead (Handle,Buffer,Count);
*)
end;
Function FileWrite (Handle : Longint; const Buffer; Count : Longint) : Longint;
begin
(* TODO fix
FileWrite:=fdWrite (Handle,Buffer,Count);
*)
end;
Function FileSeek (Handle,FOffset,Origin : Longint) : Longint;
begin
(* TODO fix
FileSeek:=fdSeek (Handle,FOffset,Origin);
*)
end;
Function FileSeek (Handle : Longint; FOffset: Int64; Origin : Longint) : Int64;
begin
(* TODO fix
{$warning need to add 64bit call }
FileSeek:=fdSeek (Handle,FOffset,Origin);
*)
end;
Procedure FileClose (Handle : Longint);
begin
(* TODO fix
fdclose(Handle);
*)
end;
Function FileTruncate (Handle: THandle; Size: Int64) : boolean;
begin
(* TODO fix
FileTruncate:=fdtruncate(Handle,Size);
*)
end;
Function FileAge (Const FileName : RawByteString): Longint;
(*
Var Info : Stat;
Y,M,D,hh,mm,ss : word;
*)
begin
(* TODO fix
If not fstat (FileName,Info) then
exit(-1)
else
begin
EpochToLocal(info.mtime,y,m,d,hh,mm,ss);
Result:=DateTimeToFileDate(EncodeDate(y,m,d)+EncodeTime(hh,mm,ss,0));
end;
*)
end;
Function FileExists (Const FileName : RawByteString) : Boolean;
(*
Var Info : Stat;
*)
begin
(* TODO fix
FileExists:=fstat(filename,Info);
*)
end;
Function DirectoryExists (Const Directory : RawByteString) : Boolean;
(*
Var Info : Stat;
*)
begin
(* TODO fix
DirectoryExists:=fstat(Directory,Info) and
((info.mode and STAT_IFMT)=STAT_IFDIR);
*)
end;
(*
Function LinuxToWinAttr (FN : Pchar; Const Info : Stat) : Longint;
begin
Result:=faArchive;
If (Info.Mode and STAT_IFDIR)=STAT_IFDIR then
Result:=Result or faDirectory;
If (FN[0]='.') and (not (FN[1] in [#0,'.'])) then
Result:=Result or faHidden;
If (Info.Mode and STAT_IWUSR)=0 Then
Result:=Result or faReadOnly;
If (Info.Mode and
(STAT_IFSOCK or STAT_IFBLK or STAT_IFCHR or STAT_IFIFO))<>0 then
Result:=Result or faSysFile;
end;
{
GlobToSearch takes a glob entry, stats the file.
The glob entry is removed.
If FileAttributes match, the entry is reused
}
Type
TGlobSearchRec = Record
Path : String;
GlobHandle : PGlob;
end;
PGlobSearchRec = ^TGlobSearchRec;
Function GlobToTSearchRec (Var Info : TSearchRec) : Boolean;
Var SInfo : Stat;
p : Pglob;
GlobSearchRec : PGlobSearchrec;
begin
GlobSearchRec:=PGlobSearchrec(Info.FindHandle);
P:=GlobSearchRec^.GlobHandle;
Result:=P<>Nil;
If Result then
begin
GlobSearchRec^.GlobHandle:=P^.Next;
Result:=Fstat(GlobSearchRec^.Path+StrPas(p^.name),SInfo);
If Result then
begin
Info.Attr:=LinuxToWinAttr(p^.name,SInfo);
Result:=(Info.ExcludeAttr and Info.Attr)=0;
If Result Then
With Info do
begin
Attr:=Info.Attr;
If P^.Name<>Nil then
Name:=strpas(p^.name);
Time:=Sinfo.mtime;
Size:=Sinfo.Size;
end;
end;
P^.Next:=Nil;
GlobFree(P);
end;
end;
*)
procedure DoFind (var F: TSearchRec; var retname: RawByteString; firstTime: Boolean);
var
err: OSErr;
s: Str255;
begin
(* TODO fix
with Rslt, findData, paramBlock do
begin
ioVRefNum := searchFSSpec.vRefNum;
if firstTime then
ioFDirIndex := 0;
while true do
begin
s := '';
ioDirID := searchFSSpec.parID;
ioFDirIndex := ioFDirIndex + 1;
ioNamePtr := @s;
err := PBGetCatInfoSync(@paramBlock);
if err <> noErr then
begin
if err = fnfErr then
DosError := 18
else
DosError := MacOSErr2RTEerr(err);
break;
end;
attr := GetFileAttrFromPB(Rslt.paramBlock);
if ((Attr and not(searchAttr)) = 0) then
begin
retname := s;
SetCodePage(retname, DefaultFileSystemCodePage, false);
UpperString(s, true);
if FNMatch(Rslt.searchFSSpec.name, s) then
begin
size := GetFileSizeFromPB(paramBlock);
time := MacTimeToDosPackedTime(ioFlMdDat);
Result := 0;
break;
end;
end;
end;
end;
*)
end;
Function InternalFindFirst (Const Path : RawByteString; Attr : Longint; out Rslt : TAbstractSearchRec; var Name: RawByteString) : Longint;
var
s: Str255;
begin
(* TODO fix
if path = '' then
begin
Result := 3;
Exit;
end;
{We always also search for readonly and archive, regardless of Attr.}
Rslt.searchAttr := (Attr or (archive or readonly));
{ TODO: convert PathArgToFSSpec (and the routines it calls) to rawbytestring }
Result := PathArgToFSSpec(path, Rslt.searchFSSpec);
with Rslt do
if (Result = 0) or (Result = 2) then
begin
{ FIXME: SearchSpec is a shortstring -> ignores encoding }
SearchSpec := path;
NamePos := Length(path) - Length(searchFSSpec.name);
if (Pos('?', searchFSSpec.name) = 0) and (Pos('*', searchFSSpec.name) = 0) then {No wildcards}
begin {If exact match, we don't have to scan the directory}
exactMatch := true;
Result := DoFindOne(searchFSSpec, paramBlock);
if Result = 0 then
begin
Attr := GetFileAttrFromPB(paramBlock);
if ((Attr and not(searchAttr)) = 0) then
begin
name := searchFSSpec.name;
SetCodePage(name, DefaultFileSystemCodePage, false);
size := GetFileSizeFromPB(paramBlock);
time := MacTimeToDosPackedTime(paramBlock.ioFlMdDat);
end
else
Result := 18;
end
else if Result = 2 then
Result := 18;
end
else
begin
exactMatch := false;
s := searchFSSpec.name;
UpperString(s, true);
Rslt.searchFSSpec.name := s;
DoFind(Rslt, name, true);
end;
end;
*)
end;
Function InternalFindNext (var Rslt : TAbstractSearchRec; var Name : RawByteString) : Longint;
begin
(* TODO fix
if F.exactMatch then
Result := 18
else
Result:=DoFind (Rslt, Name, false);
*)
end;
Procedure InternalFindClose (var Handle: THandle; var FindData: TFindData);
(*
Var
GlobSearchRec : PGlobSearchRec;
*)
begin
(* TODO fix
GlobSearchRec:=PGlobSearchRec(Handle);
GlobFree (GlobSearchRec^.GlobHandle);
Dispose(GlobSearchRec);
*)
end;
Function FileGetDate (Handle : Longint) : Longint;
(*
Var Info : Stat;
*)
begin
(* TODO fix
If Not(FStat(Handle,Info)) then
Result:=-1
else
Result:=Info.Mtime;
*)
end;
Function FileSetDate (Handle,Age : Longint) : Longint;
begin
// TODO fix
// Impossible under Linux from FileHandle !!
FileSetDate:=-1;
end;
Function FileGetAttr (Const FileName : RawByteString) : Longint;
(*
Var Info : Stat;
*)
begin
(* TODO fix
If Not FStat (FileName,Info) then
Result:=-1
Else
Result:=LinuxToWinAttr(Pchar(FileName),Info);
*)
end;
Function FileSetAttr (Const Filename : RawByteString; Attr: longint) : Longint;
begin
Result:=-1;
end;
Function DeleteFile (Const FileName : RawByteString) : Boolean;
begin
(* TODO fix
Result:=UnLink (FileName);
*)
end;
Function RenameFile (Const OldName, NewName : RawByteString) : Boolean;
begin
(* TODO fix
RenameFile:=Unix.FRename(OldNAme,NewName);
*)
end;
{****************************************************************************
Disk Functions
****************************************************************************}
{
The Diskfree and Disksize functions need a file on the specified drive, since this
is required for the statfs system call.
These filenames are set in drivestr[0..26], and have been preset to :
0 - '.' (default drive - hence current dir is ok.)
1 - '/fd0/.' (floppy drive 1 - should be adapted to local system )
2 - '/fd1/.' (floppy drive 2 - should be adapted to local system )
3 - '/' (C: equivalent of dos is the root partition)
4..26 (can be set by you're own applications)
! Use AddDisk() to Add new drives !
They both return -1 when a failure occurs.
}
Const
FixDriveStr : array[0..3] of pchar=(
'.',
'/fd0/.',
'/fd1/.',
'/.'
);
var
Drives : byte;
DriveStr : array[4..26] of pchar;
Procedure AddDisk(const path:string);
begin
if not (DriveStr[Drives]=nil) then
FreeMem(DriveStr[Drives],StrLen(DriveStr[Drives])+1);
GetMem(DriveStr[Drives],length(Path)+1);
StrPCopy(DriveStr[Drives],path);
inc(Drives);
if Drives>26 then
Drives:=4;
end;
Function DiskFree(Drive: Byte): int64;
(*
var
fs : tstatfs;
*)
Begin
(* TODO fix
if ((Drive<4) and (not (fixdrivestr[Drive]=nil)) and statfs(StrPas(fixdrivestr[drive]),fs)) or
((not (drivestr[Drive]=nil)) and statfs(StrPas(drivestr[drive]),fs)) then
Diskfree:=int64(fs.bavail)*int64(fs.bsize)
else
Diskfree:=-1;
*)
End;
Function DiskSize(Drive: Byte): int64;
(*
var
fs : tstatfs;
*)
Begin
(* TODO fix
if ((Drive<4) and (not (fixdrivestr[Drive]=nil)) and statfs(StrPas(fixdrivestr[drive]),fs)) or
((not (drivestr[Drive]=nil)) and statfs(StrPas(drivestr[drive]),fs)) then
DiskSize:=int64(fs.blocks)*int64(fs.bsize)
else
DiskSize:=-1;
*)
End;
{****************************************************************************
Locale Functions
****************************************************************************}
Procedure GetLocalTime(var SystemTime: TSystemTime);
begin
(* TODO fix
Unix.GetTime(SystemTime.Hour, SystemTime.Minute, SystemTime.Second);
Unix.GetDate(SystemTime.Year, SystemTime.Month, SystemTime.Day);
SystemTime.MilliSecond := 0;
*)
end ;
Procedure InitAnsi;
Var
i : longint;
begin
{ Fill table entries 0 to 127 }
for i := 0 to 96 do
UpperCaseTable[i] := chr(i);
for i := 97 to 122 do
UpperCaseTable[i] := chr(i - 32);
for i := 123 to 191 do
UpperCaseTable[i] := chr(i);
Move (CPISO88591UCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
for i := 0 to 64 do
LowerCaseTable[i] := chr(i);
for i := 65 to 90 do
LowerCaseTable[i] := chr(i + 32);
for i := 91 to 191 do
LowerCaseTable[i] := chr(i);
Move (CPISO88591LCT,UpperCaseTable[192],SizeOf(CPISO88591UCT));
end;
Procedure InitInternational;
begin
InitInternationalGeneric;
InitAnsi;
end;
function SysErrorMessage(ErrorCode: Integer): String;
begin
(* TODO fix
Result:=StrError(ErrorCode);
*)
end;
{****************************************************************************
OS utility functions
****************************************************************************}
Function GetEnvironmentVariable(Const EnvVar : String) : String;
begin
(* TODO fix
Result:=Unix.Getenv(PChar(EnvVar));
*)
end;
Function GetEnvironmentVariableCount : Integer;
begin
// Result:=FPCCountEnvVar(EnvP);
Result:=0;
end;
Function GetEnvironmentString(Index : Integer) : {$ifdef FPC_RTL_UNICODE}UnicodeString{$else}AnsiString{$endif};
begin
// Result:=FPCGetEnvStrFromP(Envp,Index);
Result:='';
end;
{ Create a DoScript AppleEvent that targets the given application with text as the direct object. }
function CreateDoScriptEvent (applCreator: OSType; scriptText: PChar; var theEvent: AppleEvent): OSErr;
var
err: OSErr;
targetAddress: AEDesc;
s: signedByte;
begin
err := AECreateDesc(FourCharCodeToLongword(typeApplSignature), @applCreator, sizeof(applCreator), targetAddress);
if err = noErr then
begin
err := AECreateAppleEvent(FourCharCodeToLongword('misc'), FourCharCodeToLongword('dosc'),
targetAddress, kAutoGenerateReturnID, kAnyTransactionID, theEvent);
if err = noErr then
{ Add script text as the direct object parameter. }
err := AEPutParamPtr(theEvent, FourCharCodeToLongword('----'),
FourCharCodeToLongword('TEXT'), scriptText, Length(scriptText));
if err <> noErr then
AEDisposeDesc(theEvent);
AEDisposeDesc(targetAddress);
end;
CreateDoScriptEvent := err;
end;
Procedure Fpc_WriteBuffer(var f:Text;const b;len:longint);[external name 'FPC_WRITEBUFFER'];
{declared in text.inc}
procedure WriteAEDescTypeCharToFile(desc: AEDesc; var f: Text);
begin
if desc.descriptorType = FourCharCodeToLongword(typeChar) then
begin
HLock(desc.dataHandle);
Fpc_WriteBuffer(f, PChar(desc.dataHandle^)^, GetHandleSize(desc.dataHandle));
Flush(f);
HUnLock(desc.dataHandle);
end;
end;
function ExecuteToolserverScript(scriptText: PChar; var statusCode: Longint): OSErr;
var
err: OSErr;
err2: OSErr; {Non serious error}
theEvent: AppleEvent;
reply: AppleEvent;
aresult: AEDesc;
applFileSpec: FSSpec;
p: SignedByte;
const
applCreator = 'MPSX'; {Toolserver}
begin
statusCode:= 3; //3 according to MPW.
err:= CreateDoScriptEvent (FourCharCodeToLongword(applCreator), scriptText, theEvent);
if err = noErr then
begin
err := AESend(theEvent, reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeOut, nil, nil);
if err = connectionInvalid then { Toolserver not available }
begin
err := FindApplication(FourCharCodeToLongword(applCreator), applFileSpec);
if err = noErr then
err := LaunchFSSpec(false, applFileSpec);
if err = noErr then
err := AESend(theEvent, reply, kAEWaitReply, kAENormalPriority, kAEDefaultTimeOut, nil, nil);
end;
if err = noErr then
begin
err:= AEGetParamDesc(reply, FourCharCodeToLongword('stat'),
FourCharCodeToLongword(typeLongInteger), aresult);
if err = noErr then
if aresult.descriptorType = FourCharCodeToLongword(typeLongInteger) then
statusCode:= LongintPtr(aresult.dataHandle^)^;
{If there is no output below, we get a non zero error code}
err2:= AEGetParamDesc(reply, FourCharCodeToLongword('----'),
FourCharCodeToLongword(typeChar), aresult);
if err2 = noErr then
WriteAEDescTypeCharToFile(aresult, stdout);
err2:= AEGetParamDesc(reply, FourCharCodeToLongword('diag'),
FourCharCodeToLongword(typeChar), aresult);
if err2 = noErr then
WriteAEDescTypeCharToFile(aresult, stderr);
AEDisposeDesc(reply);
{$IFDEF TARGET_API_MAC_CARBON }
{$ERROR FIXME AEDesc data is not allowed to be directly accessed}
{$ENDIF}
end;
AEDisposeDesc(theEvent);
end;
ExecuteToolserverScript:= err;
end;
function ExecuteProcess (const Path: RawByteString; const ComLine: RawByteString;Flags:TExecuteFlags=[]):
integer;
var
s: AnsiString;
wdpath: RawByteString;
laststatuscode : longint;
E: EOSError;
Begin
{Make ToolServers working directory in sync with our working directory}
PathArgToFullPath(':', wdpath);
wdpath:= 'Directory ' + wdpath;
Result := ExecuteToolserverScript(PChar(wdpath), laststatuscode);
{TODO Only change path when actually needed. But this requires some
change counter to be incremented each time wd is changed. }
s:= path + ' ' + comline;
Result := ExecuteToolserverScript(PChar(s), laststatuscode);
if Result = afpItemNotFound then
Result := 900
else
Result := MacOSErr2RTEerr(Result);
if Result <> 0 then
begin
E := EOSError.CreateFmt (SExecuteProcessFailed, [Comline, DosError]);
E.ErrorCode := DosError;
raise E;
end;
//TODO Better dos error codes
if laststatuscode <> 0 then
begin
{MPW status might be 24 bits}
Result := laststatuscode and $ffff;
if Result = 0 then
Result := 1;
end
else
Result := 0;
End;
function ExecuteProcess (const Path: RawByteString;
const ComLine: array of RawByteString;Flags:TExecuteFlags=[]): integer;
var
CommandLine: RawByteString;
I: integer;
begin
Commandline := '';
for I := 0 to High (ComLine) do
if Pos (' ', ComLine [I]) <> 0 then
CommandLine := CommandLine + ' ' + '"' + ToSingleByteFileSystemEncodedFileName(ComLine [I]) + '"'
else
CommandLine := CommandLine + ' ' + ToSingleByteFileSystemEncodedFileName(Comline [I]);
ExecuteProcess := ExecuteProcess (Path, CommandLine);
end;
procedure Sleep(milliseconds: Cardinal);
begin
end;
(*
Function GetLastOSError : Integer;
begin
end;
*)
{****************************************************************************
Initialization code
****************************************************************************}
Initialization
InitExceptions; { Initialize exceptions. OS independent }
InitInternational; { Initialize internationalization settings }
Finalization
DoneExceptions;
end.