mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-06 16:48:05 +02:00

* rtl/macos/Makefile.fpc: Update dependency list DOS_DEPS: remove unixutil and add missing macostp. * rtl/macos/Makefile: Regenerate.
1002 lines
27 KiB
ObjectPascal
1002 lines
27 KiB
ObjectPascal
{
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2004 by Olle Raab and
|
|
members of the Free Pascal development team
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
{$IFNDEF FPC_DOTTEDUNITS}
|
|
Unit Dos;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
Interface
|
|
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
Uses
|
|
MacOSApi.MacOSTP;
|
|
{$ELSE FPC_DOTTEDUNITS}
|
|
Uses
|
|
macostp;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
|
|
Const
|
|
FileNameLen = 255;
|
|
|
|
Type
|
|
SearchRec = packed record
|
|
Attr: Byte; {attribute of found file}
|
|
Time: LongInt; {last modify date of found file}
|
|
Size: LongInt; {file size of found file}
|
|
Reserved: Word; {future use}
|
|
Name: string[FileNameLen]; {name of foundfile}
|
|
SearchSpec: string[FileNameLen]; {search pattern}
|
|
NamePos: Word; {end of path,start of name position}
|
|
|
|
{MacOS specific params, private, do not use:}
|
|
paramBlock: CInfoPBRec;
|
|
searchFSSpec: FSSpec;
|
|
searchAttr: Byte; {attribute we are searching for}
|
|
exactMatch: Boolean;
|
|
end;
|
|
|
|
{$DEFINE HAS_FILENAMELEN}
|
|
{$I dosh.inc}
|
|
|
|
Implementation
|
|
|
|
{TODO Obtain disk size and disk free values for volumes > 2 GB.
|
|
For this, PBXGetVolInfoSync can be used. However, this function
|
|
is not available on older versions of Mac OS, so the function has
|
|
to be weak linked. An alternative is to directly look into the VCB
|
|
(Volume Control Block), but since this is on low leveel it is a
|
|
compatibility risque.}
|
|
|
|
{TODO Perhaps make SearchRec.paramBlock opaque, so that uses macostp;
|
|
is not needed in the interface part.}
|
|
|
|
{TODO Perhaps add some kind of "Procedure AddDisk" for accessing other
|
|
volumes. At lest accessing the possible disk drives with
|
|
drive number 1 and 2 should be easy.}
|
|
|
|
{TODO Perhaps use LongDateTime for time functions. But the function
|
|
calls must then be weak linked.}
|
|
|
|
{$IFDEF FPC_DOTTEDUNITS}
|
|
Uses
|
|
MacOSApi.MacUtils;
|
|
{$ELSE FPC_DOTTEDUNITS}
|
|
Uses
|
|
macutils;
|
|
{$ENDIF FPC_DOTTEDUNITS}
|
|
|
|
{$UNDEF USE_FEXPAND_INC}
|
|
//{$DEFINE USE_FEXPAND_INC}
|
|
|
|
{$IFNDEF USE_FEXPAND_INC}
|
|
|
|
{$DEFINE HAS_FEXPAND}
|
|
{Own implemetation of fexpand.inc}
|
|
{$I dos.inc}
|
|
|
|
{$ELSE}
|
|
|
|
{$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}
|
|
|
|
{ NOTE: If HAS_FEXPAND is not defined, fexpand.inc is included in dos.inc. }
|
|
{ TODO A lot of issues before this works}
|
|
|
|
{$I dos.inc}
|
|
|
|
{$UNDEF FPC_FEXPAND_VOLUMES}
|
|
{$UNDEF FPC_FEXPAND_NO_DEFAULT_PATHS}
|
|
{$UNDEF FPC_FEXPAND_DRIVESEP_IS_ROOT}
|
|
{$UNDEF FPC_FEXPAND_NO_DOTS_UPDIR}
|
|
{$UNDEF FPC_FEXPAND_NO_CURDIR}
|
|
|
|
{$ENDIF}
|
|
|
|
function MacTimeToDosPackedTime(macfiletime: UInt32): Longint;
|
|
var
|
|
mdt: DateTimeRec; {Mac OS datastructure}
|
|
ddt: Datetime; {Dos OS datastructure}
|
|
dospackedtime: Longint;
|
|
|
|
begin
|
|
SecondsToDate(macfiletime, mdt);
|
|
with ddt do
|
|
begin
|
|
year := mdt.year;
|
|
month := mdt.month;
|
|
day := mdt.day;
|
|
hour := mdt.hour;
|
|
min := mdt.minute;
|
|
sec := mdt.second;
|
|
end;
|
|
Packtime(ddt, dospackedtime);
|
|
MacTimeToDosPackedTime:= dospackedtime;
|
|
end;
|
|
|
|
{******************************************************************************
|
|
--- Info / Date / Time ---
|
|
******************************************************************************}
|
|
|
|
function DosVersion:Word;
|
|
|
|
begin
|
|
DosVersion:=
|
|
(macosSystemVersion and $FF00) or
|
|
((macosSystemVersion and $00F0) shr 4);
|
|
end;
|
|
|
|
procedure GetDate (var year, month, mday, wday: word);
|
|
|
|
var
|
|
d: DateTimeRec;
|
|
|
|
begin
|
|
Macostp.GetTime(d);
|
|
year := d.year;
|
|
month := d.month;
|
|
mday := d.day;
|
|
wday := d.dayOfWeek - 1; {1-based on mac}
|
|
end;
|
|
|
|
procedure GetTime (var hour, minute, second, sec100: word);
|
|
|
|
var
|
|
d: DateTimeRec;
|
|
|
|
begin
|
|
Macostp.GetTime(d);
|
|
hour := d.hour;
|
|
minute := d.minute;
|
|
second := d.second;
|
|
sec100 := 0;
|
|
end;
|
|
|
|
Procedure SetDate(Year, Month, Day: Word);
|
|
|
|
var
|
|
d: DateTimeRec;
|
|
|
|
Begin
|
|
Macostp.GetTime(d);
|
|
d.year := year;
|
|
d.month := month;
|
|
d.day := day;
|
|
Macostp.SetTime(d)
|
|
End;
|
|
|
|
Procedure SetTime(Hour, Minute, Second, Sec100: Word);
|
|
|
|
var
|
|
d: DateTimeRec;
|
|
|
|
Begin
|
|
Macostp.GetTime(d);
|
|
d.hour := hour;
|
|
d.minute := minute;
|
|
d.second := second;
|
|
Macostp.SetTime(d)
|
|
End;
|
|
|
|
{******************************************************************************
|
|
--- Exec ---
|
|
******************************************************************************}
|
|
|
|
{ Create a DoScript AppleEvent that targets the given application with text as the direct object. }
|
|
function CreateDoScriptEvent (applCreator: OSType; scriptText: PAnsiChar; 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, PAnsiChar(desc.dataHandle^)^, GetHandleSize(desc.dataHandle));
|
|
Flush(f);
|
|
HUnLock(desc.dataHandle);
|
|
end;
|
|
end;
|
|
|
|
function ExecuteToolserverScript(scriptText: PAnsiChar; var statusCode: Longint): OSErr;
|
|
|
|
var
|
|
err: OSErr;
|
|
err2: OSErr; {Non serious error}
|
|
theEvent: AppleEvent;
|
|
reply: AppleEvent;
|
|
result: 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), result);
|
|
|
|
if err = noErr then
|
|
if result.descriptorType = FourCharCodeToLongword(typeLongInteger) then
|
|
statusCode:= LongintPtr(result.dataHandle^)^;
|
|
|
|
{If there is no output below, we get a non zero error code}
|
|
|
|
err2:= AEGetParamDesc(reply, FourCharCodeToLongword('----'),
|
|
FourCharCodeToLongword(typeChar), result);
|
|
if err2 = noErr then
|
|
WriteAEDescTypeCharToFile(result, stdout);
|
|
|
|
err2:= AEGetParamDesc(reply, FourCharCodeToLongword('diag'),
|
|
FourCharCodeToLongword(typeChar), result);
|
|
if err2 = noErr then
|
|
WriteAEDescTypeCharToFile(result, 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;
|
|
|
|
Procedure Exec (Const Path: PathStr; Const ComLine: ComStr);
|
|
var
|
|
s: AnsiString;
|
|
err: OSErr;
|
|
wdpath: RawByteString;
|
|
|
|
Begin
|
|
wdpath:='';
|
|
{Make ToolServers working directory in sync with our working directory}
|
|
PathArgToFullPath(':', wdpath);
|
|
wdpath:= 'Directory ''' + wdpath + '''';
|
|
err:= ExecuteToolserverScript(PAnsiChar(wdpath), LastDosExitCode);
|
|
{TODO Only change path when actually needed. But this requires some
|
|
change counter to be incremented each time wd is changed. }
|
|
|
|
s:= path + ' ' + comline;
|
|
|
|
err:= ExecuteToolserverScript(PAnsiChar(s), LastDosExitCode);
|
|
if err = afpItemNotFound then
|
|
DosError := 900
|
|
else
|
|
DosError := MacOSErr2RTEerr(err);
|
|
//TODO Better dos error codes
|
|
End;
|
|
|
|
|
|
{******************************************************************************
|
|
--- Disk ---
|
|
******************************************************************************}
|
|
|
|
{If drive is 0 the free space on the volume of the working directory is returned.
|
|
If drive is 1 or 2, the free space on the first or second floppy disk is returned.
|
|
If drive is 3 the free space on the boot volume is returned.
|
|
If the free space is > 2 GB, then 2 GB is reported.}
|
|
Function DiskFree(drive: Byte): Int64;
|
|
|
|
var
|
|
myHPB: HParamBlockRec;
|
|
myErr: OSErr;
|
|
|
|
begin
|
|
myHPB.ioNamePtr := NIL;
|
|
myHPB.ioVolIndex := 0;
|
|
case drive of
|
|
0: myHPB.ioVRefNum := GetWorkingDirectoryVRefNum;
|
|
1: myHPB.ioVRefNum := 1;
|
|
2: myHPB.ioVRefNum := 2;
|
|
3: myHPB.ioVRefNum := macosBootVolumeVRefNum;
|
|
else
|
|
begin
|
|
Diskfree:= -1;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
myErr := PBHGetVInfoSync(@myHPB);
|
|
|
|
if myErr = noErr then
|
|
Diskfree := myHPB.ioVAlBlkSiz * myHPB.ioVFrBlk
|
|
else
|
|
Diskfree:= -1;
|
|
End;
|
|
|
|
{If drive is 0 the size of the volume of the working directory is returned.
|
|
If drive is 1 or 2, the size of the first or second floppy disk is returned.
|
|
If drive is 3 the size of the boot volume is returned.
|
|
If the actual size is > 2 GB, then 2 GB is reported.}
|
|
Function DiskSize(drive: Byte): Int64;
|
|
|
|
var
|
|
myHPB: HParamBlockRec;
|
|
myErr: OSErr;
|
|
|
|
Begin
|
|
myHPB.ioNamePtr := NIL;
|
|
myHPB.ioVolIndex := 0;
|
|
case drive of
|
|
0: myHPB.ioVRefNum := GetWorkingDirectoryVRefNum;
|
|
1: myHPB.ioVRefNum := 1;
|
|
2: myHPB.ioVRefNum := 2;
|
|
3: myHPB.ioVRefNum := macosBootVolumeVRefNum;
|
|
else
|
|
begin
|
|
DiskSize:= -1;
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
myErr := PBHGetVInfoSync(@myHPB);
|
|
|
|
if myErr = noErr then
|
|
DiskSize := myHPB.ioVAlBlkSiz * myHPB.ioVNmAlBlks
|
|
else
|
|
DiskSize:=-1;
|
|
End;
|
|
|
|
{******************************************************************************
|
|
--- Findfirst FindNext ---
|
|
******************************************************************************}
|
|
|
|
function FNMatch (const Pattern, Name: string): Boolean;
|
|
|
|
var
|
|
LenPat, LenName: longint;
|
|
|
|
function DoFNMatch (i, j: longint): Boolean;
|
|
|
|
var
|
|
Found: boolean;
|
|
|
|
begin
|
|
Found := true;
|
|
while Found and (i <= LenPat) do
|
|
begin
|
|
case Pattern[i] of
|
|
'?':
|
|
Found := (j <= LenName);
|
|
'*':
|
|
begin
|
|
{find the next character in pattern, different of ? and *}
|
|
while Found and (i < LenPat) do
|
|
begin
|
|
i := i + 1;
|
|
case Pattern[i] of
|
|
'*':
|
|
;
|
|
'?':
|
|
begin
|
|
j := j + 1;
|
|
Found := (j <= LenName);
|
|
end;
|
|
otherwise
|
|
Found := false;
|
|
end;
|
|
end;
|
|
{Now, find in name the character which i points to, if the * or ?}
|
|
{wasn 't the last character in the pattern, else, use up all the}
|
|
{chars in name }
|
|
Found := true;
|
|
if (i <= LenPat) then
|
|
begin
|
|
repeat
|
|
{find a letter (not only first !) which maches pattern[i]}
|
|
while (j <= LenName) and (name[j] <> pattern[i]) do
|
|
j := j + 1;
|
|
if (j < LenName) then
|
|
begin
|
|
if DoFnMatch(i + 1, j + 1) then
|
|
begin
|
|
i := LenPat;
|
|
j := LenName;{we can stop}
|
|
Found := true;
|
|
end
|
|
else
|
|
j := j + 1;{We didn't find one, need to look further}
|
|
end;
|
|
until (j >= LenName);
|
|
end
|
|
else
|
|
j := LenName;{we can stop}
|
|
end;
|
|
otherwise {not a wildcard character in pattern}
|
|
Found := (j <= LenName) and (pattern[i] = name[j]);
|
|
end;
|
|
i := i + 1;
|
|
j := j + 1;
|
|
end;
|
|
DoFnMatch := Found and (j > LenName);
|
|
end;
|
|
|
|
begin {start FNMatch}
|
|
LenPat := Length(Pattern);
|
|
LenName := Length(Name);
|
|
FNMatch := DoFNMatch(1, 1);
|
|
end;
|
|
|
|
|
|
function GetFileAttrFromPB (var paramBlock: CInfoPBRec): Word;
|
|
|
|
var
|
|
isLocked, isInvisible, isDirectory, isNameLocked: Boolean;
|
|
attr: Word;
|
|
|
|
{NOTE "nameLocked" was in pre-System 7 called "isSystem".
|
|
It is used for files whose name and icon cannot be changed by the user,
|
|
that is essentially system files. However in System 9 the folder
|
|
"Applications (Mac OS 9)" also has this attribute, and since this is
|
|
not a system file in traditional meaning, we will not use this attribute
|
|
as the "sysfile" attribute.}
|
|
|
|
begin
|
|
with paramBlock do
|
|
begin
|
|
attr := 0;
|
|
|
|
isDirectory := (ioFlAttrib and $10) <> 0;
|
|
if isDirectory then
|
|
attr := (attr or directory);
|
|
|
|
isLocked := (ioFlAttrib and $01) <> 0;
|
|
if isLocked then
|
|
attr := (attr or readonly);
|
|
|
|
if not isDirectory then
|
|
begin
|
|
isInvisible := (ioFlFndrInfo.fdFlags and 16384) <> 0;
|
|
(* isNameLocked := (ioFlFndrInfo.fdFlags and 4096) <> 0; *)
|
|
end
|
|
else
|
|
begin
|
|
isInvisible := (ioDrUsrWds.frFlags and 16384) <> 0;
|
|
(* isNameLocked := (ioDrUsrWds.frFlags and 4096) <> 0; *)
|
|
end;
|
|
|
|
if isInvisible then
|
|
attr := (attr or hidden);
|
|
|
|
(*
|
|
if isNameLocked then
|
|
attr := (attr or sysfile);
|
|
*)
|
|
|
|
GetFileAttrFromPB := attr;
|
|
end;
|
|
end;
|
|
|
|
procedure SetPBFromFileAttr (var paramBlock: CInfoPBRec; attr: Word);
|
|
|
|
begin
|
|
with paramBlock do
|
|
begin
|
|
(*
|
|
{Doesn't seem to work, despite the documentation.}
|
|
{Can instead be set by FSpSetFLock/FSpRstFLock}
|
|
if (attr and readonly) <> 0 then
|
|
ioFlAttrib := (ioFlAttrib or $01)
|
|
else
|
|
ioFlAttrib := (ioFlAttrib and not($01));
|
|
*)
|
|
|
|
if (attr and hidden) <> 0 then
|
|
ioFlFndrInfo.fdFlags := (ioFlFndrInfo.fdFlags or 16384)
|
|
else
|
|
ioFlFndrInfo.fdFlags := (ioFlFndrInfo.fdFlags and not(16384))
|
|
end;
|
|
end;
|
|
|
|
function GetFileSizeFromPB (var paramBlock: CInfoPBRec): Longint;
|
|
|
|
begin
|
|
with paramBlock do
|
|
if ((ioFlAttrib and $10) <> 0) then {if directory}
|
|
GetFileSizeFromPB := 0
|
|
else
|
|
GetFileSizeFromPB := ioFlLgLen + ioFlRLgLen; {Add length of both forks}
|
|
end;
|
|
|
|
function DoFindOne (var spec: FSSpec; var paramBlock: CInfoPBRec): Integer;
|
|
|
|
var
|
|
err: OSErr;
|
|
|
|
begin
|
|
with paramBlock do
|
|
begin
|
|
ioVRefNum := spec.vRefNum;
|
|
ioDirID := spec.parID;
|
|
ioNamePtr := @spec.name;
|
|
ioFDirIndex := 0;
|
|
|
|
err := PBGetCatInfoSync(@paramBlock);
|
|
|
|
DoFindOne := MacOSErr2RTEerr(err);
|
|
end;
|
|
end;
|
|
|
|
{To be used after a call to DoFindOne, with the same spec and paramBlock.}
|
|
{Change those parameters in paramBlock, which is to be changed.}
|
|
function DoSetOne (var spec: FSSpec; var paramBlock: CInfoPBRec): Integer;
|
|
|
|
var
|
|
err: OSErr;
|
|
|
|
begin
|
|
with paramBlock do
|
|
begin
|
|
ioVRefNum := spec.vRefNum;
|
|
ioDirID := spec.parID;
|
|
ioNamePtr := @spec.name;
|
|
|
|
err := PBSetCatInfoSync(@paramBlock);
|
|
|
|
DoSetOne := MacOSErr2RTEerr(err);
|
|
end;
|
|
end;
|
|
|
|
procedure DoFind (var F: SearchRec; firstTime: Boolean);
|
|
|
|
var
|
|
err: OSErr;
|
|
s: Str255;
|
|
|
|
begin
|
|
with F, 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(f.paramBlock);
|
|
if ((Attr and not(searchAttr)) = 0) then
|
|
begin
|
|
name := s;
|
|
UpperString(s, true);
|
|
|
|
if FNMatch(F.searchFSSpec.name, s) then
|
|
begin
|
|
size := GetFileSizeFromPB(paramBlock);
|
|
time := MacTimeToDosPackedTime(ioFlMdDat);
|
|
DosError := 0;
|
|
break;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FindFirst (const path: pathstr; Attr: Word; var F: SearchRec);
|
|
var
|
|
s: Str255;
|
|
|
|
begin
|
|
fillchar(f, sizeof(f), 0);
|
|
|
|
if path = '' then
|
|
begin
|
|
DosError := 3;
|
|
Exit;
|
|
end;
|
|
|
|
{We always also search for readonly and archive, regardless of Attr.}
|
|
F.searchAttr := (Attr or (archive or readonly));
|
|
|
|
DosError := PathArgToFSSpec(path, F.searchFSSpec);
|
|
with F do
|
|
if (DosError = 0) or (DosError = 2) then
|
|
begin
|
|
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;
|
|
DosError := DoFindOne(searchFSSpec, paramBlock);
|
|
if DosError = 0 then
|
|
begin
|
|
Attr := GetFileAttrFromPB(paramBlock);
|
|
if ((Attr and not(searchAttr)) = 0) then
|
|
begin
|
|
name := searchFSSpec.name;
|
|
size := GetFileSizeFromPB(paramBlock);
|
|
time := MacTimeToDosPackedTime(paramBlock.ioFlMdDat);
|
|
end
|
|
else
|
|
DosError := 18;
|
|
end
|
|
else if DosError = 2 then
|
|
DosError := 18;
|
|
end
|
|
else
|
|
begin
|
|
exactMatch := false;
|
|
|
|
s := searchFSSpec.name;
|
|
UpperString(s, true);
|
|
F.searchFSSpec.name := s;
|
|
|
|
DoFind(F, true);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FindNext (var f: searchRec);
|
|
|
|
begin
|
|
if F.exactMatch then
|
|
DosError := 18
|
|
else
|
|
DoFind(F, false);
|
|
end;
|
|
|
|
procedure FindClose (var f: searchRec);
|
|
{Note: Even if this routine is empty, this doesn't mean it will}
|
|
{be empty in the future. Please use it.}
|
|
begin
|
|
end;
|
|
|
|
|
|
|
|
{******************************************************************************
|
|
--- File ---
|
|
******************************************************************************}
|
|
|
|
function FSearch (path: pathstr; dirlist: string): pathstr;
|
|
{Searches for a file 'path' in the working directory and then in the list of }
|
|
{directories in 'dirlist' . Returns a valid (possibly relative) path or an }
|
|
{empty string if not found . Wildcards are NOT allowed }
|
|
{The dirlist can be separated with ; or , but not :}
|
|
|
|
var
|
|
NewDir: string[255];
|
|
p1: Longint;
|
|
spec: FSSpec;
|
|
fpcerr: Integer;
|
|
|
|
begin
|
|
FSearch := '';
|
|
if (Length(path) = 0) then
|
|
Exit;
|
|
|
|
{Check for Wild Cards}
|
|
if (Pos('?', Path) <> 0) or (Pos('*', Path) <> 0) then
|
|
Exit;
|
|
|
|
if pathTranslation then
|
|
path := TranslatePathToMac(path, false);
|
|
|
|
{Search in working directory, or as full path}
|
|
fpcerr := PathArgToFSSpec(path, spec);
|
|
if (fpcerr = 0) and not IsDirectory(spec) then
|
|
begin
|
|
FSearch := path;
|
|
Exit;
|
|
end
|
|
else if not IsMacFullPath(path) then {If full path, we do not need to continue.}
|
|
begin
|
|
{Replace ';' with native mac PathSeparator (',').}
|
|
{Note: we cannot support unix style ':', because it is used as dir separator in MacOS}
|
|
for p1 := 1 to length(dirlist) do
|
|
if dirlist[p1] = ';' then
|
|
dirlist[p1] := PathSeparator;
|
|
|
|
repeat
|
|
p1 := Pos(PathSeparator, DirList);
|
|
if p1 = 0 then
|
|
p1 := 255;
|
|
|
|
if pathTranslation then
|
|
NewDir := TranslatePathToMac(Copy(DirList, 1, P1 - 1), false)
|
|
else
|
|
NewDir := Copy(DirList, 1, P1 - 1);
|
|
|
|
NewDir := ConcatMacPath(NewDir, Path);
|
|
|
|
Delete(DirList, 1, p1);
|
|
|
|
fpcerr := PathArgToFSSpec(NewDir, spec);
|
|
if fpcerr = 0 then
|
|
begin
|
|
if IsDirectory(spec) then
|
|
NewDir := '';
|
|
end
|
|
else
|
|
NewDir := '';
|
|
until (DirList = '') or (Length(NewDir) > 0);
|
|
FSearch := NewDir;
|
|
end;
|
|
end;
|
|
|
|
{$IFNDEF USE_FEXPAND_INC}
|
|
|
|
{ TODO nonexisting dirs in path's doesnt work (nonexisting files do work)
|
|
example: Writeln('FExpand on :nisse:kalle : ', FExpand(':nisse:kalle')); }
|
|
|
|
function FExpand (const path: pathstr): pathstr;
|
|
var
|
|
fullpath: RawByteString;
|
|
begin
|
|
fullpath:='';
|
|
DosError:= PathArgToFullPath(path, fullpath);
|
|
FExpand:= fullpath;
|
|
end;
|
|
|
|
{$ENDIF USE_FEXPAND_INC}
|
|
|
|
|
|
procedure GetFTime (var f ; var time: longint);
|
|
|
|
var
|
|
spec: FSSpec;
|
|
paramBlock: CInfoPBRec;
|
|
|
|
begin
|
|
{$ifdef FPC_ANSI_TEXTFILEREC}
|
|
DosError := PathArgToFSSpec(filerec(f).name, spec);
|
|
{$else}
|
|
DosError := PathArgToFSSpec(ToSingleByteFileSystemEncodedFileName(filerec(f).name), spec);
|
|
{$endif}
|
|
if (DosError = 0) or (DosError = 2) then
|
|
begin
|
|
DosError := DoFindOne(spec, paramBlock);
|
|
if DosError = 0 then
|
|
time := MacTimeToDosPackedTime(paramBlock.ioFlMdDat);
|
|
end;
|
|
end;
|
|
|
|
procedure SetFTime (var f ; time: longint);
|
|
|
|
var
|
|
spec: FSSpec;
|
|
paramBlock: CInfoPBRec;
|
|
d: DateTimeRec; {Mac OS datastructure}
|
|
t: datetime;
|
|
macfiletime: UInt32;
|
|
|
|
begin
|
|
{$ifdef FPC_ANSI_TEXTFILEREC}
|
|
DosError := PathArgToFSSpec(filerec(f).name, spec);
|
|
{$else}
|
|
DosError := PathArgToFSSpec(ToSingleByteFileSystemEncodedFileName(filerec(f).name), spec);
|
|
{$endif}
|
|
if (DosError = 0) or (DosError = 2) then
|
|
begin
|
|
DosError := DoFindOne(spec, paramBlock);
|
|
if DosError = 0 then
|
|
begin
|
|
Unpacktime(time, t);
|
|
with t do
|
|
begin
|
|
d.year := year;
|
|
d.month := month;
|
|
d.day := day;
|
|
d.hour := hour;
|
|
d.minute := min;
|
|
d.second := sec;
|
|
end;
|
|
DateToSeconds(d, macfiletime);
|
|
paramBlock.ioFlMdDat := macfiletime;
|
|
DosError := DoSetOne(spec, paramBlock);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure GetFAttr (var f ; var attr: word);
|
|
|
|
var
|
|
spec: FSSpec;
|
|
paramBlock: CInfoPBRec;
|
|
|
|
begin
|
|
DosError := PathArgToFSSpec(StrPas(filerec(f).name), spec);
|
|
if (DosError = 0) or (DosError = 2) then
|
|
begin
|
|
DosError := DoFindOne(spec, paramBlock);
|
|
if DosError = 0 then
|
|
attr := GetFileAttrFromPB(paramBlock);
|
|
end;
|
|
end;
|
|
|
|
procedure SetFAttr (var f ; attr: word);
|
|
|
|
var
|
|
spec: FSSpec;
|
|
paramBlock: CInfoPBRec;
|
|
|
|
begin
|
|
if (attr and VolumeID) <> 0 then
|
|
begin
|
|
Doserror := 5;
|
|
Exit;
|
|
end;
|
|
|
|
DosError := PathArgToFSSpec(StrPas(filerec(f).name), spec);
|
|
if (DosError = 0) or (DosError = 2) then
|
|
begin
|
|
DosError := DoFindOne(spec, paramBlock);
|
|
if DosError = 0 then
|
|
begin
|
|
SetPBFromFileAttr(paramBlock, attr);
|
|
DosError := DoSetOne(spec, paramBlock);
|
|
|
|
if (paramBlock.ioFlAttrib and $10) = 0 then {check not directory}
|
|
if DosError = 0 then
|
|
if (attr and readonly) <> 0 then
|
|
DosError := MacOSErr2RTEerr(FSpSetFLock(spec))
|
|
else
|
|
DosError := MacOSErr2RTEerr(FSpRstFLock(spec));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{******************************************************************************
|
|
--- Environment ---
|
|
******************************************************************************}
|
|
|
|
Function EnvCount: Longint;
|
|
var
|
|
envcnt : longint;
|
|
p : PPAnsiChar;
|
|
Begin
|
|
envcnt:=0;
|
|
p:=envp; {defined in system}
|
|
while (p^<>nil) do
|
|
begin
|
|
inc(envcnt);
|
|
inc(p);
|
|
end;
|
|
EnvCount := envcnt
|
|
End;
|
|
|
|
|
|
Function EnvStr (Index: longint): String;
|
|
|
|
Var
|
|
i : longint;
|
|
p : PPAnsiChar;
|
|
Begin
|
|
if Index <= 0 then
|
|
envstr:=''
|
|
else
|
|
begin
|
|
p:=envp; {defined in system}
|
|
i:=1;
|
|
while (i<Index) and (p^<>nil) do
|
|
begin
|
|
inc(i);
|
|
inc(p);
|
|
end;
|
|
if p=nil then
|
|
envstr:=''
|
|
else
|
|
envstr:=strpas(p^) + '=' + strpas(p^+strlen(p^)+1);
|
|
end;
|
|
end;
|
|
|
|
|
|
function c_getenv(varname: PAnsiChar): PAnsiChar; {TODO perhaps move to a separate inc file.}
|
|
external 'StdCLib' name 'getenv';
|
|
|
|
Function GetEnv(EnvVar: String): String;
|
|
var
|
|
p: PAnsiChar;
|
|
name: String;
|
|
Begin
|
|
name:= EnvVar+#0;
|
|
p:= c_getenv(@name[1]);
|
|
if p=nil then
|
|
GetEnv:=''
|
|
else
|
|
GetEnv:=StrPas(p);
|
|
End;
|
|
|
|
{
|
|
Procedure GetCBreak(Var BreakValue: Boolean);
|
|
Begin
|
|
-- Might be implemented in future on MacOS to handle Cmd-. (period) key press
|
|
End;
|
|
|
|
Procedure SetCBreak(BreakValue: Boolean);
|
|
Begin
|
|
-- Might be implemented in future on MacOS to handle Cmd-. (period) key press
|
|
End;
|
|
|
|
Procedure GetVerify(Var Verify: Boolean);
|
|
Begin
|
|
-- Might be implemented in future on MacOS
|
|
End;
|
|
|
|
Procedure SetVerify(Verify: Boolean);
|
|
Begin
|
|
-- Might be implemented in future on MacOS
|
|
End;
|
|
}
|
|
|
|
|
|
{******************************************************************************
|
|
--- Initialization ---
|
|
******************************************************************************}
|
|
|
|
End.
|