{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
{ }
{ GENERIC PREFERENCE FILES }
{ ``````````````````````````````````````````` }
{ }
{ Requires the Folders.p unit to be in the project. }
{ A Generic Utilities unit, for use by any program.
Copyright © by David Sinclair, 1990 2001.
I am releasing these units to the Pascal community. Feel free to use them in whole or part in your Pascal programs.
You are also welcome to modify these units to suit your needs. If you wish to re-distribute the sources with your changes,
please clearly indicate that you have changed them. In all cases, you must leave these comments and the copyright notice intact.
If you use a significant portion of these units, I would appreciate acknowledgement in your About dialog and/or documentation,
e.g. ³Dejal Generic Utilities copyright © by David Sinclair, 1990 - 2001.²
I¹d appreciate it if you also e-mail me at if you find these units useful. If you have any questions about
these units, you can e-mail me at that address and I will do my best to help, time permitting. However, these units are provided
³as is² and I do not guarantee their reliablity or suitability for any particular purpose.
These units have been used extensively in my Dejal shareware and freeware products over the years. Most of the code was written
many years ago, and the code and style may not be optimal in all cases, but unless otherwise noted all routines have been used
in released software, so should work as described.
Please visit and try out Dejal QuickEncrypt and/or my other shareware products. If you want to show
your appreciation for these units financially, registrations for my shareware are always welcome! Or you can make a donation to
me via my online order form: .
I hope you find these units useful, and good luck in your Pascal endeavors!
- David Sinclair, Dejal
}
{ * * * }
{ UNIT HISTORY: }
{ }
{ Version: Start - finish dates: Comments / changes: }
{ }
{ 1.0: 17 December 1990 Started work on the unit, for use with my Chatterbox }
{ FKEY. Based on MacOthello¹s File Operations unit }
{ 31 Mar 1 Apr 91 Got around to finishing work on above }
{ 1.0.1: 7 April 1991 Fixed problem with icon walking on every save }
{ 1.0.2: 11 April 1991 Fixed problem of reporting file not found on open }
{ 1.1: 17 August 1991 Switched off dialog I/O for INITs }
{ 1.2: 910 Dec 1991 Puts Prefs file in the Preferences folder under 7.0.x }
{ Added the prefsCopyResList function }
{ 1.3: 11 December 1991 Removed error alerts: stupid to report errs for prefs }
{ 2.0: 20 December 1991 First public release, in library form }
{ 27 October 2001 Public release as source code. }
{ * * * }
{ This unit was based on the File Operations unit from MacOthello, a 2nd year uni }
{ assignment at the University of Auckland in 1990; written by Shane Henderson for }
{ ³MacCheckers², Nov - Dec 1989; revised by John Thornley, January 1990; adapted }
{ to ³MacOthello² by Shane Henderson, January 1990; last alteration: 22 Feb 1990. }
{ * * * }
{ N.B: Most of my units require the compile-time variables Œapplication¹ }
{ and Œdebug¹, both of which are booleans. }
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
unit genPrefs;
interface
uses
Folders, genNumerics, genStrings;
const
resListType = 'Res#';
maxResCount = 30;
type
resSpec = record
theResType: resType;
theResID: integer;
newResID: integer
end;
resListRec = record
count: integer;
resToCopy: array[1..maxResCount] of resSpec
end;
resListPtr = ^resListRec;
resListHandle = ^resListPtr;
function prefsSave (prefsHandle: UNIV Handle; filename: str255; fileCreator, fileType: osType; {}
saveRoutine: procPtr): boolean;
{ Save the preference resources to a prefs file in the Preferences folder if under System }
{ 7.0 or greater, otherwise in the current System folder. }
{ This function calls a function in the host program to save the actual prefs resources to }
{ the prefs file, after opening or creating it successfully. This function should be of the form: }
{ function savePrefs (prefsHandle: UNIV Handle): Boolean; }
{ and should return true if the resources are saved correctly; use prefsCopyRes to copy any }
{ existing resources like a vers or TMPLs, and prefsSaveData for dynamic data. PrefsHandle }
{ is a handle to your own data, as passed to prefsSave: this facility is provided for FKEYs }
{ and INITs, etc, which cannot have global data‹applications can simply use globals and }
{ pass nil for this handle. }
function prefsRead (prefsHandle: UNIV Handle; filename: Str255; readRoutine: procPtr): boolean;
{ Read the preferences resources from the prefs file‹if present‹in the Preferences folder }
{ within the current System folder (for System 7.0 and greater) or in the root System folder }
{ (if prior to System 7.0). If this function returns false, call your read routine yourself, to }
{ read the defaults from the program¹s resource fork. }
{ This function calls a function in the host program to read the actual prefs resources from }
{ the prefs file, after opening it successfully. This function should be of the form: }
{ function readPrefs (prefsHandle: UNIV Handle): Boolean; }
{ and should return true if the resources are read correctly. The function should call }
{ GetResource for each resource it wishes to read from the prefs file, or whatever. }
{ PrefsHandle is a handle to your own data to be filled in by your readPrefs function: this }
{ facility is provided for FKEYs and INITs, etc, which cannot have global data‹ }
{ applications can simply use globals and pass nil for this handle. }
function prefsCopyRes (theType: resType; theID, newID: integer): boolean;
{ Copies the specified resource from the application resource file to the current (or prefs) }
{ resource file. Call this function during your savePrefs function for each resource to }
{ copy to the new prefs file (e.g. vers, TMPL, etc); note that you should use the following }
{ function if you are writing data from memory to the prefs file, rather than this one, as this }
{ one just copies existing‹unused‹resources. You should specify the ID to use if different }
{ from its current ID. N.B: this function does not open or close the resource file, and it does }
{ assume that the destination resource file is current (and also that the source was opened }
{ prior to the destination, and so is in the search path for getting resources), so do not call }
{ this routine anywhere except when you are in the savePrefs file without opening and }
{ closing the file manually. }
function prefsSaveData (theData: handle; theType: resType; theID: integer; theName: str255): boolean;
{ Adds the specified data to the currently open resource file as a resource of theType, with }
{ theID and theName. Returns true if successful. Call this function during your savePrefs }
{ function for each item of data to be saved from memory. Note: don¹t dispose of the data, }
{ since if you do it won¹t be available when the prefs file is saved. See the above function }
{ for more comments regarding use of these functions. }
function prefsCopyResList (resListID: integer): boolean;
{ Use this function to add several resources to the prefs file at the same time. Pass the ID }
{ of a ŒRes#¹ resource (in the app file) which contains a list of resource types, current }
{ IDs, and new IDs. (There is a template for the ŒRes#¹ resource in the genResFile.rsrc }
{ file.) Simply calls the prefsCopyRes function, above, for each resource specified in the }
{ list. Returns true if successful. }
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
implementation
function getPrefsVolNum (var volNum: integer): osErr;
{ Returns the vRefNum (or a working directory number) of the Preferences folder if }
{ running under System 7 or greater, or of the System folder if pre-7.0. }
{ Written by David Sinclair, 9 December 1991. }
var
err: osErr;
response: longint;
hasFindFolder: boolean;
theWorld: sysEnvRec;
WDParamBlk: WDPBRec;
prefsVRefNum: integer;
prefsDirID: longint;
unusedInt: integer;
begin
if gestalt(gestaltFindFolderAttr, response) = noErr then
hasFindFolder:= bitTst(@response, 31 - gestaltFindFolderPresent)
else
hasFindFolder:= false;
if hasFindFolder then
begin
err:= findFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, prefsVRefNum, prefsDirID);
if err = noErr then
begin
with WDParamBlk do
begin
ioNamePtr:= nil;
ioVRefNum:= prefsVRefNum;
ioWDProcID:= longint('ERIK');
ioWDDirID:= prefsDirID
end;
err:= PBOpenWDSync(@WDParamBlk);
volNum:= WDParamBlk.ioVRefNum
end
end
else
begin
err:= noErr;
unusedInt:= sysEnvirons(curSysEnvVers, theWorld);
volNum:= theWorld.sysVRefNum
end;
getPrefsVolNum:= err
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
FUNCTION CallSaveOrReadRoutine (prefsHandle: UNIV Handle; userRoutine: ProcPtr): Boolean;
{Calls the user¹s prefs saving or reading routine; a ProcPtr is used as this is only called once, so there¹s no need
to store the UPP.}
{Written by David Sinclair, 31 May 1997.}
{$IFC GENERATINGPOWERPC}
CONST
uppPrefsProcInfo = $000000D0; { FUNCTION (4 byte param): 1 byte result; }
VAR
theUPP: UniversalProcPtr;
result: Boolean;
BEGIN
theUPP:= NewRoutineDescriptor(userRoutine,uppPrefsProcInfo,GetCurrentISA);
result:= (CallUniversalProc(theUPP,uppPrefsProcInfo,prefsHandle)<>0);
DisposeRoutineDescriptor(theUPP);
CallSaveOrReadRoutine:= result
END;
{$ELSEC}
INLINE
$205f, { movea.l (a7)+,a0 ; (a0) is a ptr to string, 4(a0) is mode }
$4e90;
{$ENDC}
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
function prefsSave (prefsHandle: UNIV Handle; filename: str255; fileCreator, fileType: osType; {}
saveRoutine: procPtr): boolean;
{ Save the preference resources to a prefs file in the Preferences folder if under System }
{ 7.0 or greater, otherwise in the current System folder. }
{ This function calls a function in the host program to save the actual prefs resources to }
{ the prefs file, after opening or creating it successfully. This function should be of the form: }
{ function savePrefs (prefsHandle: UNIV Handle): Boolean; }
{ and should return true if the resources are saved correctly; use prefsCopyRes to copy any }
{ existing resources like a vers or TMPLs, and prefsSaveData for dynamic data. PrefsHandle }
{ is a handle to your own data, as passed to prefsSave: this facility is provided for FKEYs }
{ and INITs, etc, which cannot have global data‹applications can simply use globals and }
{ pass nil for this handle. }
{ Written by David Sinclair, 17 December 1990 & 31 March 1991; based heavily on the code }
{ from MacOthello: see unit header for copyright dates and names. }
var
unusedErr: OSErr;
applResRefNum, oldVol, volumeNumber, resForkRef: integer;
success, FileAlreadyExists, unusedBool: Boolean;
TempName: str255;
function FilePresent (FileName: str255; volume: integer): Boolean;
{ Function to determine whether a file is present on the given volume. }
var
ReferenceNumber: integer;
error: OSErr;
begin
error:= FSOpen(FileName, volume, ReferenceNumber);
if error = NoErr then { File already exists so see if it, or the volume is locked }
begin
FilePresent:= true;
error:= FSClose(ReferenceNumber)
end
else
FilePresent:= false
end; { of function FilePresent }
function NoLocks (volume: integer; FileName: str255; FileIsPresent: Boolean): Boolean;
{ Function to determine whether locks exist on the given volume. }
var
error: OSErr;
begin
if FileIsPresent then
NoLocks:= rename(FileName, volume, FileName) = noErr
else
begin
NoLocks:= create(FileName, volume, FileCreator, FileType) = noErr;
error:= FSDelete(FileName, VolumeNumber)
end
end; { of of function NoLocks }
function UniqueName (volume: integer): str255;
var
name: str255;
error: OSErr;
FileInfo: FInfo;
{ Function that returns a name that is not used on the given volume. }
begin
name:= 'Klut';
repeat
name:= concat(name, 'z');
error:= GetFInfo(name, volume, FileInfo);
until error <> NoErr;
UniqueName:= name
end; { of function UniqueName }
procedure copyTempToFile;
{ Copies the contents of the recently created temp file to the old prefs file. }
{ Written by David Sinclair, 7 April 1991. }
var
oldResForkRef: integer;
fileSize: longint;
buffPtr: ptr;
begin
success:= false;
if openRF(filename, volumeNumber, oldResForkRef) = noErr then
if openRF(tempName, volumeNumber, resForkRef) = noErr then
if getEOF(resForkRef, fileSize) = noErr then
begin
buffPtr:= newPtr(fileSize);
if buffPtr <> nil then
if fsRead(resForkRef, fileSize, buffPtr) = noErr then
if setEOF(oldResForkRef, 0) = noErr then
if fsWrite(oldResForkRef, fileSize, buffPtr) = noErr then
success:= true
end;
if success then { We only want to check for success here if all ok so far }
success:= fsClose(oldResForkRef) = noErr
else
unusedErr:= fsClose(oldResForkRef);
if success then { We only want to check for success here if all ok so far }
success:= fsClose(resForkRef) = noErr
else
unusedErr:= fsClose(resForkRef)
end; { Information copied! }
begin
SetCursor(getCursor(watchCursor)^^);
if saveRoutine <> nil then
begin
applResRefNum:= curResFile;
unusedErr:= getVol(nil, oldVol);
success:= getPrefsVolNum(volumeNumber) = noErr;
if success then
success:= SetVol(nil, volumeNumber) = noErr;
if success then
begin
FileAlreadyExists:= FilePresent(FileName, VolumeNumber);
success:= NoLocks(VolumeNumber, FileName, FileAlreadyExists);
if success then
begin
TempName:= UniqueName(VolumeNumber);
success:= Create(TempName, VolumeNumber, FileCreator, FileType) = noErr;
if success then
begin { Create the resource fork of the file. }
CreateResFile(TempName);
success:= ResError = noErr;
end;
if success then { Open the resource fork. }
begin
ResForkRef:= OpenResFile(TempName);
success:= ResError = noErr
end;
if success then
success:= CallSaveOrReadRoutine(prefsHandle, saveRoutine); { Call the user-specified }
{ routine to save whatever }
{ resources are appropriate }
CloseResFile(ResForkRef); { Here we only check the result of the }
if success then { close if we have been successful so far. }
success:= ResError = noErr;
unusedErr:= flushVol(nil, volumeNumber);
if success then
if fileAlreadyExists then
copyTempToFile { Copy the contents of the temp file to the prefs file }
else
success:= rename(TempName, VolumeNumber, FileName) = noErr;
{ Rename the temp file as the real prefs file }
unusedErr:= FSDelete(TempName, VolumeNumber) { Trash temp file }
end { of if NoLocks }
end; { of if success }
unusedErr:= flushVol(nil, volumeNumber);
useResFile(applResRefNum);
unusedBool:= SetVol(nil, oldVol) = noErr;
prefsSave:= success
end
else
prefsSave:= false { Idiot didn¹t specify a save routine, so ignore data. }
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
function prefsRead (prefsHandle: UNIV Handle; filename: Str255; readRoutine: procPtr): boolean;
{ Read the preferences resources from the prefs file‹if present‹in the Preferences folder }
{ within the current System folder (for System 7.0 and greater) or in the root System folder }
{ (if prior to System 7.0). If this function returns false, call your read routine yourself, to }
{ read the defaults from the program¹s resource fork. }
{ This function calls a function in the host program to read the actual prefs resources from }
{ the prefs file, after opening it successfully. This function should be of the form: }
{ function readPrefs (prefsHandle: UNIV Handle): Boolean; }
{ and should return true if the resources are read correctly. The function should call }
{ GetResource for each resource it wishes to read from the prefs file, or whatever. }
{ PrefsHandle is a handle to your own data to be filled in by your readPrefs function: this }
{ facility is provided for FKEYs and INITs, etc, which cannot have global data‹ }
{ applications can simply use globals and pass nil for this handle. }
{ Written by David Sinclair, 17 December 1990 & 31 March 1991; based heavily on the }
{ code from MacOthello: see unit header for copyright dates and names. }
var
oldVol, volumeNumber, applResRefNum, prefsResRefNum: integer;
readOK, unusedBool: boolean;
begin
setCursor(getCursor(watchCursor)^^);
if readRoutine <> nil then
begin
applResRefNum:= curResFile;
readOk:= getVol(nil, oldVol) = noErr;
readOk:= getPrefsVolNum(volumeNumber) = noErr;
ReadOK:= SetVol(nil, volumeNumber) = noErr;
if ReadOK then { Open the resource fork of the file. }
begin
prefsResRefNum:= OpenResFile(filename);
if resError = fnfErr then { Don¹t report file not found errors }
readOK:= false
else
ReadOK:= ResError = noErr
end;
if ReadOK then { Read in the prefs from the resource fork }
{ We have to be extra careful here. If a resource is missing totally then GetResource }
{ returns nil and ResError = NoErr so we must check the handles returned as well. }
readOK:= CallSaveOrReadRoutine(prefsHandle, readRoutine); { Call the user-specified }
{ routine to read whatever }
{ resources are appropriate }
CloseResFile(prefsResRefNum);
if ReadOK then { We only want to check if all ok so far }
ReadOK:= ResError = noErr;
useResFile(applResRefNum);
unusedBool:= SetVol(nil, oldVol) = noErr;
prefsRead:= readOK
end
else
prefsRead:= false { Idiot didn¹t specify a read routine, so ignore data. }
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
function prefsCopyRes (theType: resType; theID, newID: integer): boolean;
{ Copies the specified resource from the application resource file to the current (or prefs) }
{ resource file. Call this function during your savePrefs function for each resource to }
{ copy to the new prefs file (e.g. vers, TMPL, etc); note that you should use the following }
{ function if you are writing data from memory to the prefs file, rather than this one, as this }
{ one just copies existing‹unused‹resources. You should specify the ID to use if different }
{ from its current ID. N.B: this function does not open or close the resource file, and it does }
{ assume that the destination resource file is current (and also that the source was opened }
{ prior to the destination, and so is in the search path for getting resources), so do not call }
{ this routine anywhere except when you are in the savePrefs file without opening and }
{ closing the file manually. }
{ Written by David Sinclair, 31 March 1991. }
var
theResource: handle;
theName: str255;
begin
prefsCopyRes:= false;
theResource:= getResource(theType, theID); { Read the resource }
if theResource <> nil then
begin
getResInfo(theResource, theID, theType, theName);
detachResource(theResource);
if resError = noErr then
begin
addResource(theResource, theType, newID, theName); { Add the resource }
prefsCopyRes:= resError = noErr
end;
end
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
function prefsSaveData (theData: handle; theType: resType; theID: integer; theName: str255): boolean;
{ Adds the specified data to the currently open resource file as a resource of theType, with }
{ theID and theName. Returns true if successful. Call this function during your savePrefs }
{ function for each item of data to be saved from memory. Note: don¹t dispose of the data, }
{ since if you do it won¹t be available when the prefs file is saved. See the above function }
{ for more comments regarding use of these functions. }
{ Written by David Sinclair, 1 April 1991. }
begin
AddResource(theData, theType, theID, theName);
prefsSaveData:= ResError = NoErr
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
function prefsCopyResList (resListID: integer): boolean;
{ Use this function to add several resources to the prefs file at the same time. Pass the ID }
{ of a ŒRes#¹ resource (in the app file) which contains a list of resource types, current }
{ IDs, and new IDs. (There is a template for the ŒRes#¹ resource in the genResFile.rsrc }
{ file.) Simply calls the prefsCopyRes function, above, for each resource specified in the }
{ list. Returns true if successful. }
{ Written by David Sinclair, 9 December 1991. }
var
resListHndl: resListHandle;
success: boolean;
hndlState: signedByte;
index: integer;
begin
resListHndl:= resListHandle(getResource(resListType, resListID));
if resListHndl = nil then
prefsCopyResList:= false
else
begin
success:= true;
index:= 1;
numLockHandle(resListHndl, hndlState);
with resListHndl^^ do
while (index <= count) & success do
begin
with resToCopy[index] do
success:= prefsCopyRes(theResType, theResID, newResID);
index:= index + 1
end;
numUnlockHandle(resListHndl, hndlState);
prefsCopyResList:= success
end
end;
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
{--------------------------------|--------------------------------------------------------------------|---------------------------------------------------------|------------------------}
end.