TweetFollow Us on Twitter

Library Manager
Volume Number:3
Issue Number:8
Column Tag:Pascal Procedures

Library Manager Cures HFS Brain Damage

By Dave Rausch, Fullerton, CA

Dave Raush is an expert on Mac music generation

“File Not Found!”

How many times have you run a compiler or linker program only to have it come up and say “file not found” after which the program returns you to the Finder or worse, crashes the system. Then you have to find the mystery file the program wants, then figure out which folder the program expects to find it, and try to coerce the program into locating it. Programs that make you do this are HFS brain damaged. They can’t find their way out of a paper bag. Microsoft is notorious for products which either can’t find files (MS Basic) or require files to be in certain folders (MS Word), or die when a file cannot be found (MS Fortran). While Apple provided an elegant way for applications to allow the user to find a document, via the standard file dialog, no thought was given to those types of programs which must locate a library or reference file that is either not known to the user, or not directly related to a user created document, but which is vital for the program to function. Think Technologies with their Lightspeed C and Pascal was one of the first companies to market a truly HFS smart compiler. If LS Pascal can’t find a file it needs, it asks the user to find it! What could be simpler or more straight forward? Who needs path managers? Who needs Finder DA’s? Who needs path name scripts? In this article I show how you can use this technique to make your applications HFS smart, by putting up a standard file dialog if a library file cannot be found. Once the user finds the file, it’s location is remembered in a resource so the dialog does not have to be repeated unless the file is moved or renamed. Why should a program (like 4th Dimension) prevent you from moving or renaming your files? Even though HFS has been with us for over a year and a half, many developers are still working around it, not with it. It’s time to make our programs HFS friendly and this article will show you how to do it.

Library Manager Tracks Your Files

As Macintosh programs become smarter and heftier, the need increases to maintain libraries of information that are external to the application itself. Because I am currently developing an application that requires three separate kinds of open file lists, I spent some time putting together what I call a “Library Manager” to help keep things straight. The classic example of a file you would use the library manager to track and open is the “dictionary”, a library type that may include a whole list of generic, technical, and user-defined files that should be opened automatically without unnecessary user intervention and without the user having to place them in either (a) the same folder with the application or (b) the System Folder which is beginning to get cluttered...; (Acius, are you paying attention?)

Fig. 1 Let the user find it!

Our application is a demonstration of how an application can be designed to keep track of and locate files without crashing, quiting or forcing the user to place or name files in a certain way. The demo application makes calls to what I have named the Library Manager, a unit of LS Pascal routines which provide the support for creating HFS friendly programs. By adding this Library Manager unit to your own projects, your applications can be made as HFS friendly as the products from Think Technologies.

Installing Library Manager

The Library Manager keeps track of application files using an application defined resource of type LMIR, Library Manager Information Resource. A separate LMIR resource is created for each file for which the application needs to know the whereabouts. The LMIR is shown defined below:

LMIR = RECORD  {Library Manager Info Record}
 rsrcID : integer;
 volname :  str31;
 vRef : integer;
 hfsvolume :   boolean;
 DirID :  Longint;
 filename : str63;
 fRefNum :  integer;
 next, prev :  LmirHdl;
 FTyp : integer;
 RecsOnFile, CurRec : integer;
 changed :  boolean;
 Status : (Open, Closed, NotFound);
 END;

Once the Library Manager code is added to an existing project and the routine OpenLib is called, the Manager is self-initializing. When a call is made to OpenLib for a LMIR resource that does not exist, OpenLib creates the resource and adds it to the current resource file. A typical setup routine is shown below that makes this call to OpenLib:

 PROCEDURE SetUpThings;
 BEGIN
 OpenLib(TopFile, ‘Application Default Library’); 
 {When program boots}
 OpenLinkedLib(TopFile, ‘User Default Library’); 
 {When program boots}
 END;

When OpenLib is called, if the appropriate LMIR resource does not exist or if the file the resource points to has been moved (or renamed, is unmounted, etc.) the user is prompted and allowed (not forced) to find or create a new library file. The message displayed to the user tells him the exact nature of the problem: The volume isn’t mounted, the file can’t be found, the file is already open for read/write, etc. All of the possible HFS file problems are clearly trapped and displayed here. No more “call Aldus, error number 1234” stuff.

For the developer, this means that once the library manager is added to his code and calls are made to OpenLib, he can

(1) Ignore prompts to find or create the library (until ready)

(2) Create a new library file someplace (and not be bothered again)

(3) Tell the program where he has moved the library file to

(4) Mount the volume that the library file is on, if that is the problem.

Note that moving the application won’t disturb the Library Manager’s ability to find a file, only tampering with the location or name of the library file itself.

