summaryrefslogtreecommitdiff
path: root/rtl/macos/macutils.inc
diff options
context:
space:
mode:
Diffstat (limited to 'rtl/macos/macutils.inc')
-rw-r--r--rtl/macos/macutils.inc566
1 files changed, 566 insertions, 0 deletions
diff --git a/rtl/macos/macutils.inc b/rtl/macos/macutils.inc
new file mode 100644
index 0000000000..c741313a25
--- /dev/null
+++ b/rtl/macos/macutils.inc
@@ -0,0 +1,566 @@
+{
+ $Id: macutils.inc,v 1.6 2005/04/03 22:15:04 olle Exp $
+ This file is part of the Free Pascal run time library.
+ Copyright (c) 2004 by Olle Raab
+
+ Some utilities specific for Mac OS.
+ Modified portions from Peter N. Lewis (PNL Libraries). Thanks !
+
+ 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.}
+
+function FSpGetFullPath (spec: FSSpec; var fullPath: AnsiString;
+ parent: Boolean): OSErr;
+
+ var
+ res: OSErr;
+ pb: CInfoPBRec;
+
+begin
+ res := noErr;
+ 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 }
+ fullPath:= spec.name + ':';
+ end
+ else
+ begin
+ fullPath:= '';
+ res:= afpObjectTypeErr; {to have something close to this error.}
+ end;
+ end
+ else
+ begin
+ { The object isn't a volume }
+
+ { Add the object name }
+ if not parent then
+ fullPath:= spec.name
+ else
+ fullPath:= '';
+
+ { 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 := spec.name + ':';
+ { Add directory name to fullPathHandle }
+ fullPath:= spec.name + fullPath;
+ end
+ end
+ until not ((res = noErr) and (pb.ioDrDirID <> fsRtDirID));
+ end;
+
+ FSpGetFullPath := res;
+end;
+
+function PathArgToFSSpec(s: string; var spec: FSSpec): Integer;
+var
+ err: OSErr;
+begin
+ if pathTranslation then
+ s := TranslatePathToMac(s, false);
+ err:= FSMakeFSSpec(workingDirectorySpec.vRefNum,
+ workingDirectorySpec.parID, s, spec);
+
+ if s <> '' then
+ PathArgToFSSpec := MacOSErr2RTEerr(err)
+ else
+ PathArgToFSSpec := 3; {Empty paths are invalid paths}
+end;
+
+function PathArgToFullPath(s: string; var fullpath: AnsiString): Integer;
+
+var
+ err: OSErr;
+ res: Integer;
+ spec: FSSpec;
+
+begin
+ res:= PathArgToFSSpec(s, spec);
+ if (res = 0) or (res = 2) then
+ begin
+ err:= FSpGetFullPath(spec, fullpath, false);
+ 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;
+
+ function GetVolInfo (var name: Str63; var vrn: integer; index: integer; var CrDate: longint): OSErr;
+ var
+ pb: ParamBlockRec;
+ oe: OSErr;
+ begin
+ if (name <> '') and (name[length(name)] <> ':') then begin
+ name := concat(name, ':');
+ end;
+ pb.ioNamePtr := @name;
+ pb.ioVRefNum := vrn;
+ pb.ioVolIndex := index;
+ oe := PBGetVInfoSync(@pb);
+ if oe = noErr then begin
+ vrn := pb.ioVRefNum;
+ CrDate := pb.ioVCrDate;
+ end;
+ GetVolInfo := oe;
+ end;
+
+ {Checks that fs really is an application with the specified creator}
+ function ConfirmApplicationExists (creator: OSType; var fs: FSSpec): OSErr;
+
+ var
+ err: OSErr;
+ info: FInfo;
+ begin
+ err := HGetFInfo(fs.vRefNum, fs.parID, fs.name, info);
+ if err = noErr then begin
+ if (info.fdType <> FourCharCodeToLongword('APPL')) or (info.fdCreator <> creator) then begin
+ err := fnfErr;
+ end;
+ end;
+ ConfirmApplicationExists := err;
+ end;
+
+ {Find an application with the given creator, in any of the mounted volumes.}
+ function FindApplication (creator: OSType; var fs: FSSpec): OSErr;
+ var
+ i: integer;
+ pbdt: DTPBRec;
+ crdate: longint;
+ oe: OSErr;
+ found: Boolean;
+ begin
+ found := false;
+ if (macosSystemVersion >= $0700) then begin
+ i := 1;
+ repeat
+ fs.vRefNum := 0;
+
+ {Get info for volume i}
+ oe := GetVolInfo(fs.name, fs.vRefNum, i, crdate);
+ i := i + 1;
+ if oe = noErr then begin
+ with pbdt do begin
+ fs.name := '';
+ ioNamePtr := @fs.name;
+ ioVRefNum := fs.vRefNum;
+
+ {Get the desktop database for this volume}
+ oe := PBDTGetPath(@pbdt);
+ if oe = noErr then begin
+ ioFileCreator := creator;
+
+ {Look first for the "default" (newest) application file}
+ ioIndex := 0;
+ oe := PBDTGetAPPLSync(@pbdt);
+ if oe = noErr then begin
+ fs.parID := pbdt.ioAPPLParID;
+ found := ConfirmApplicationExists(creator,fs)=noErr;
+ end;
+
+ {If not ok, look for older ones.}
+ if not found then begin
+ ioIndex := 1;
+ repeat
+ oe := PBDTGetAPPLSync(@pbdt);
+ if oe = noErr then begin
+ fs.parID := pbdt.ioAPPLParID;
+ found := ConfirmApplicationExists(creator,fs)=noErr;
+ end;
+ ioIndex := ioIndex + 1;
+ until found or (oe <> noErr);
+ end;
+
+ end;
+ end;
+ oe := noErr;
+ end;
+ until found or (oe <> noErr);
+ end;
+ if found then begin
+ oe := noErr;
+ end else begin
+ oe := fnfErr;
+ fs.vRefNum := 0;
+ fs.parID := 2;
+ fs.name := '';
+ end;
+ FindApplication := oe;
+ end;
+
+function LaunchFSSpec (tofront: Boolean; const applicationFileSpec: FSSpec): OSErr;
+var
+ launchThis: LaunchParamBlockRec;
+begin
+ launchThis.launchAppSpec := @applicationFileSpec;
+ launchThis.launchAppParameters := nil;
+ launchThis.launchBlockID := extendedBlock;
+ launchThis.launchEPBLength := extendedBlockLen;
+ launchThis.launchFileFlags := 0;
+ launchThis.launchControlFlags := launchContinue or launchNoFileFlags;
+ if not tofront then begin
+ launchThis.launchControlFlags := launchThis.launchControlFlags or launchDontSwitch;
+ end;
+
+ LaunchFSSpec:= LaunchApplication(@launchThis);
+end;
+