mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-01 05:13:43 +02:00
460 lines
14 KiB
PHP
460 lines
14 KiB
PHP
{
|
|
$Id$
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2004 by Olle Raab
|
|
|
|
Some utilities specific for Mac OS
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
|
|
{NOTE: This file requires the following global variables to be declared:
|
|
workingDirectorySpec: FSSpec;}
|
|
|
|
function FourCharCodeToLongword(fourcharcode: Shortstring): Longword;
|
|
|
|
begin
|
|
FourCharCodeToLongword:=
|
|
(ord(fourcharcode[1]) shl 24) or
|
|
(ord(fourcharcode[2]) shl 16) or
|
|
(ord(fourcharcode[3]) shl 8) or
|
|
(ord(fourcharcode[4]))
|
|
end;
|
|
|
|
function BitIsSet(arg: Longint; bitnr: Integer): Boolean;
|
|
|
|
begin
|
|
BitIsSet:= (arg and (1 shl bitnr)) <> 0;
|
|
end;
|
|
|
|
{ Converts MacOS specific error codes to the correct FPC error code.
|
|
All non zero MacOS errors corresponds to a nonzero FPC error.}
|
|
Function MacOSErr2RTEerr(err: OSErr): Integer;
|
|
|
|
var
|
|
res: Integer;
|
|
|
|
begin
|
|
if err = noErr then { Else it will go through all the cases }
|
|
res:= 0
|
|
else case err of
|
|
dirFulErr, { Directory full }
|
|
dskFulErr { disk full }
|
|
:res:=101;
|
|
nsvErr { no such volume }
|
|
:res:=3;
|
|
ioErr, { I/O error (bummers) }
|
|
bdNamErr { there may be no bad names in the final system! }
|
|
:res:=1; //TODO Exchange to something better
|
|
fnOpnErr { File not open }
|
|
:res:=103;
|
|
eofErr, { End of file }
|
|
posErr { tried to position to before start of file (r/w) }
|
|
:res:=100;
|
|
mFulErr { memory full (open) or file won't fit (load) }
|
|
:res:=1; //TODO Exchange to something better
|
|
tmfoErr { too many files open}
|
|
:res:=4;
|
|
fnfErr { File not found }
|
|
:res:=2;
|
|
wPrErr { diskette is write protected. }
|
|
:res:=150;
|
|
fLckdErr { file is locked }
|
|
:res:=5;
|
|
vLckdErr { volume is locked }
|
|
:res:=150;
|
|
fBsyErr { File is busy (delete) }
|
|
:res:=5;
|
|
dupFNErr { duplicate filename (rename) }
|
|
:res:=5;
|
|
opWrErr { file already open with with write permission }
|
|
:res:=5;
|
|
rfNumErr, { refnum error }
|
|
gfpErr { get file position error }
|
|
:res:=1; //TODO Exchange to something better
|
|
volOffLinErr { volume not on line error (was Ejected) }
|
|
:res:=152;
|
|
permErr { permissions error (on file open) }
|
|
:res:=5;
|
|
volOnLinErr{ drive volume already on-line at MountVol }
|
|
:res:=1; //TODO Exchange to something other
|
|
nsDrvErr { no such drive (tried to mount a bad drive num) }
|
|
:res:=1; //TODO Perhaps exchange to something better
|
|
noMacDskErr, { not a mac diskette (sig bytes are wrong) }
|
|
extFSErr { volume in question belongs to an external fs }
|
|
:res:=157; //TODO Perhaps exchange to something better
|
|
fsRnErr, { file system internal error:during rename the old
|
|
entry was deleted but could not be restored. }
|
|
badMDBErr { bad master directory block }
|
|
:res:=1; //TODO Exchange to something better
|
|
wrPermErr { write permissions error }
|
|
:res:=5;
|
|
dirNFErr { Directory not found }
|
|
:res:=3;
|
|
tmwdoErr { No free WDCB available }
|
|
:res:=1; //TODO Exchange to something better
|
|
badMovErr { Move into offspring error }
|
|
:res:=5;
|
|
wrgVolTypErr { Wrong volume type error [operation not
|
|
supported for MFS] }
|
|
:res:=1; //TODO Exchange to something better
|
|
volGoneErr { Server volume has been disconnected. }
|
|
:res:=152;
|
|
|
|
diffVolErr { files on different volumes }
|
|
:res:=17;
|
|
catChangedErr { the catalog has been modified }
|
|
{ OR comment: when searching with PBCatSearch }
|
|
:res:=1; //TODO Exchange to something other
|
|
afpAccessDenied, { Insufficient access privileges for operation }
|
|
afpDenyConflict { Specified open/deny modes conflict with current open modes }
|
|
:res:=5;
|
|
afpNoMoreLocks { Maximum lock limit reached }
|
|
:res:=5;
|
|
afpRangeNotLocked, { Tried to unlock range that was not locked by user }
|
|
afpRangeOverlap { Some or all of range already locked by same user }
|
|
:res:=1; //TODO Exchange to something better
|
|
afpObjectTypeErr { File/Directory specified where Directory/File expected }
|
|
:res:=3;
|
|
afpCatalogChanged { OR comment: when searching with PBCatSearch }
|
|
:res:=1; //TODO Exchange to something other
|
|
afpSameObjectErr
|
|
:res:=5; //TODO Exchange to something better
|
|
|
|
memFullErr { Not enough room in heap zone }
|
|
:res:=203;
|
|
else
|
|
res := 1; //TODO Exchange to something better
|
|
end;
|
|
MacOSErr2RTEerr:= res;
|
|
end;
|
|
|
|
{Translates a unix or dos path to a mac path. Even a mac path can be input, }
|
|
{then it is returned as is. A trailing directory separator in input}
|
|
{will result in a trailing mac directory separator. For absolute paths, the }
|
|
{parameter mpw affects how the root volume is denoted. If mpw is true, }
|
|
{the path is intended for use in MPW, and the environment variable Boot is}
|
|
{prepended. Otherwise the actual boot volume name is appended.}
|
|
{All kinds of paths are attempted to be translated, except the unusal }
|
|
{dos construct: a relative path on a certain drive like : C:xxx\yyy}
|
|
|
|
function TranslatePathToMac (const path: string; mpw: Boolean): string;
|
|
|
|
function GetVolumeIdentifier: string;
|
|
|
|
var
|
|
s: Str255;
|
|
dummy: Integer;
|
|
err: OSErr;
|
|
|
|
begin
|
|
if mpw then
|
|
GetVolumeIdentifier := '{Boot}'
|
|
else
|
|
GetVolumeIdentifier := macosBootVolumeName;
|
|
end;
|
|
|
|
var
|
|
slashPos, oldpos, newpos, oldlen, maxpos: Longint;
|
|
|
|
begin
|
|
oldpos := 1;
|
|
slashPos := Pos('/', path);
|
|
if (slashPos <> 0) then {its a unix path}
|
|
begin
|
|
if slashPos = 1 then
|
|
begin {its a full path}
|
|
oldpos := 2;
|
|
TranslatePathToMac := GetVolumeIdentifier;
|
|
end
|
|
else {its a partial path}
|
|
TranslatePathToMac := ':';
|
|
end
|
|
else
|
|
begin
|
|
slashPos := Pos('\', path);
|
|
if (slashPos <> 0) then {its a dos path}
|
|
begin
|
|
if slashPos = 1 then
|
|
begin {its a full path, without drive letter}
|
|
oldpos := 2;
|
|
TranslatePathToMac := GetVolumeIdentifier;
|
|
end
|
|
else if (Length(path) >= 2) and (path[2] = ':') then {its a full path, with drive letter}
|
|
begin
|
|
oldpos := 4;
|
|
TranslatePathToMac := GetVolumeIdentifier;
|
|
end
|
|
else {its a partial path}
|
|
TranslatePathToMac := ':';
|
|
end;
|
|
end;
|
|
|
|
if (slashPos <> 0) then {its a unix or dos path}
|
|
begin
|
|
{Translate "/../" to "::" , "/./" to ":" and "/" to ":" }
|
|
newpos := Length(TranslatePathToMac);
|
|
oldlen := Length(path);
|
|
SetLength(TranslatePathToMac, newpos + oldlen); {It will be no longer than what is already}
|
|
{prepended plus length of path.}
|
|
maxpos := Length(TranslatePathToMac); {Get real maxpos, can be short if String is ShortString}
|
|
|
|
{There is never a slash in the beginning, because either it was an absolute path, and then the}
|
|
{drive and slash was removed, or it was a relative path without a preceding slash.}
|
|
while oldpos <= oldlen do
|
|
begin
|
|
{Check if special dirs, ./ or ../ }
|
|
if path[oldPos] = '.' then
|
|
if (oldpos + 1 <= oldlen) and (path[oldPos + 1] = '.') then
|
|
begin
|
|
if (oldpos + 2 > oldlen) or (path[oldPos + 2] in ['/', '\']) then
|
|
begin
|
|
{It is "../" or ".." translates to ":" }
|
|
if newPos = maxPos then
|
|
begin {Shouldn't actually happen, but..}
|
|
Exit('');
|
|
end;
|
|
newPos := newPos + 1;
|
|
TranslatePathToMac[newPos] := ':';
|
|
oldPos := oldPos + 3;
|
|
continue; {Start over again}
|
|
end;
|
|
end
|
|
else if (oldpos + 1 > oldlen) or (path[oldPos + 1] in ['/', '\']) then
|
|
begin
|
|
{It is "./" or "." ignor it }
|
|
oldPos := oldPos + 2;
|
|
continue; {Start over again}
|
|
end;
|
|
|
|
{Collect file or dir name}
|
|
while (oldpos <= oldlen) and not (path[oldPos] in ['/', '\']) do
|
|
begin
|
|
if newPos = maxPos then
|
|
begin {Shouldn't actually happen, but..}
|
|
Exit('');
|
|
end;
|
|
newPos := newPos + 1;
|
|
TranslatePathToMac[newPos] := path[oldPos];
|
|
oldPos := oldPos + 1;
|
|
end;
|
|
|
|
{When we come here there is either a slash or we are at the end.}
|
|
if (oldpos <= oldlen) then
|
|
begin
|
|
if newPos = maxPos then
|
|
begin {Shouldn't actually happen, but..}
|
|
Exit('');
|
|
end;
|
|
newPos := newPos + 1;
|
|
TranslatePathToMac[newPos] := ':';
|
|
oldPos := oldPos + 1;
|
|
end;
|
|
end;
|
|
|
|
SetLength(TranslatePathToMac, newpos);
|
|
end
|
|
else if (path = '.') then
|
|
TranslatePathToMac := ':'
|
|
else if (path = '..') then
|
|
TranslatePathToMac := '::'
|
|
else
|
|
TranslatePathToMac := path; {its a mac path}
|
|
end;
|
|
|
|
{Concats the relative or full path path1 and the relative path path2.}
|
|
function ConcatMacPath (path1, path2: string): string;
|
|
|
|
begin
|
|
if Pos(':', path1) = 0 then {its partial}
|
|
Insert(':', path1, 1); {because otherwise it would be interpreted}
|
|
{as a full path, when path2 is appended.}
|
|
|
|
if path1[Length(path1)] = ':' then
|
|
begin
|
|
if path2[1] = ':' then
|
|
begin
|
|
Delete(path1, Length(path1), 1);
|
|
ConcatMacPath := Concat(path1, path2)
|
|
end
|
|
else
|
|
ConcatMacPath := Concat(path1, path2)
|
|
end
|
|
else
|
|
begin
|
|
if path2[1] = ':' then
|
|
ConcatMacPath := Concat(path1, path2)
|
|
else
|
|
ConcatMacPath := Concat(path1, ':', path2)
|
|
end;
|
|
end;
|
|
|
|
function IsMacFullPath (const path: string): Boolean;
|
|
|
|
begin
|
|
if Pos(':', path) = 0 then {its partial}
|
|
IsMacFullPath := false
|
|
else if path[1] = ':' then
|
|
IsMacFullPath := false
|
|
else
|
|
IsMacFullPath := true
|
|
end;
|
|
|
|
function IsDirectory (var spec: FSSpec): Boolean;
|
|
|
|
var
|
|
err: OSErr;
|
|
paramBlock: CInfoPBRec;
|
|
|
|
begin
|
|
with paramBlock do
|
|
begin
|
|
ioVRefNum := spec.vRefNum;
|
|
ioDirID := spec.parID;
|
|
ioNamePtr := @spec.name;
|
|
ioFDirIndex := 0;
|
|
|
|
err := PBGetCatInfoSync(@paramBlock);
|
|
|
|
if err = noErr then
|
|
IsDirectory := (paramBlock.ioFlAttrib and $10) <> 0
|
|
else
|
|
IsDirectory := false;
|
|
end;
|
|
end;
|
|
|
|
{Gives the path for a given file or directory. If parent is true,
|
|
a path to the directory, where the file or directory is located,
|
|
is returned. Functioning even with System 6.
|
|
TODO use AnsiString instead of Mac_Handle}
|
|
function FSpGetFullPath (spec: FSSpec; var fullPathHandle: Mac_Handle;
|
|
parent: Boolean): OSErr;
|
|
|
|
var
|
|
res: OSErr;
|
|
pb: CInfoPBRec;
|
|
|
|
begin
|
|
fullPathHandle:= NewHandle(0); { Allocate a zero-length handle }
|
|
if fullPathHandle = nil then
|
|
begin
|
|
FSpGetFullPath:= MemError;
|
|
Exit;
|
|
end;
|
|
|
|
if spec.parID = fsRtParID then { The object is a volume }
|
|
begin
|
|
if not parent then
|
|
begin
|
|
{ Add a colon to make it a full pathname }
|
|
spec.name := Concat(spec.name, ':');
|
|
|
|
{ We're done }
|
|
Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
|
|
res := MemError;
|
|
end
|
|
else
|
|
res := noErr;
|
|
end
|
|
else
|
|
begin
|
|
{ The object isn't a volume }
|
|
|
|
{ Add the object name }
|
|
if not parent then
|
|
Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
|
|
|
|
{ Get the ancestor directory names }
|
|
pb.ioNamePtr := @spec.name;
|
|
pb.ioVRefNum := spec.vRefNum;
|
|
pb.ioDrParID := spec.parID;
|
|
|
|
repeat { loop until we have an error or find the root directory }
|
|
begin
|
|
pb.ioFDirIndex := -1;
|
|
pb.ioDrDirID := pb.ioDrParID;
|
|
res := PBGetCatInfoSync(@pb);
|
|
|
|
if res = noErr then
|
|
begin
|
|
{ Append colon to directory name }
|
|
spec.name := Concat(spec.name, ':');
|
|
{ Add directory name to fullPathHandle }
|
|
Munger(fullPathHandle, 0, nil, 0, @spec.name[1], Length(spec.name));
|
|
res := MemError;
|
|
end
|
|
end
|
|
until not ((res = noErr) and (pb.ioDrDirID <> fsRtDirID));
|
|
end;
|
|
|
|
if res <> noErr then
|
|
begin
|
|
DisposeHandle(fullPathHandle);
|
|
fullPathHandle:= nil;
|
|
end;
|
|
|
|
FSpGetFullPath := res;
|
|
end;
|
|
|
|
function PathArgToFSSpec(s: string; var spec: FSSpec): Integer;
|
|
var
|
|
err: OSErr;
|
|
begin
|
|
err:= FSMakeFSSpec(workingDirectorySpec.vRefNum,
|
|
workingDirectorySpec.parID, s, spec);
|
|
PathArgToFSSpec := MacOSErr2RTEerr(err);
|
|
end;
|
|
|
|
function PathArgToFullPath(s: string; var fullpath: AnsiString): Integer;
|
|
|
|
var
|
|
err: OSErr;
|
|
res: Integer;
|
|
spec: FSSpec;
|
|
pathHandle: Mac_Handle;
|
|
|
|
begin
|
|
res:= PathArgToFSSpec(s, spec);
|
|
if (res = 0) or (res = 2) then
|
|
begin
|
|
err:= FSpGetFullPath(spec, pathHandle, false);
|
|
if err = noErr then
|
|
begin
|
|
HLock(pathHandle);
|
|
SetString(fullpath, pathHandle^, GetHandleSize(pathHandle));
|
|
DisposeHandle(pathHandle);
|
|
PathArgToFullPath:= 0;
|
|
end
|
|
else
|
|
PathArgToFullPath:= MacOSErr2RTEerr(err);
|
|
end
|
|
else
|
|
PathArgToFullPath:=res;
|
|
end;
|
|
|
|
function GetVolumeName(vRefNum: Integer; var volName: String): OSErr;
|
|
|
|
var
|
|
pb: HParamBlockRec;
|
|
|
|
begin
|
|
pb.ioNamePtr := @volName;
|
|
pb.ioVRefNum := vRefNum;
|
|
pb.ioVolIndex := 0;
|
|
PBHGetVInfoSync(@pb);
|
|
volName:= volName + ':';
|
|
GetVolumeName:= pb.ioResult;
|
|
end;
|
|
|
|
function GetWorkingDirectoryVRefNum: Integer;
|
|
|
|
begin
|
|
GetWorkingDirectoryVRefNum:= workingDirectorySpec.vRefNum;
|
|
end;
|