Lotsa Lovely Linked Lists of Libraries

When calls are made to OpenLinkedLib additional library files are linked using a circular forward and backward chain. As many separate lists of files as desired can be created with OpenLib, and as many files can be added to any particular list with OpenLinkedLib as the application demands. Thus you can support a whole bunch of related files that must be opened at run time.

The linked lists allow you to search through all files of a given library type by using a loop along the lines of:

 next := top;
 repeat
 IFoundIt := YourSearchRoutine(next);
 next : next^^.next;
 until IFoundIt or (next = top);

Another agreeable aspect of linked lists is that the library manager requires you to define only one (global?) variable per list: the handle to the top item of the list.

Removing Files from a List

Call RemoveLib to close an open library file and remove it from the list. Note that RemoveLib always returns a valid handle if there are still any open libraries in the list. This allows you to remove even the “top” item from a library list, because RemoveLib automatically replaces it with a “new” top. If you are removing the top item from the list, you should reference it specifically when you pass it to RemoveLib.

 RemoveLib(Top); 
 { that is, whatever variable name you passed 
 to Open Lib originally }

not

 RemoveLib(next);

or

 RemoveLib(next^^.next);

if there’s a chance that next or next^^.next = Top.

A file that has been removed with RemoveLib can be reopened with OpenLinkedLib or OpenLib.

RemoveLib checks whether the changed field of the Library Manager Record is true. If so, it calls HandleChanges. HandleChanges does nothing! It is just included to give you the general idea.

Closing All Open Files in a List

At exit time, call CloseLibList for each library list that is open. It will close all open files in a list, by successive calls to RemoveLib, until the list is empty.

Access is by Resource Name, not Resource ID

As you will see when you examine OpenLib and GetLibResource, the library manager accesses files by a descriptive resource name rather than by resource ID.

The Library Manager was developed to work in conjunction with routines that will extend menus by allowing the user to create new menu items and associate those items with individual files. If you are allowing the user to extend libraries in this (or some similar) way, you should call GetLibResource before you call OpenLinkedLib or OpenLib to make sure that a “new” library descriptive name does not already exist. It is your responsibility to make sure that the names used to access resources are unique, the Library Manager does not check.

Library File Type

When the Library Manager creates a new file it assigns it type LMIR. Likewise, when you look for a file using the standard file dialog, it will only show you files of type LMIR. This can be modified according to your application’s requirements.

The Nitty Gritty of Tracking Things

The routines work with new roms or old roms, HFS or MFS volumes (or any mixture);

In order to actually install a LMIR resource, the user (or developer) is guided by the application through the Standard File Package.

In order to maintain compatibility between HFS and MFS, Apple fixed things so that the Standard File returns a working directory number instead of a volume reference number. The important thing about working directory numbers is that they are created on the fly and they are strictly temporary and relative to a particular session.

What is needed is to turn the working directory number into a Volume Name and a Directory ID. The procedure that does this is GetPathInfo. When a file needs to be opened, the procedure OpenWD creates a new working directory for the file from this information. This new working directory number is then passed to FSOpen as the volume reference. If the volume is MFS and not HFS, OpenWD simply returns the volume number instead of a working directory and FSOpen is still happy.

File IO on Open Files

The File reference number for an opened file is stored in the LMIR record. There are also fields to maintain information on file type, total records, current record number. You can, of course, add other fields to your LMIR definition (or remove some of mine).

The Program Lib Mgr Demo..

...does a minimum three things

1) It looks for mythical “application default” and “user default” library files.

2) It tells you the status of the files: Opened, Closed, NotFound.

3) It waits for you to press the mouse button and then closes the files (if any are open).

The first time you run the program it will prompt you to find or create the two files mentioned above. If you satisfy these requests, the next time it will simply open the files without your intervention. To see how the program handles various problems, try moving, deleting, renaming, dismounting one or both files and rerunning the program.

The program was developed in LightSpeed Pascal. If you are using something other than LightSpeed, remove the DisplayMsg procedure and use the DisplayMsg2 procedure.

The only resource that the file needs is an alert dialog, ID = 301 and its associated item list. If you run the program in Lightspeed project mode, make sure you use a project resource file, or the “current resource file” the LMIR resources will be added to will be your system file!

One last thing: this code will need to be modified to fit your particular needs. The Library Manager I’ve presented is meant to be suggestive, not definitive. I’m sure that you will have to make changes for your own application environment(s). But it will give you a good base to work from. Toward this end, direct access to procedures not specifically mentioned in this article is provided in the interface portion of the LibMgr unit.

Acknowledgements

The HFSRunning and NewRoms came from a utilities package developed by David O’Rourke that is in the public domain. Thanks, Dave!

And so, if there are no further questions, let’s break for lunch.


