From: jesjones@stein.u.washington.edu (Jesse Jones)
Newsgroups: comp.sys.mac.programmer
Subject: Re: code for Finding Prefs File (long)
Summary: A slick Prefs module written in Modula-2
Date: 9 Jan 93 05:28:21 GMT
Organization: University of Washington, Seattle
NNTP-Posting-Host: stein.u.washington.edu


   There has been some discussion lately about how to handle a preferences
file, so I thought I'd post my code. It's nothing astounding, but I think
it has some advantages over the previously posted code:

   1) FindFolder and the high level file manager routines are used so
      the code is quit a bit easier to follow.
   2) The GetPref procedure is passed a procedure parameter that is used to
      return a default preference if the preference is missing from the
      Prefs file.
   3) Individual preferences are referenced by the ResType, resource name,
      and version number. If the version numbers don't match the default
      preference is returned.
      

   --Jesse
   
   
   Here is the interface for the Prefs module. It's written in SemperSoft
Modula-2. HANDLE is compatible with any pointer to pointer and Mstring is
a 256-byte string terminated with zero.

DEFINITION MODULE Prefs;   (* By Jesse Jones, 1992 *)


FROM SYSTEM     IMPORT HANDLE;
FROM Types      IMPORT Str255, Mstring;


TYPE
   PrefsProc = PROCEDURE(): HANDLE;
   
VAR
   PrefsName: Str255;         (* Name of the Preferences file. *)


PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
   (* default is called if Prefs not present or version <> vers. *)

PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
   (* Change or add a preference. *)

PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;

PROCEDURE DeletePref (rType, rName: Mstring);


END Prefs.

   And here is the implementation of the Prefs module.

IMPLEMENTATION MODULE Prefs;

(* Import list deleted *)

   (* Version numbers are saved in PVRS resources. These resources contain a
      single integer for the version number and are referenced by name, with
      the name being the concatenation of the preference resource type and
      the preference's name. *)
PROCEDURE GetVersion (rType, rName: Mstring): INTEGER;
   VAR 
      data: IntHandle;
      str : Str255;
      vers: INTEGER;
      err : OSErr;
BEGIN
   str := StrToPStr(Concat(rType, rName));
   data := Get1NamedResource('PVRS', str);
   err := ResError();
   IF (err = resNotFound) OR (data = NIL) THEN
      vers := -1;
   ELSIF ErrCheck(err) THEN
      vers := data^^;
   ELSE
      vers := -1;
   END;
   RETURN vers;
END GetVersion;

PROCEDURE SetVersion (rType, rName: Mstring; version: INTEGER);
   VAR 
      data : IntHandle;
      str  : Mstring;
      err  : OSErr;
      dummy: BOOLEAN;
BEGIN
   str := Concat(rType, rName);
   data := Get1NamedResource('PVRS', StrToPStr(str));
   err := ResError();
   IF (err = resNotFound) OR (data = NIL) THEN
      data := CreateHandle(SIZE(INTEGER));
      data^^ := version;
      dummy := InsertResource('PVRS', str, 0, data, TRUE);
   ELSIF ErrCheck(err) THEN
      data^^ := version;
      dummy := ChangeResource(data);
   END;
END SetVersion;

PROCEDURE OpenPrefs (VAR refNum: INTEGER; perm: SignedByte): BOOLEAN;
   VAR
      vRef : INTEGER;
      dir  : LONGINT;
      err  : OSErr;
      dummy: BOOLEAN;
BEGIN
   err := FindPrefsFolder(vRef, dir);                           (* find (or create) prefs folder *)
   IF ErrCheck(err) THEN
      refNum := HOpenResFile(vRef, dir, PrefsName, perm);      (* open prefs file *)
      err := ResError();
      IF err = fnfErr THEN                                       (* if prefs file is missing *)
         HCreateResFile(vRef, dir, PrefsName);                  (* then create it *)
         refNum := HOpenResFile(vRef, dir, PrefsName, perm);   (* and open the new file *)
         err := ResError();
         dummy := ErrCheck(err);
      END;
   END;
   RETURN err = noErr;
END OpenPrefs;

PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
   VAR
      refNum: INTEGER;
      data  : HANDLE;
      err   : OSErr;
BEGIN
   IF OpenPrefs(refNum, fsRdPerm) THEN
      IF GetVersion(rType, rName) <> vers THEN
         data := default();
      ELSE
         data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
         err := ResError();
         IF (err = resNotFound) OR (data = NIL) THEN
            data := default();
         ELSIF ErrCheck(err) THEN
            DetachResource(data);
            HNoPurge(data);
         ELSE
            data := default();
         END;
      END;
      CloseResFile(refNum);
   ELSE
      data := default();
   END;
   Assert(data <> NIL);
   RETURN data;
END GetPref;

PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;
   VAR
      refNum: INTEGER;
      data  : HANDLE;
      found : BOOLEAN;
      err   : OSErr;
BEGIN
   found := FALSE;
   IF OpenPrefs(refNum, fsRdPerm) THEN
      IF GetVersion(rType, rName) >= vers THEN
         data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
         err := ResError();
         IF (err <> resNotFound) AND (data <> NIL) THEN
            found := ErrCheck(err);
         END;
      END;
      CloseResFile(refNum);
   END;
   RETURN found;
END PrefPresent;

   (* SetPref is the only routine to actually add a preference to the
      Prefs file. Note that the preferences are purgable. *)
PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
   VAR
      refNum: INTEGER;
      old   : HANDLE;
      err   : OSErr;
      done  : BOOLEAN;
BEGIN
   done := FALSE;
   Assert(data <> NIL);
   IF OpenPrefs(refNum, fsRdWrPerm) THEN
      old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
      err := ResError();
      IF (err = resNotFound) OR (old = NIL) THEN
         done := InsertResource(rType, rName, 0, data, TRUE);   
      ELSIF ErrCheck(err) THEN
         RemoveResource(old);
         done := InsertResource(rType, rName, 0, data, TRUE);
      END;
      IF done THEN 
         SetVersion(rType, rName, vers);
         DetachResource(data);
      END;
      CloseResFile(refNum);
   END;
END SetPref;

PROCEDURE DeletePref (rType, rName: Mstring);
   VAR
      refNum: INTEGER;
      old   : HANDLE;
      err   : OSErr;
BEGIN
   IF OpenPrefs(refNum, fsRdWrPerm) THEN
      old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
      err := ResError();
      IF (err = resNotFound) OR (old = NIL) THEN
         (* do nothing *)
      ELSIF ErrCheck(err) THEN
         RemoveResource(old);
      END;
      CloseResFile(refNum);
   END;
END DeletePref;


BEGIN
   PrefsName := "";
END Prefs.

   InsertResource, ChangeResource, and RemoveResource are some handy resource
manipulation routines I wrote a while back. I'll post them in a seperate
message.
Newsgroups: comp.sys.mac.programmer