{1}
UNIT LibMgr;

INTERFACE
 USES
 Rom85, HFS;
 TYPE
 StandardType = (StandardGet, StandardPut);
 Outcome = (Success, Error, Cancellation);
 str63 = STRING[63];
 str31 = STRING[31];
 str80 = STRING[80];

 LmirPtr = ^Lmir;
 LmirHdl = ^LmirPtr;

 LMIR = RECORD  {Library Manager Info Record}
 rsrcID : integer;
 volname : str31;
 vRef : integer;
 hfsvolume : boolean;
 DirID : Longint;
 filename : str63;
 fRefNum : integer;
 next, prev : LmirHdl;
 FTyp : integer;
 RecsOnFile, CurRec : integer;
 changed : boolean;
 Status : (Open, Closed, NotFound);
 END;

 PROCEDURE GetPathInfo (vRefNum : integer;
 VAR rootVol : Str31;
 VAR hfsFlag : boolean;
 VAR WDirID : longint);

 FUNCTION OpenWD (VAR vRefNum : integer;
 DirID : longint) : OSErr;
 PROCEDURE OpenLib (VAR whichLib : LmirHdl;
 itsName : str255);
 PROCEDURE OpenLinkedLib (LinkTo : LmirHdl;
 ResName : str255);
 FUNCTION CreateLib (VAR newLib : LmirHdl) : boolean;
 FUNCTION FindLib (VAR theLib : LmirHdl) : boolean;
 PROCEDURE RemoveLib (VAR whichFile : LmirHdl);    
 {Close file and remove from list of open libraries}
 PROCEDURE CloseLibList (Top : LmirHdl); 
 {Close all open libraries in a given list and empty list}
 FUNCTION StandardFile (opCode : StandardType;
 oldName : Str255;
 fType : OSType;
 VAR vRef : integer) : str63;
 PROCEDURE GetLibraryResource (VAR theLibrary : LmirHdl;
 ResourceName : str255);

IMPLEMENTATION

 FUNCTION HFSRunning : boolean;
 CONST
 FSFCBLen = $3F6;
 VAR
 HFS : ^INTEGER;
 BEGIN
 HFS := POINTER(FSFCBLen);
 HFSRunning := (HFS^ > 0);
 END;

 FUNCTION NewRoms : boolean;
 CONST
 NewRomsID = 117;
 VAR
 RomVersion, Machine : INTEGER;
 BEGIN
 Environs(RomVersion, Machine);
 NewRoms := RomVersion >= NewRomsID;
 END;

 FUNCTION GetErrorMsg (Result : OSErr) : str80;
 BEGIN
 Result := abs(Result);
 CASE Result OF
33 : 
 GetErrorMsg := ‘the file directory is full.  ‘;
34 : 
 GetErrorMsg := ‘all allocation blocks on the volume are full.  ‘;
35 : 
 GetErrorMsg := ‘the specified volume is not mounted.  ‘;
36 : 
 GetErrorMsg := ‘there was an unspecified I/O Error.  ‘;
37 : 
 GetErrorMsg := ‘the file name or volume name is bad (perhaps zero-length). 
 ‘;
39 : 
 GetErrorMsg := ‘logical end-of-file was reached unexpetedly during read 
operation.  ‘;
40 : 
 GetErrorMsg := ‘an attempt was made to position before start of file. 
 ‘;
42 : 
 GetErrorMsg := ‘too many are files open.  ‘;
43 : 
 GetErrorMsg := ‘the file could not be found.  ‘;
44 : 
 GetErrorMsg := ‘the volume is locked by a hardware setting.  ‘;
45 : 
 GetErrorMsg := ‘the file is locked’;
46 : 
 GetErrorMsg := ‘the volume is locked by a software flag.  ‘;
47 : 
 GetErrorMsg := ‘the file is already in use.  ‘;
48 : 
 GetErrorMsg := ‘a file with the specified name exists and cannot be 
overwritten.  ‘;
49 : 
 GetErrorMsg := ‘the file is already open for read/write.  It cannot 
be reopened.  ‘;
50 : 
 GetErrorMsg := ‘no volume was specified and there is no default volume. 
‘;
51 : 
 GetErrorMsg := ‘a non-existent path was specified.  ‘;
52 : 
 GetErrorMsg := ‘there was an error finding current position in file. 
 ‘;
53 : 
 GetErrorMsg := ‘the specified volume is not on-line.  ‘;
54 : 
 GetErrorMsg := ‘there was an attempt to open a locked file for writing. 
 ‘;
55 : 
 GetErrorMsg := ‘there was an attempt to mount an already mounted volume. 
 ‘;
56 : 
 GetErrorMsg := ‘the specified drive number is not mounted.  ‘;
57 : 
 GetErrorMsg := ‘the volume lacks Macintosh-format directory.  ‘;
58 : 
 GetErrorMsg := ‘there was an external file system error.  ‘;
59 : 
 GetErrorMsg := ‘there was a problem during rename.  ‘;
60 : 
 GetErrorMsg := ‘the master directory block is bad; this volume must 
be reinitialized.  ‘;
61 : 
 GetErrorMsg := ‘the read/write permission of the file/folder does not 
allow writing . ‘;
108 : 
 GetErrorMsg := ‘there is insufficient application memory.  ‘;
120 : 
 GetErrorMsg := ‘the directory could not be found.  ‘;
121 : 
 GetErrorMsg := ‘too many working directories are open.  ‘;
122 : 
 GetErrorMsg := ‘a folder cannot be placed in its own subfolder.  ‘;
123 : 
 GetErrorMsg := ‘an attempt was made to do hierarchical operations on 
a nonhierarchical volume.  ‘;
127 : 
 GetErrorMsg := ‘there was an internal file system error.  ‘;
 END;
 END;

 PROCEDURE UpdateResource (vanilla : handle);
 BEGIN
 ChangedResource(vanilla);
 WriteResource(vanilla);
 END;

 PROCEDURE IOCheck (resultCode : OSErr);
 VAR
 ignore : INTEGER;
 errorString : Str255;
 BEGIN
 IF resultCode <> NoErr THEN
 BEGIN
 NumToString(resultCode, errorString);
 ParamText(‘Macintosh Error #’, errorString, ‘:  ‘, GetErrorMsg(resultCode));
 InitCursor;
 ignore := StopAlert(305, NIL);
 END
 END;

 FUNCTION StandardFile;
 {opCode : StandardType;  oldName : 
 Str255; fType : OSType; }
 {var vRef : integer) :  str63 }
 VAR
 where : Point;
 reply : SFReply;
 textType : SFTypeList;
 BEGIN
 where.h := 80;
 where.v := 55;
 textType[0] := fType;
 reply.vRefNum := vRef;
 IF opCode = StandardGet THEN
 SFGetFile(where, ‘Select Application to Launch’, NIL, 1, textType, NIL, 
reply)
 ELSE
 SFPutFile(where, ‘’, oldName, NIL, reply);
 WITH reply DO
 IF NOT good THEN
 StandardFile := ‘’
 ELSE
 BEGIN
 StandardFile := fName;
 vRef := vRefNum
 END
 END;

 PROCEDURE HandleChanges (changedFile : LmirHdl);
 BEGIN
 {A boolean field in the LMIR can be set if your   change records in 
memory but you do not immediately  write them out to the file... Then 
put whatever   routines you need to handle updates to records in 
 memory here}
 END;

 PROCEDURE RemoveLib;{var whichFile : LmirHdl);}
 VAR
 ReturnValidHdl : LmirHdl;
 BEGIN
 IF whichFile^^.changed THEN
 HandleChanges(whichFile);
 IF whichFile^^.status = Open THEN
 IOCheck(FSClose(whichFile^^.fRefNum));
 ReturnValidHdl := whichFile^^.next;
 whichFile^^.prev^^.next := whichFile^^.next;
 whichFile^^.next^^.prev := whichFile^^.prev;
 whichFile^^.status := Closed;
 UpdateResource(handle(whichFile));
 ReleaseResource(handle(whichFile));
 whichFile := ReturnValidHdl;
 END;

 PROCEDURE CloseLibList; {Top : LmirHdl; }
 VAR
 next : LmirHdl;
 BEGIN
 next := Top^^.next;
 REPEAT
 RemoveLib(next);
 UNTIL next = Top;
 RemoveLib(Top);
 END;

 FUNCTION OpenWD; {var vREfNum : integer; }
 { DirID : longint)  }
 {  : OSErr;   }
 VAR
 blk : WDPBRec;
 Result : OSErr;
 BEGIN
 blk.ioCompletion := NIL;
 Result := PBGetVol(@blk, false); 
 {this just sets ioWDProcID to whatever...}
 IF Result = NoErr THEN
 BEGIN
 WITH blk DO
 BEGIN
 ioNamePtr := NIL;
 ioVREfNum := vRefNum;
 ioWDDirID := DirID;
 END;
 Result := PBOPenWD(@blk, false);
 vRefNum := blk.ioVRefNum;
 END;
 OpenWD := Result;
 END;

 PROCEDURE GetPathInfo; { vRefNum : integer; }
 { var rootVol : Str31; }
 { var hfsFlag : boolean );}
 {var WDirID : longint;}
 VAR
 blk : CInfoPBRec;
 volBlk : HParamBlockRec;
 dirname : str255;
 BEGIN
 rootVol := ‘’;
 WITH volBlk DO
 BEGIN
 ioCompletion := NIL;
 ioNamePtr := @rootVol;
 ioVRefNum := vRefNum;
 ioVolindex := 0;
 ioVSigWord := 0;
 IOCheck(PBHGetVINfo(@volBlk, false));
 END;
 rootVol := Concat(rootVol, ‘:’);
 hfsFlag := HFSRunning;
 IF hfsFlag THEN
 WITH blk DO
 BEGIN
 ioCompletion := NIL;
 dirname := ‘’;
 ioNamePtr := @dirname;
 ioVRefNum := vRefNum;
 ioFDirINdex := -1;
 ioDrDirID := 0;
 IOCheck(PBGetCatINfo(@blk, false));
 WDirId := ioDrDirID;
 END;
 END;

 FUNCTION CreateLib; {newLib : LmirHdl; prompt : boolean) : boolean}
 CONST
 null = ‘’;
 VAR
 Result : OSERR;
 BEGIN
 CreateLib := False;
 WITH newLib^^ DO
 BEGIN
 Filename := StandardFile(StandardPut, ‘Make My Day’, ‘LMIR’, vref);
 IF Filename <> null THEN
 BEGIN
 Result := Create(FileName, vRef, ‘DAVE’, ‘LMIR’);
 IF Result = NoErr THEN
 BEGIN
 GetPathInfo(vRef, volName, hfsvolume, DirID);
 CreateLib := True;
 END
 ELSE
 IOCheck(Result);
 END
 END
 END;

 FUNCTION UserWantsToCreateLib : boolean;
 CONST
 yes = 1;
 VAR
 p1, p2, p3, p4 : str80;
 Response : integer;
 BEGIN
 p1 := ‘Create a new library? ‘;
 p2 := ‘’;
 p3 := ‘’;
 p4 := ‘’;
 ParamText(p1, p2, p3, p4);
 InitCursor;
 Response := CautionAlert(301, NIL);
 IF (Response = Yes) THEN
 UserWantsToCreateLib := true
 ELSE
 UserWantsToCreateLib := false;
 END;

 FUNCTION FindLib; {var  : theLib : LmirHdl; prompt : boolean; result 
: OSErr) : boolean}
 CONST
 null = ‘’;
 VAR
 dummy : OSERR;
 SaveRef : integer;
 BEGIN
 FindLib := False;
 WITH theLib^^ DO
 BEGIN
 Filename := StandardFile(StandardGet, ‘’, ‘LMIR’, vref);
 IF FileName <> null THEN
 BEGIN
 GetPathInfo(vRef, volName, hfsvolume, DirID);
 FindLib := True;
 END;
 END;
 END;

 FUNCTION UserWantsToFindLib (whichLib : LmirHdl;
 Reference : Str255;
 errorCode : OSErr) : boolean;
 CONST
 yes = 1;
 VAR
 p1, p2, p3, p4 : str80;
 Response : integer;
 UseName : str63;
 BEGIN
 IF whichLib^^.filename = ‘’ THEN
 UseName := Reference
 ELSE
 UseName := whichLib^^.filename;
 p1 := ConCat(‘The ‘, UseName, ‘ File was not opened because ‘);
 p2 := GetErrorMsg(ErrorCode);
 p3 := ‘Look for a library to open?  ‘;
 p4 := ‘’;
 ParamText(p1, p2, p3, p4);
 InitCursor;
 Response := CautionAlert(301, NIL);
 IF (Response = Yes) THEN
 UserWantsToFindLib := true
 ELSE
 UserWantsToFindLib := false;
 END;

 PROCEDURE GetUserHelp (whichLibrary : LmirHdl;
 ReferredToAs : str255;
 ErrMsg : OSErr);
 VAR
 Intent, Attainment, Cancelled : boolean;
 BEGIN
 whichLibrary^^.status := NotFound; 
 {Guilty until proven innocent}
 HLock(Handle(whichLibrary));

 IF UserWantsToFindLib(whichLibrary, ReferredToAs, ErrMsg) THEN
 IF FindLib(whichLibrary) THEN
 BEGIN
 UpdateResource(Handle(whichLibrary));
 whichLibrary^^.status := Closed;
 END;
 IF whichLibrary^^.status = NotFound THEN
 {User chose not to Open Existing File}
 REPEAT
 Intent := UserWantsToCreateLib;
 IF Intent THEN
 Attainment := CreateLib(whichLibrary);
 IF Intent AND Attainment THEN
 BEGIN
 UpdateResource(Handle(whichLibrary));
 whichLibrary^^.status := Closed;
 END;
 UNTIL (NOT Intent) OR (Attainment);
 HUnLock(Handle(whichLibrary));
 END;

 FUNCTION LibOpenedSuccessfully (LibToOpen : LmirHdl; VAR Result : OSErr) 
: boolean;
 VAR
 fRefNum : integer;
 SaveCurrentvol : integer;
 Success : boolean;
 Ignore : OSErr;
 BEGIN
 Success := False;
 Ignore := GetVol(NIL, SaveCurrentVol);
 {Save the default volume }
 MoveHHI(Handle(LibToOpen));
 HLock(Handle(LibToOpen));
 WITH LibToOpen^^ DO
 BEGIN
 result := SetVol(@volname, 0);
 {Is the root volume mounted?}
 IF Result = NoErr THEN
 result := GetVol(NIL, vRef);
 {Then make it default }
 IF (Result = NoErr) AND hfsVolume THEN
 {Open the Working Directory}
 Result := OpenWD(vRef, DirID);
 IF Result = NoErr THEN
 {Vref is now correct whether HFS or MFS}
 Result := FSOpen(fileName, vRef, fRefNum);
 IF Result = NoErr THEN
 BEGIN
 Success := True;
 status := open;
 END;
 END;
 HUnLock(Handle(LibToOpen));
 LibOpenedSuccessfully := Success;
 Ignore := SetVol(NIL, SaveCurrentVol);
 {Restore the original default volume}
 END;

 PROCEDURE InitLibResource (VAR Lib : LmirHdl;
 LibName : str255);

 BEGIN
 Lib := LmirHdl(newHandle(SizeOf(Lmir)));
 Lib^^.RsrcId := uniqueID(‘LMIR’);
 WITH Lib^^ DO
 BEGIN
 vRef := 0;
 RecsOnFile := 0;
 filename := ‘’;
 volname := ‘’;
 DirID := 0;
 FTyp := 0;
 RecsOnFile := 0;
 CurRec := 0;
 status := NotFound;
 changed := false;
 END;
 AddResource(Handle(Lib), ‘LMIR’, Lib^^.RsrcID, LibName);
 END;

 PROCEDURE GetLibraryResource; {var theLibrary : LmirHdl;  ResourceName 
: str255}
 BEGIN
 IF NewRoms THEN
 theLibrary := LmirHdl(Get1NamedResource( ‘LMIR’, ResourceName))
 ELSE
 theLibrary := LmirHdl(GetNamedResource( ‘LMIR’, ResourceName));
 END;

PROCEDURE OpenLib; {var whichLib : LmirHdl; itsName : str255}
 VAR
 Result : OSErr;
 BEGIN
 GetLibraryResource(whichLib, itsName);
 IF whichLib = NIL THEN
 InitLibResource(whichLib, itsName);
 {No resource even exists... Create one}

{Potential Problem #1 - The resource was *just* created by GetLibrary}
 IF whichLib^^.status = NotFound THEN 
 {A resource exists, but no file }
 GetUserHelp(whichLib, itsName, 43);
{Potential Problem #2 - The resource is there but the file couldn’t be 
opened}
 WHILE (whichLib^^.status = Closed) AND (NOT LibOpenedSuccessfully(whichLib, 
result)) DO
 GetUserHelp(whichLib, itsName, Result);

{Note: if the user refuses to either look for or create a file, then 
status will be set to NotFound}
{and the loop ends.  Of course, the loop also ends if a file is opened 
successfully.  }
 whichLib^^.next := whichLib;
 whichLib^^.prev := whichLib;
 END;

 PROCEDURE OpenLinkedLib; {LinkTo : LmirHdl;}
 { ResName : str255);}
 VAR
 nwLib : LmirHdl;
 BEGIN
 OpenLib(newLib, ResName);
 newLib^^.next := LinkTo^^.next;
 LinkTo^^.next := newLib;
 newLib^^.prev := LinkTo;
 newLib^^.next^^.prev := newLib;
 END;
END.

Fig. 2 Our demo puts up a standard file dialog

Fig. 3 Link Procedure


{2}
PROGRAM LibMgrDemo;
{$I-    Lightspeed Compiler Command}
 USES
 LibMgr;
 VAR
 TopFile : LmirHdl;

 PROCEDURE InitThings;
 BEGIN
 InitGraf(@thePort); {grafport for the screen}
 MoreMasters;
 MoreMasters;
 MoreMasters;
 InitFonts;
 InitWindows;
 InitMenus;
 TEInit;
 InitDialogs(NIL);
 FlushEvents(everyEvent, 0);
 InitCursor;
 END;

 PROCEDURE SetUpThings;
 BEGIN
 OpenLib(TopFile, ‘Application Default Library’); 
 {When program boots}
 OpenLinkedLib(TopFile, ‘User Default Library’); 
 {When program boots}
 END;

 PROCEDURE DisplayMsg2;
 VAR
 p1, p2, p3, p4 : str80;
 status : ARRAY[0..3] OF str80;
 x : integer;
 BEGIN
 Status[0] := ‘Open’;
 Status[1] := ‘Closed’;
 Status[3] := ‘Not Found’;
 x := Ord(TopFile^^.status);
 p1 := Concat(TopFile^^.filename, ‘ is ‘, Status[x], ‘.  ‘);
 x := Ord(TopFile^^.next^^.status);
 p2 := Concat(TopFile^^.next^^.filename, ‘ is ‘, Status[x], ‘.  ‘);
 p3 := ‘                                                             
  ‘;
 p4 := ‘Would you like a nice cool glass of lemonade?  ‘;
 ParamText(p1, p2, p3, p4);
 x := CautionAlert(301, NIL);
 END;

 PROCEDURE DisplayMsg;
 VAR
 next : LmirHdl;
 newRect : Rect;
 BEGIN
 SetRect(newRect, 80, 40, 430, 200);
 SetTextRect(newRect);
 ShowText;
 next := TopFile;
 REPEAT
 writeln(‘   The File ‘, next^^.filename, ‘ is ‘, next^^.status);
 next := next^^.next;
 UNTIL next = topfile;
 Writeln(‘   Press mouse button to close files and exit’);
 WHILE NOT button DO
 ;
 END;

 PROCEDURE CloseThings;
 BEGIN
 CloseLibList(TopFile);
 END;

BEGIN
 InitThings;
 SetUpThings;
 DisplayMsg;
 CloseThings;
END.

LibMgrRsrc.Rel

Type DITL

     ,301
3
*   1
Button Enabled
112 108 132 168
Yes

*   2
Button Enabled
112 246 132 306
No

*   3
StaticText Enabled
9 69 104 337
^0^1^2^3

Type ALRT
     ,301
60 60 210 420
301
CCCC
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Garmin Express 4.0.18.0 - Manage your Ga...
Garmin Express is your essential tool for managing your Garmin devices. Update maps, golf courses and device software. You can even register your device. Update maps Update software Register your... Read more
jAlbum Pro 12.6.4 - Organize your digita...
jAlbum Pro has all the features you love in jAlbum, but comes with a commercial license. With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code!... Read more
jAlbum 12.6.4 - Create custom photo gall...
With jAlbum, you can create gorgeous custom photo galleries for the Web without writing a line of code! Beginner-friendly, with pro results Simply drag and drop photos into groups, choose a design... Read more
Microsoft Remote Desktop 8.0.16 - Connec...
With Microsoft Remote Desktop, you can connect to a remote PC and your work resources from almost anywhere. Experience the power of Windows with RemoteFX in a Remote Desktop client designed to help... Read more
Spotify 1.0.4.90. - Stream music, create...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more
djay Pro 1.1 - Transform your Mac into a...
djay Pro provides a complete toolkit for performing DJs. Its unique modern interface is built around a sophisticated integration with iTunes and Spotify, giving you instant access to millions of... Read more
Vivaldi 1.0.118.19 - Lightweight browser...
Vivaldi browser. In 1994, two programmers started working on a web browser. Our idea was to make a really fast browser, capable of running on limited hardware, keeping in mind that users are... Read more
Stacks 2.6.11 - New way to create pages...
Stacks is a new way to create pages in RapidWeaver. It's a plugin designed to combine drag-and-drop simplicity with the power of fluid layout. Features: Fluid Layout: Stacks lets you build pages... Read more
xScope 4.1.3 - Onscreen graphic measurem...
xScope is powerful set of tools that are ideal for measuring, inspecting, and testing on-screen graphics and layouts. Its tools float above your desktop windows and can be accessed via a toolbar,... Read more
Cyberduck 4.7 - FTP and SFTP browser. (F...
Cyberduck is a robust FTP/FTP-TLS/SFTP browser for the Mac whose lack of visual clutter and cleverly intuitive features make it easy to use. Support for external editors and system technologies such... Read more

This Week at 148Apps: April 20-24, 2015
The Apps of April How do you know what apps are worth your time and money? Just look to the review team at 148Apps. We sort through the chaos and find the apps you're looking for. The ones we love become Editor’s Choice, standing out above the many... | Read more »
Discover Your Reflexes With Minimalist G...
Discover O, BYOF Studios, may look simple at first with its' color matching premise and swipe controls, but as you speed up the task becomes more daunting. [Read more] | Read more »
Here's Another Roundup of Notable A...
Now that the Apple Watch is publically available (kind of), even more apps and games have been popping up for it. Some of them are updates to existing software, others are brand new. The main thing is that they're all for the Apple Watch, and if you... | Read more »
Use Batting Average and the Apple Watch...
Batting Average, by Pixolini, is designed to help you manage your statistics. Every time you go to bat, you can use your Apple Watch to track  your swings, strikes, and hits. [Read more] | Read more »
Celebrate Studio Pango's 3rd Annive...
It is time to party, Pangoland pals! Studio Pango is celebrating their 3rd birthday and their gift to you is a new update to Pangoland. [Read more] | Read more »
Become the World's Most Important D...
Must Deliver, by cherrypick games, is a top-down endless-runner witha healthy dose of the living dead. [Read more] | Read more »
SoundHound + LiveLyrics is Making its De...
SoundHound Inc. has announced that SoundHound + LiveLyrics, will be one of the first third-party apps to hit the Apple Watch. With  SoundHound you'll be able to tap on your watch and have the app recognize the music you are listening to, then have... | Read more »
Adobe Joins the Apple Watch Lineup With...
A whole tidal wave of apps are headed for the Apple Watch, and Adobe has joined in with 3 new ways to enhance your creativity and collaborate with others. The watch apps pair with iPad/iPhone apps to give you total control over your Adobe projects... | Read more »
Z Steel Soldiers, Sequel to Kavcom'...
Kavcom has released Z Steel Soldiers, which continues the story of the comedic RTS originally created by the Bitmap Brothers. [Read more] | Read more »
Seene Lets You Create 3D Images With You...
Seene, by Obvious Engineering, is a 3D capture app that's meant to allow you to create visually stunning 3D images with a tap of your finger, and then share them as a 3D photo, video or gif. [Read more] | Read more »

Price Scanner via MacPrices.net

Final day for Best Buy’s 4 Day Sale: Up to $3...
Best Buy has the new 2015 13″ 1.6GHz/128GB MacBook Air on sale for $799.99 through April 27th. That’s $200 off MSRP, and it’s the lowest price available for this new model from any reseller. Choose... Read more
College Student Deals: Additional $50 off Mac...
Take an additional $50 off all MacBooks and iMacs at Best Buy Online with their College Students Deals Savings, valid through May 9, 2015. Anyone with a valid .EDU email address can take advantage of... Read more
Save up to $300 on a new Mac, $30 on an iPad,...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Zoho Business Apps for Apple Watch Put Select...
Pleasanton, California based Zoho has launched Zoho Business Apps for Apple Watch, a line of apps that extends Zoho business applications to let users perform select functions from an Apple Watch.... Read more
Universal Stylus Initiative Launched to Creat...
OEMs, stylus and touch controller manufacturers have announced the launch of Universal Stylus Initiative (USI), a new organization formed to develop and promote an industry specification for an... Read more
Amazon Shopping App for Apple Watch
With the new Amazon shopping app for Apple Watch, Amazon customers with one of the wearable devices can simply tap the app on the watch to purchase items in seconds, or save an idea for later. The... Read more
Mac Pros on sale for up to $260 off MSRP
B&H Photo has Mac Pros on sale for up to $260 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2799, $200 off MSRP - 3.5GHz 6-core Mac Pro: $3719.99... Read more
Apple refurbished Time Capsules available for...
The Apple Store has certified refurbished Time Capsules available for $100 off MSRP. Apple’s one-year warranty is included with each Time Capsule, and shipping is free: - 2TB Time Capsule: $199, $100... Read more
Intel Compute Stick: A New Mini-Computing For...
The Intel Compute Stick, a new pocket-sized computer based on a quad-core Intel Atom processor running Windows 8.1 with Bing, is available now through Intel Authorized Dealers across much of the... Read more
Heal to Launch First One-Touch House Call Doc...
Santa Monica, California based Heal, a pioneer in on-demand personal health care services — will offer the first one-touch, on-demand house call doctor app for the Apple Watch. Heal’s Watch app,... Read more

Jobs Board

*Apple* iOS Specialist - TCML (United States...
AppleiOS Specialist Senior Apple /iOS Administrator/Engineer responsible for configuration and distribution of desktop, laptop and mobile devices to State Employees Read more
*Apple* Client Systems Solution Specialist -...
…drive revenue and profit in assigned sales segment and/or region specific to the Apple brand and product sets. This person will work directly with CDW Account Managers Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Support Technician IV - Jack Henry a...
Job Description Jack Henry & Associates is seeking an Apple Support Technician. This position while acting independently, ensures the proper day-to-day control of Read more
*Apple* Client Systems Solution Specialist -...
…drive revenue and profit in assigned sales segment and/or region specific to the Apple brand and product sets. This person will work directly with CDW Account Managers Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.