TweetFollow Us on Twitter

Standard Search Unit
Volume Number:7
Issue Number:10
Column Tag:Pascal Forum

Standard Search Unit

By Ed Eichman, Mak Jukie, B & E Software, Germany

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

[Ed & Mak work as programmers for B & E Software, makers of the program RagTime 3. Ed is a former musician who was able to find another job that would let him sleep till noon every day, and Mak will not admit to having done anything prior to writing this article]

The Concept

While working on the latest release of RagTime 3, we realized that we needed a better way to organize the extra files that ship with the program. RagTime is an integrated page layout/spreadsheet/text processing/graphing/picture program which supports text import and direct scanning into the program via external code resources. The combination of scanner drivers, text filters, one or more dictionaries, PPD files, etc. would make for a large mess inside the users system folder, so we decided to use a standard folder into which all auxiliary files could be kept, and shared with other programs if desired.

This led to the development of a standard search unit (SSU) for our files. The SSU looks for a folder (for the remainder of this article, we will refer to this folder as the ‘Company Folder’) in which all the files which are needed by the program are kept. This folder will first be searched for in the folder from which the application was launched, and then in the current system folder. The first folder found with the specified name will then become the target folder. The SSU will not only access the files within the ‘Company Folder’, but also recursively check all folders within the ‘Company Folder’ hierarchy. This will allow the user to organize the target folder in any way that is convenient.

This folder could have a number of uses. If you are writing a number of applications that all need to use a dictionary, for example, you could place one dictionary into the ‘Company Folder’ in the system folder, and have all the applications access this folder. You could also define a interface for external code resources, and just pop them into the ‘Company Folder’ to access them while the program is running. Finally, the SSU can keep track of multiple resources in one or more files. This could be handy if, for instance, you have a game which uses a number of large sound resources; these resources could be put in one file and only used by the program if the user chooses to put them on his or her hard disk in the appropriate folder.

Set Up

In order to use the SSU, you must first call InitSSU to set up three global variables. The first, gCompanyFStruct, is initialized to NIL and will eventually contain the hierarchy within the ‘Company Folder’. Next, gAppWDRefNum and gSysWDRefNum will be initialized to contain the Working Directory Reference Number of the folder containing the current application and the current System Folder, respectively.

The List

The SSU stores information about specified files or resources contained in the company folder in a linked list. This list contains records of type ItemInfoRec which contains the following information:

il_nextItemInfo is of type ItemInfoHdl and contains a handle to the next record in the linked list. When this field is NIL, it indicates that you are at the end of the list.

il_privData is provided to the caller of the SSU to store any extra data. The caller may use this field in any way; usually to point to a block of memory that holds information about the item in the list. The best time to enter data in this field is during a call to your CallIdentifyProc (see below).

Please read the description of the killPrivData Procedure (below) to get a better understanding of when you can expect the il_privData field to change.

il_resourceType contains the resource type if this item is a resource, and will be NIL otherwise.

il_resID contains the resource number if this item is a resource, and will be NIL otherwise.

il_fileType will contain the file type of the file in the list, or the file type of the file which contains the specified resource.

il_fileSpec contains the vRefnum, the dirID, and the file name of either the file in the list or the file which contains the specified resource.

il_nickName may contain an alternate name for the item. For instance, if you request a list that contains all the sound resources from a particular file, this field will either contain the resource name, or the type and number of the specified resource if the resource has no name (i.e. ‘snd resource number 42’).

The SSU is capable of creating multiple lists from the ‘Company Folder’. For instance, in RagTime 3 we keep a list of all scanner drivers and a separate list of all text import filters which are available. Both list are created from one folder.

How a List is built

The list is created on the first call to SSU_BuildList:

{1}

PROCEDURE SSU_BuildList(VAR biiList BuildItemInfoAPT; numTypes:Integer; 
VAR result:SSUInfoRec; killPrivData:ProcPtr);

This procedure should be called each time you want to use the list. The first time SSU_BuildList is called, it puts the tick count of the last modification date of the ‘Company Folder’ into a field from gCompanyFStruct (this global is used by all list which the SSU builds). Thereafter, each time you call SSU_BuildList and pass in the list created on the first call to the procedure, it determines if the list must be rebuilt (based on comparing the previous modification tick count to the current one) and rebuilds the list if necessary.

biiList is an array of type BuildItemInfoAPT. Each member of the array contains criteria to decide if a particular file/resource should be added to the current list. Each search criteria must contain at least a file type. Files /resources for the list can then be chosen by creator, resource type, file name, or any combination of the three.

Additionally, any files that match the above search criteria can be passed to a caller supplied procedure (passed in the bi_procAddr as a ProcPtr) which can either accept or reject the file/resource, and/or which can be used to setup the il_privData field. The procedure should have the following setup:

{2}

PROCEDURE CallIdentifyProc(VAR theName: Str255; theResH:Handle; theID:Integer; 
VAR privData:LongInt);

theName will either be the file name or the il_nickName field from the FileInfoRec if the item in question is a resource. This argument is guaranteed not to be empty upon entry to this procedure. If the CallIdentifyProc wants to reject the item, an empty string should be returned here. The name may also be changed if a different name is desired.

If the current item is a resource, theResH will contain a handle to the resource, and theID will contain the resID. If the current item is a file, theResH will be NIL and theID will contain the file reference number of the file.

privData is a LongInt which will be 0 on entry, and may be used in any way by the application. This value will be put in the il_privData field of the ItemInfoRecord.

The next argument to the SSU_BuildList procedure is numTypes, which must contain the number of BuildItemInfoRT records that are being passed in the array.

The result argument must be cleared before calling this routine for the first time. Thereafter, result must contain the SSUInfoRec returned on the previous call to SSU_BuildList for the list in question. The argument result can be cleared with a call to BlockClear from the SSU like this:

{3}

BlockClear(@result, SizeOf(result));

BlockClear can be useful in a number of other situations (such as clearing a param block before giving it to the low level file manager calls), and the code for the call is listed below:

{4}

PROCEDURE BlockClear(thePtr:UNIV Ptr; count:Integer);
BEGIN
 WHILE count > 0 DO BEGIN
 count := count - 1;
 thePtr^ := 0;
 thePtr := Ptr(LongInt(thePtr)+1);
 END;
END;

Finally, in case the list should need to be rebuilt, your call to SSU_BuildList should include a ProcPtr to a procedure that can dispose of any private data that you have set up. The setup for your procedure must be:

{5}

PROCEDURE KillPrivData(VAR pData:LongInt);

Please notice that your private data could be destroyed any time that you call SSU_BuildList. If the SSU determines the list must be rebuilt (based on the tick count, described above), it totally destroys the old list before building the new list.

If you have not allocated any privData, this argument can be set to NIL.

Using items from a List

The list may contain both files and resources. However, the SSU only opens or closes files from the list, or files which contain the resource in the item list. These files may be opened with a call to the function SSU_OpenFile:

{6}

FUNCTION SSU_OpenFile(theItemInfoHdl: ItemInfoHdl; VAR fileRefNum:Integer; 
permission:Byte; openResFork: Boolean):OSErr;

theItemInfoHdl must contain a handle to the item that you want to open (if the item is a resource, all needed info to open the file is found in its ItemInfoHdl; you don’t have to create a separate handle for a call to SSU_OpenFile). The argument fileRefNum will be zeroed by the SSU before being used by the function, and will return the file reference number of the opened file. The permission argument specifies the access privileges that you are requesting for the file to be opened. The constants for this argument may be found in Inside Mac, Volume 2, page 100.

The openResFork boolean should be set to TRUE if you want the resource fork rather than the data fork of the file to be opened. Setting this boolean to TRUE is required if you are trying to access an item which is a resource, but could also be set to TRUE if you want to access resources from a file in the item list. Finally, the SSU_OpenFile function returns an OSErr indicating the success or failure of the function.

Each call to SSU_OpenFile should be offset by a call to SSU_CloseFile after you are done using the file/resource.

{7}

FUNCTION SSU_CloseFile(VAR fileRefNum: Integer; closeResFork:Boolean):OSErr;

This routine closes the file opened by SSU_OpenFile. The argument fileRefNum should be set to the file reference number returned in the call to SSU_OpenFile, and closeResFork should have the same value that was originally used for SSU_OpenFile.

fileRefNum is declared a VAR variable so that the file reference number of the file can be set to zero when the file is closed. SSU_CloseFile first checks to make sure fileRefNum is not zero before trying to close the file, so that multiple calls to SSU_CloseFile for the same file will not cause errors.

Disposing of a List

After you are done using a list, the memory allocated to the maintenance of the list must be disposed of. This is accomplished with a call to SSU_DisposList:

{8}

PROCEDURE SSU_DisposList(VAR theResult: SSUInfoRec; killPrivData:ProcPtr);

theResult is passed as a VAR parameter so that it may be set to NIL after the list is disposed of. This way, if theResult is passed again to SSU_BuildList, the SSU will build a new list instead of dying painfully.

The KillPrivData ProcPtr is explained above in the description of SSU_BuildList.

It should be noted here that a list should only be disposed of when you are completely done using it, or when you can not afford the memory that the list takes up, since rebuilding the list can be time consuming. Depending on the amount of files in the ‘Company Folder’ and the complexity of your CallIdentifyProc, the list could take a couple of seconds to build on each call to SSU_BuildList.

Depending on your application, it might be worth setting up a global SSUInfoRec for each of the lists that you will be using. This way, when you call SSU_BuildList, the list will only be rebuilt if the ‘Company Folder’ modification tick count has changed.

Possible additions

One of the first things you will notice when displaying a list from the SSU is that the items are not sorted. If you plan to use the list for display, a sorting procedure is a must.

Additionally, the SSU will be more than happy to add an identical name to the list if one is found. If this occurs with your list, you may want to check which of the two identical files/resources is the one you really need, and eliminate the other. This would usually be done by checking the version resource of a file to see which of the files is newer.

Another good idea might be to add the searching, not just of the ‘Company Folder’, but also of the System Folder and the folder from which the application was launched. Most of the work for this addition is already done for you in the routine SearchDirectory. Just be sure to set the recursive argument to FALSE, or you will end up searching the ‘Company Folder’ a second time.

Finally, you may want to have mercy on your mortal users by being a little flexible with the naming of the ‘Company Folder’. If you find something that is fairly close (i.e. ‘Dompany Folder’), you could put up a message asking if that is the correct folder, and warning them that you will rename the folder to the correct name.

UNIT SSU;

INTERFACE

USES
 MemTypes, OSIntf, ToolIntf, Packages;

TYPE
 NickNameT= Str63; {Maximum length of an 
 alternate name}
 
 CanonFileSpec = RECORD 
 cf_vRefNum : Integer;
 cf_dirID : LongInt;
 cf_fileName: Str63;
 END;

 ItemInfoHdl= ^ItemInfoPtr;
 ItemInfoPtr= ^ItemInfoRec;
 ItemInfoRec= RECORD
 il_nextItemInfo:ItemInfoHdl; {If NIL,last entry} 
 il_privData: LongInt;  {User definable}
 il_resourceType : OSType;{Resource OS Type  }
 il_resID : Integer; {Resource ID  }
 il_fileType: OSType;{File type    }
 il_fileSpec: CanonFileSpec;{dirId +
 vRefNum + fileName}
 il_nickName: NickNameT;  {Alternate name    }
 END;
 
 SSUInfoPtr = ^SSUInfoRec;
 SSUInfoRec = RECORD
 ssu_folderTime  : LongInt; 
 {Copy of DirInfoArr.di_time}
 ssu_firstItemInfo : ItemInfoHdl;  
 {Handle to the first item in the list }
 END;
 
 BuildItemInfoRT = RECORD
 bi_fileType: OSType;{File type. Required          }
 bi_crtrType: OSType;{Creator. Can be NIL          }
 bi_resType : OSType;{Res type. Can be NIL   }
 bi_fileName: StringPtr;{File name. Can be NIL     }
 bi_procAddr: ProcPtr;  {ID routine. Can be NIL    }
 END;
 
 BuildItemInfoAPT= ^BuildItemInfoAT;
 BuildItemInfoAT = ARRAY [1..4] OF BuildItemInfoRT;
 

PROCEDURE InitSSU;

PROCEDURE SSU_BuildList(  biiList:BuildItemInfoAPT; 
 numTypes:Integer; 
 VAR result:SSUInfoRec; killPrivData:ProcPtr);

PROCEDURE SSU_DisposList(VAR result:SSUInfoRec; 
 killPrivData:ProcPtr);

FUNCTIONSSU_OpenFile(theItemInfoHdl:ItemInfoHdl;
 VAR fileRefNum:Integer;
 permission:Byte;
 openResFork:Boolean):OSErr;

FUNCTIONSSU_CloseFile(  VAR fileRefNum:Integer;
 closeResFork:Boolean):OSErr;

PROCEDURE BlockClear(thePtr:UNIV Ptr;
 count:Integer);

VARgAppWDRefNum  : Integer; 
 {App working directory  refnum
 }
 gSysWDRefNum  : Integer; 
 {System Folder working directory refnum     }


IMPLEMENTATION
{$S SegSSU} {••••••••••••••••••••••••••••••••••••••••••••••••••}

TYPE
 DirectoryInfoRec = RECORD
 di_dirModTime : LongInt; {Mod date of dir                     }
 di_dirID : LongInt; {Directory ID }
 di_vRefNum : Integer;  {Volume ref number         }
 END;
 
 DirInfoArrHdl = ^DirInfoArrPtr;
 DirInfoArrPtr = ^DirInfoArr;
 DirInfoArr = RECORD
 di_time: LongInt; 
 {Time in ticks when this structure was built}
 di_count : Integer; {Number of entries in list    }
 di_list: ARRAY [1..512] OF DirectoryInfoRec;            
 END;

CONST
 kDirInfoArrHeadSize = 
 SizeOf(LongInt) + SizeOf(Integer);
 kCompanyFName   = ‘Company Folder’;
 eternity = False;
 
VARgCompanyFStruct : DirInfoArrHdl;

PROCEDURE CallIdentifyProc(VAR theName:Str255;
 theCodeH:Handle;
 theResID:Integer;
 VAR thePrivData:LongInt;
 theProcAddr:ProcPtr);
 INLINE $205F,   { MOVE.L (A7)+,A0 }
 $4E90; { JSR    (A0)}

PROCEDURE CallKillPrivData(VAR privData:LongInt;
 theProcAddr:ProcPtr);
 INLINE $205F,   { MOVE.L (A7)+,A0 }
 $4E90; { JSR    (A0)}

{Clears a block of memory of a specified size}
PROCEDURE BlockClear(thePtr:UNIV Ptr;  count:Integer);
BEGIN
 WHILE count > 0 DO BEGIN
 count  := count - 1;
 thePtr^  := 0;
 thePtr := Ptr(LongInt(thePtr)+1);
 END;
END;

{CleanDisposHandle works like DisposHandle but has two advantages. 1) 
If a handle is NIL, DisposHandle is not called and 2) If h is part of 
a dereferenced relocatable block, CleanDisposHandle first makes a copy 
of the handle before calling DisposHandle, avoiding a possible HEAP error 
 }
PROCEDURE CleanDisposHandle(VAR h:UNIV Handle);
VARtemp : Handle;
BEGIN
 IF h <> NIL THEN BEGIN
 temp := h; 
 h := NIL;
 DisposHandle(temp);
 END;
END;

{Identical to FSClose, except that it will not try to close if dataForkRefNum 
is equal to 0 on entry; also, dataForkRefNum is set to 0 on exit
 }
FUNCTION CleanFSClose(VAR dataForkRefNum:Integer) 
 :OSErr;
VARtempRefNum  : Integer;
BEGIN
 IF dataForkRefNum <> 0 THEN BEGIN
 tempRefNum := dataForkRefNum;
 dataForkRefNum  := 0;
 CleanFSClose  := FSClose(tempRefNum);
 END ELSE 
 CleanFSClose  := noErr;
END;
 
{Identical to CloseResFile, except that it will not try to close if resForkRefNum 
is equal to 0 on entry; also, resForkRefNum is set to 0 on exit
 }
FUNCTION CleanCloseResFile(VAR resForkRefNum:Integer):OSErr;
VARtempRefNum  : Integer;
BEGIN
 IF resForkRefNum <> 0 THEN BEGIN
 tempRefNum := resForkRefNum;
 resForkRefNum   := 0;
 CloseResFile(tempRefNum);
 CleanCloseResFile := ResError;
 END ELSE 
 CleanCloseResFile := noErr;
END;

{Identical to ReleaseResource, except that it will not try to release 
if resHandle is equal to 0 on entry; also, resHandle is set to NIL on 
exit    }
FUNCTION CleanReleaseResource(VAR resHandle:UNIV   
 Handle):OSErr;
VARtempH: Handle;
BEGIN
 IF resHandle <> NIL THEN BEGIN
 tempH  := resHandle;
 resHandle  := NIL;
 ReleaseResource(tempH);
 CleanReleaseResource   := ResError;
 END ELSE 
 CleanReleaseResource   := noErr;
END;

{CleanOpenWD opens a working directory so that files contained therein 
can be accessed or searched.
‘vRefNum’ is the working directory reference                   
 number of either the Application folder                       
 or the System Folder. Additionally, when                      
 recursively searching the ‘Company  Folder’, this can also be the wdRefNum 
of an enclosed folder.
‘dirID’ working directory reference number of the              
 folder in vRefNum
‘mustCloseWD’returns TRUE if we opened the working             
 directory and FALSE if it was already opened. If ‘mustCloseWD’ is FALSE, 
we should not call ‘CleanCloseWD’ (see comments in CleanCloseWD). 
This function returns an operating system error          }
FUNCTION CleanOpenWD(vRefNum:Integer;
 dirID:LongInt;
 VAR wdRefNum:Integer;
 VAR mustCloseWD:Boolean):OSErr;
VARwdInfo : WDPBRec;
 tempErr: OSErr;
BEGIN
 BlockClear(@wdInfo, SizeOf(wdInfo));
 
{According to the Macintosh Tech notes #77 and #190, only those working 
directories that were created by the app should be closed by the app. 
This routine checks if working directory for the specified volume/dirID 
has been already created by somebody. (perhaps by the Finder or any other 
application running under MultiFinder), and returns FALSE in mustCloseWD 
if we didn’t open it.}
 REPEAT
 wdInfo.ioVRefNum:= vRefNum;
 wdInfo.ioWdIndex:= wdInfo.ioWdIndex + 1;
 wdInfo.ioWDProcID := 0;
 wdInfo.ioWDVRefNum:= 0;
 tempErr := PBGetWDInfo(@wdInfo, False);
 UNTIL (tempErr <> noErr) | (wdInfo.ioWDDirID =                
 dirID);
 
 mustCloseWD     := tempErr <> noErr;
 
 IF mustCloseWD THEN BEGIN
 tempErr := OpenWD(vRefNum, dirID, LongInt(‘ERIK’), wdRefNum);
 IF tempErr <> noErr THEN 
 wdRefNum := 0;
 CleanOpenWD   := tempErr;
 END ELSE BEGIN
 CleanOpenWD   := noErr;
 wdRefNum := wdInfo.ioVRefNum;
 END;
END;

{CleanCloseWD closes a working directory that we opened.
‘wdRefNum’ is the reference number of the working              
 directory that should be closed
This function returns an operating system error.
WARNING:This function must never be called if the              
 variable mustCloseWD returned FALSE in                        
 call to CleanOpenWD }
FUNCTION CleanCloseWD(VAR wdRefNum:Integer):OSErr;
BEGIN
 IF wdRefNum <> 0 THEN BEGIN
 CleanCloseWD  := CloseWD(wdRefNum);
 wdRefNum := 0;
 END ELSE 
 CleanCloseWD  := noErr;
END;

{SSU_OpenFile opens file specified in ItemInfoHdl.
‘theItemInfoHdl’ is a handle to information about a            
 particular file
‘fileRefNum’returned file reference of the                     
 file ‘permission’ specifies access  privileges for the file   
 (Constants may be found in Inside Mac Volume 2 page 100)
‘openResFork’    if TRUE, the res fork should  be              
 opened instead of the data fork
This function returns an operating system error          }
FUNCTION SSU_OpenFile(theItemInfoHdl:ItemInfoHdl;
 VAR fileRefNum:Integer;
 permission:Byte;
 openResFork:Boolean):OSErr;
VARpbs  : HParamBlockRec;
 wdRefNum : Integer;
 tempErr: Integer;
 mustCloseWD: Boolean;
BEGIN
 fileRefNum := 0;
 IF theItemInfoHdl <> NIL THEN BEGIN
 IF openResFork THEN BEGIN
 IF Length
 (theItemInfoHdl^^.il_fileSpec.cf_fileName)  = 0   
 THEN BEGIN 
{This resource belongs to program resource fork}
 tempErr := 0;
 END ELSE BEGIN
 HLock(Handle(theItemInfoHdl));
 WITH theItemInfoHdl^^ DO BEGIN
 IF il_fileSpec.cf_dirID <> 0 THEN BEGIN                       
 tempErr := CleanOpenWD   (il_fileSpec.cf_vRefNum,             
 il_fileSpec.cf_dirID, wdRefNum,   mustCloseWD);
 
 IF tempErr = noErr THEN BEGIN
 fileRefNum := OpenRFPerm (StringPtr(@il_fileSpec.cf_fileName)^, 
 wdRefNum, permission);
 tempErr := ResError;
 
 IF tempErr <> noErr THEN BEGIN
 fileRefNum := 0;
 IF mustCloseWD & (CleanCloseWD    (wdRefNum) <> noErr) THEN ;
 END ELSE BEGIN
 IF mustCloseWD THEN
 tempErr :=  CleanCloseWD(wdRefNum);
 END;
 END;
 END ELSE BEGIN
 fileRefNum := OpenRFPerm (StringPtr(@il_fileSpec.cf_fileName)^, 
 il_fileSpec.cf_vRefNum, permission);
 tempErr := ResError;
 IF tempErr <> noErr THEN 
 fileRefNum := 0;
 END;
 END;
 HUnLock(Handle(theItemInfoHdl));
 END;
 END ELSE BEGIN
 IF Length  (theItemInfoHdl^^.il_fileSpec.cf_fileName) > 0     
 THEN BEGIN
 HLock(Handle(theItemInfoHdl));
 BlockClear(@pbs, SizeOf(pbs));
 WITH theItemInfoHdl^^ DO BEGIN
 pbs.ioNamePtr := @il_fileSpec.cf_fileName;
 pbs.ioVRefNum := il_fileSpec.cf_vRefNum;
 pbs.ioPermssn := permission;
 pbs.ioDirID:= il_fileSpec.cf_dirID; 
 END;
 tempErr := PBHOpen(@pbs, False);
 HUnLock(Handle(theItemInfoHdl));
 IF tempErr = noErr THEN 
 fileRefNum := pbs.ioRefNum;
 END ELSE tempErr := memFullErr;
 END;
 END ELSE tempErr := memFullErr;
 
 SSU_OpenFile := tempErr;
END;

{SSU_CloseFile closes file specified by fileRefNum
‘fileRefNum’file ref num of file to be closed
‘closeResFork’ if TRUE, then this routine closes the           
 res fork instead of the data fork
This function returns an operating system error          }
FUNCTION SSU_CloseFile( VAR fileRefNum:Integer;
 closeResFork:Boolean):OSErr;
BEGIN
 IF closeResFork THEN BEGIN
 SSU_CloseFile := CleanCloseResFile(fileRefNum);
 END ELSE BEGIN
 SSU_CloseFile := CleanFSClose(fileRefNum);
 END;
END;


FUNCTION GetDirModTime(vRefNum:Integer;                        
 directoryId:LongInt):LongInt;
VARmyCInfoPBRec  : CInfoPBRec;
BEGIN
 BlockClear(@myCInfoPBRec, SizeOf(myCInfoPBRec));
 myCInfoPBRec.ioVRefNum   := vRefNum;
 myCInfoPBRec.ioFDirIndex := -1;
 myCInfoPBRec.ioDrDirID   := directoryId;
 
 IF PBGetCatInfo(@myCInfoPBRec, False) = noErr THEN      BEGIN
 GetDirModTime := myCInfoPBRec.ioDrMdDat;
 END ELSE BEGIN 
 {could not get modTime for directory?  huh  }
 GetDirModTime := Random;
 END;
END;

PROCEDURE StuffDirInfo(vRefNum:Integer;                        
 dirID:LongInt);
VARcurrentInfo : DirectoryInfoRec;
 theTicks : LongInt;
BEGIN
 currentInfo.di_dirModTime:= GetDirModTime(vRefNum,            
 dirID);
 currentInfo.di_dirId:= dirID;
 currentInfo.di_vRefNum   := vRefNum;
 
 IF gCompanyFStruct = NIL THEN BEGIN
 theTicks := TickCount;
 gCompanyFStruct :=  DirInfoArrHdl(NewHandleClear
 (kDirInfoArrHeadSize));
 gCompanyFStruct^^.di_time := theTicks;
 END;

 SetHandleSize(Handle(gCompanyFStruct),                        
 ((gCompanyFStruct^^.di_count + 1)*  LongInt(SizeOf(DirectoryInfoRec))) 
 + kDirInfoArrHeadSize);
 WITH gCompanyFStruct^^ DO BEGIN
 di_count := di_count + 1;
 di_list[di_count] := currentInfo;
 END;
END;

{SearchForDirectory searches for “Company Folder” in the Working Directory 
specified by wdRefNum 
‘wdRefNum’is the working directory reference                   
 number of either the Application folder                       
 or the System Folder.
This function returns either the “Company Folder” directory ID, or zero 
if none is found }
FUNCTION SearchForDirectory(wdRefNum:Integer)                  
 :LongInt;
VARi    : Integer;
 dirIndex : Integer;
 len1   : Integer;
 tempName : Str255;
 searchName : Str255;
 cInfo  : CInfoPBRec;
BEGIN
 BlockClear(@cInfo, SizeOf(cInfo));
 
 dirIndex := 1;
 
 searchName := kCompanyFName;
 len1   := Length(kCompanyFName);
 
 REPEAT
 cInfo.ioNamePtr := @tempName;
 cInfo.ioDirID   := 0;
 cInfo.ioVRefNum := wdRefNum;
 cInfo.ioFDirIndex := dirIndex;
 
 IF PBGetCatInfo(@cInfo, False) = noErr THEN                   
 BEGIN
 IF BAnd(Integer(cInfo.ioFlAttrib), $010) <> 0                 
 THEN BEGIN { We’ve found a directory }
 IF (Length(tempName)=len1) THEN
 IF EqualString(tempName,searchName, False,True) THEN BEGIN
 SearchForDirectory := cInfo.ioDirID;
 LEAVE;
 END;   
 END;
 dirIndex := dirIndex + 1;
 END ELSE BEGIN 
{got error, so it looks like we did not find it}
 SearchForDirectory := 0;
 LEAVE;
 END;
 UNTIL eternity;
END;

FUNCTIONMin(a,b:Integer):Integer;
BEGIN
 IF a < b THEN 
 Min := a 
 ELSE 
 Min := b;
END;

PROCEDURE SSU_BuildList(  biiList:BuildItemInfoAPT;            
 numTypes:Integer;
 VAR result:SSUInfoRec;
 killPrivData:ProcPtr);

VAR   i : Integer;
 dummyOSErr : OSErr;
 buildFolderInfo : Boolean;
 cInfo  : CInfoPBRec;
 currFileName    : Str255;
 theResCount: Integer;
 resHdl : Handle;
 theName: NickNameT;
 theResID : Integer;
 theResType : ResType;
 theResName : STR255;
 thePrivData: LongInt;
 noTypeFlag : Boolean;
 
 PROCEDURE  SearchDirectory(theVRefNum:Integer;                
 theDirID:LongInt; 
 recursive:Boolean);
 VAR  dirIndex   : Integer;
 jj: Integer;
 refNum : Integer;
 wdRefNum : Integer;
 theResIndex: Integer;
 onlyFiles: Boolean;
 validFile: Boolean;
 mustCloseWD: Boolean;
 pbs    : HParamBlockRec;
 BEGIN  
 IF (theDirID=0) | (CleanOpenWD(theVRefNum,                    
 theDirID, wdRefNum,mustCloseWD) = noErr) THEN                 BEGIN
 IF theDirID = 0 THEN 
 wdRefNum := theVRefNum;
 
 IF buildFolderInfo THEN  StuffDirInfo(theVRefNum, theDirID);
 
 dirIndex := 1;
 REPEAT
 cInfo.ioNamePtr := @currFileName;
 cInfo.ioDirID   := theDirID;
 cInfo.ioVRefNum := wdRefNum;
 cInfo.ioFDirIndex := dirIndex;
 
 IF PBGetCatInfo(@cInfo, False) = noErr THEN                   
 BEGIN
 IF BAnd(Integer(cInfo.ioFlAttrib), $010) <>                   
 0 THEN BEGIN {We’ve found a directory}
 IF recursive THEN SearchDirectory (theVRefNum, cInfo.ioDirID, True);
 END ELSE BEGIN
 FOR jj := 1 TO numTypes DO
 IF LongInt(biiList^[jj].bi_fileType) =                        
 LongInt(cInfo.ioFlFndrInfo.fdType)  THEN LEAVE;
 
 IF jj <= numTypes THEN BEGIN
 WITH biiList^[jj] DO BEGIN
 onlyFiles := LongInt(bi_resType) = 0;
 
 validFile := 
 (LongInt(bi_crtrType) = 0) | (LongInt(bi_crtrType) =          
 LongInt(cInfo.ioFlFndrInfo.fdCreator));
 
 IF validFile THEN
 IF bi_fileName <> NIL THEN
 validFile := 
 (Length(bi_fileName^) =  Length(cInfo.ioNamePtr^)) &
 EqualString(bi_fileName^,  cInfo.ioNamePtr^, False, True);
 END;
 
 refNum := 0;
 IF validFile THEN BEGIN
 IF onlyFiles THEN BEGIN
 IF biiList^[jj].bi_procAddr <> NIL  THEN BEGIN
 BlockClear(@pbs, SizeOf(pbs));
 pbs.ioNamePtr := cInfo.ioNamePtr;
 pbs.ioVRefNum := wdRefNum;
 pbs.ioPermssn := fsRdPerm;
 pbs.ioDirID:= theDirID; 
 validFile:= PBHOpen(@pbs,  False) = noErr;
 
 IF validFile THEN 
 refNum :=pbs.ioRefNum;
 END;
 END ELSE BEGIN
 SetResLoad(False);
 refNum := OpenRFPerm     (cInfo.ioNamePtr^, wdRefNum, fsRdPerm);
 SetResLoad(True);
 validFile := ResError = noErr;
 END;
 
 IF validFile THEN BEGIN
 noTypeFlag := LongInt(biiList^[jj].bi_resType)=0;
 
 IF noTypeFlag
 THEN theResCount := 1
 ELSE theResCount := Count1Resources 
 (biiList^[jj].bi_resType);
 
 FOR theResIndex := 1 TO theResCount
 DO BEGIN
 
 IF noTypeFlag
 THEN resHdl := NIL
 ELSE resHdl := Get1IndResource
 (biiList^[jj].bi_resType,theResIndex);

 IF noTypeFlag | ((resHdl <> NIL) & 
 (ResError = noErr)) THEN BEGIN
 IF noTypeFlag THEN BEGIN
 theResID := 0;
 theResType:= ResType(NIL);
 theResName:=  cInfo.ioNamePtr^;
 END ELSE BEGIN
 HLock(resHdl);
 GetResInfo (resHdl, theResID,     theResType, theResName);    
 IF Length(theResName) = 0 THEN    BEGIN
 NumToString(theResID,    theResName);
 theResName := Concat(‘xxxx resource number ‘,                 
 theResName);
 BlockMove(@theResType,   @theResName[1],4);
 END;
 END;
 
 thePrivData:= 0;
 
{ now let’s call identify routine to see if it’s available }
 IF biiList^[jj].bi_procAddr <>    NIL THEN BEGIN
 IF noTypeFlag 
 THEN CallIdentifyProc    (theResName,NIL, refNum,             
 thePrivData,    biiList^[jj].bi_procAddr)
 ELSE CallIdentifyProc    (theResName, resHdl,                 
 theResID, thePrivData,
 biiList^[jj].bi_procAddr);
 END;
 
 IF NOT noTypeFlag THEN BEGIN
 HUnLock(resHdl);
 IF CleanReleaseResource (resHdl)  <> noErr THEN theResName := ‘’;
 END;
 
{This would be a good place to check for duplicate names in the list 
and decide what to do with them}
 
 IFLength(theResName)>0 THEN  BEGIN
 { so far this file is valid }
 resHdl := NewHandleClear (SizeOf(ItemInfoRec));
 WITH ItemInfoHdl(resHdl)^^ DO     BEGIN
 theName := Copy (theResName, 1,Min(SizeOf(il_nickName)-1,     
 Length(theResName)));

 il_nickName:= theName;
 END;
 
 WITH ItemInfoHdl (resHdl)^^ DO    BEGIN
 il_privData:= thePrivData;
 il_fileSpec.cf_fileName := cInfo.ioNamePtr^;
 il_fileSpec.cf_vRefNum :=  theVRefNum;
 il_fileSpec.cf_dirID:=   theDirID;
 il_fileType:=   cInfo.ioFlFndrInfo.fdType;
 il_nextItemInfo :=  result.ssu_firstItemInfo;
 il_resourceType :=  biiList^[jj].bi_resType;
 il_resID := theResID;    
 END;
 result.ssu_firstItemInfo :=  ItemInfoHdl (resHdl);
 END;
 END; { no resErr }
 END; { all files in this res file }

 IF onlyFiles
 THEN dummyOSErr := CleanFSClose   (refNum)
 ELSE dummyOSErr :=CleanCloseResFile (refNum);
 END ELSE refNum := 0;
 END;
 END;
 END;
 
 dirIndex := dirIndex + 1;
 END ELSE BEGIN
 theName := ‘’;
 LEAVE;
 END;
 UNTIL eternity;
 
 IF(theDirID<>0) & mustCloseWD &   (CleanCloseWD(wdRefNum) <> noErr) 
THEN BEGIN
 { process the error }
 END;
 END;
 END;
 
 VAR  folderDirID: LongInt;
 folderVRefNum : Integer;
 shouldBuild: Boolean;
BEGIN
{Let’s check if the item list needs to be updated}
 shouldBuild := gCompanyFStruct = NIL;
 
 IF NOT shouldBuild THEN BEGIN
 HLock(Handle(gCompanyFStruct));
 WITH gCompanyFStruct^^ DO
 FOR i:= 1 TO di_count DO WITH di_list[i] DO
 IF GetDirModTime(di_vRefNum,di_dirId) <>                      
 di_dirModTime THEN BEGIN
 shouldBuild := NOT shouldBuild;
 LEAVE;
 END;
 HUnLock(Handle(gCompanyFStruct));
 
 IF shouldBuild THEN CleanDisposHandle (gCompanyFStruct);
 END;
 
 buildFolderInfo := gCompanyFStruct = NIL;
 
 IF shouldBuild | (result.ssu_folderTime <>                    
 gCompanyFStruct^^.di_time) THEN BEGIN
 
 SSU_DisposList(result, killPrivData);
 
{First let’s look for the “Company Folder” inside the Application Folder}
 folderVRefNum := gAppWDRefNum;
 
 folderDirID :=  SearchForDirectory  (folderVRefNum);
 IF folderDirID = 0 THEN BEGIN 
{ or else let’s look for the “Company Folder” inside the System Folder};
 folderVRefNum := gSysWDRefNum;
 folderDirID:= SearchForDirectory  (folderVRefNum);
 END;
 
{If the “Company Folder” is found in either the Application or System 
Folder, then let’s look for the items }

 IF folderDirID <> 0 THEN
 SearchDirectory(folderVRefNum, folderDirID,                   
 True);
 
 result.ssu_folderTime := gCompanyFStruct^^.di_time;     END;
END;

PROCEDURE DisposeItemInfoHdl(VAR   theItemInfoHdl:ItemInfoHdl; 
 killPrivData:ProcPtr);
BEGIN
 IF theItemInfoHdl <> NIL THEN BEGIN
 HLock(Handle(theItemInfoHdl));
 DisposeItemInfoHdl( theItemInfoHdl^^.il_nextItemInfo,killPrivData);
 IF killPrivData <> NIL THEN  CallKillPrivData(theItemInfoHdl^^.il_privData, 
 killPrivData);
 HUnLock(Handle(theItemInfoHdl));
 CleanDisposHandle(theItemInfoHdl);
 END;
END;

PROCEDURE SSU_DisposList(VAR result:SSUInfoRec; 
 killPrivData:ProcPtr);
BEGIN
 result.ssu_folderTime := 0;
 DisposeItemInfoHdl(result.ssu_firstItemInfo,            killPrivData);
END;

{$S Init}
PROCEDURE InitSSU;
VARtheWorld : SysEnvRec;
BEGIN
 gCompanyFStruct := NIL;  
 IF GetVol(nil, gAppWDRefNum) <> noErr THEN 
 {Should never happen, but you may want to be defensive };
 IF SysEnvirons (2, theWorld) = noErr
 THEN gSysWDRefNum := theWorld.sysVRefNum
 ELSE gSysWDRefNum := gAppWDRefNum;
END;

END.

 
AAPL
$94.72
Apple Inc.
+0.78
MSFT
$44.83
Microsoft Corpora
-0.01
GOOG
$594.74
Google Inc.
+5.27

MacTech Search:
Community Search:

Software Updates via MacUpdate

Airmail 1.4 - Powerful, minimal email cl...
Airmail is a powerful, minimal mail client.It was designed to retain the same experience with a single or multiple accounts and provide a quick, modern and easy-to-use user experience. Airmail... Read more
Macs Fan Control 1.1.12 - Monitor and co...
Macs Fan Control allows you to monitor and control almost any aspect of your computer's fans, with support for controlling fan speed, temperature sensors pane, menu-bar icon, and autostart with... Read more
A Better Finder Rename 9.37 - File, phot...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
MacBook Air EFI Firmware Update 2.9 - Fo...
MacBook Air EFI Firmware Update is recommended for MacBook Air (Mid 2011) models. This update addresses an issue where systems may take longer to wake from sleep than expected and fixes a rare issue... Read more
FileZilla 3.9.0.1 - Fast and reliable FT...
FileZilla (ported from Windows) is a fast and reliable FTP client and server with lots of useful features and an intuitive interface.Version 3.9.0.1: MSW: Fix installation issue with locked DLLs... Read more
OS X Yosemite 10.10 DP4 - Developer Prev...
Note: This is a Developer Preview. You must be a registered Apple Mac Developer to download this update. OS X Yosemite is Apple's newest operating system for Mac. An elegant design that feels... Read more
FinderPop 2.5.6 - Classic Mac utility, n...
FinderPop is a Universal preference pane that extends OS X's contextual menus using a FinderPop Items folder much as the Apple Menu Items folder used to do for the Apple menu. It has other features... Read more
SpiderOak 5.1.7 - Secure cloud backup, s...
SpiderOak is a multi-platform secure online backup, storage, access, and sharing solution engineered for the consumer and small businesses. You must first sign up to use SpiderOak. Running natively... Read more
Espionage 3.6 - Simple, state of the art...
Espionage offers state-of-the-art encryption and plausible deniability for your confidential data. Sometimes, encrypting your data isn't enough to protect it. That's why Espionage 3 goes beyond data... Read more
calibre 1.45.0 - Complete e-library mana...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital... Read more

Latest Forum Discussions

See All

Celebrate Summer With a Cat in the Hat L...
Celebrate Summer With a Cat in the Hat Learning Library Sale Posted by Ellis Spice on July 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
MyTaskList Review
MyTaskList Review By Jennifer Allen on July 22nd, 2014 Our Rating: :: EFFECTIVE IF PLAINUniversal App - Designed for iPhone and iPad It’s not the most stylish of task management apps, but MyTaskList has all the features you could... | Read more »
FlyCraft Herbie: Crazy Machines Review
FlyCraft Herbie: Crazy Machines Review By Jennifer Allen on July 22nd, 2014 Our Rating: :: TRICKY FLYINGUniversal App - Designed for iPhone and iPad A tough game of careful thrusting and navigation, FlyCraft Herbie: Crazy Machines... | Read more »
MTN Review
MTN Review By Jessica Fisher on July 22nd, 2014 Our Rating: :: ADORABLE, SERENE, AND AMUSINGUniversal App - Designed for iPhone and iPad MTN is an adorable, talking pet mountain that is less game and more zen garden.   | Read more »
Fly High with Ninja UP! Now Available o...
Fly High with Ninja UP! Now Available on the App Store Posted by Jessica Fisher on July 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Bio Inc. Review
Bio Inc. Review By Nadia Oxford on July 22nd, 2014 Our Rating: :: SICKENING - IN A COMPELLING WAYUniversal App - Designed for iPhone and iPad Bio Inc is about orchestrating the medical destruction of a single person. If that doesn’... | Read more »
HELMUT Review
HELMUT Review By Andrew Fisher on July 21st, 2014 Our Rating: :: TRUNDLE SIMULATOR 2014Universal App - Designed for iPhone and iPad HELMUT is a fun, fleeting time-sink that offers a momentary distraction and nothing else.   | Read more »
Walkr Review
Walkr Review By Jennifer Allen on July 21st, 2014 Our Rating: :: ORIGINAL WALKINGiPhone App - Designed for the iPhone, compatible with the iPad Walking is a bit more exciting thanks to this planet building/discovering sim reliant... | Read more »
Zombie Commando Review
Zombie Commando Review By Jennifer Allen on July 21st, 2014 Our Rating: :: MINDLESS SLAUGHTERUniversal App - Designed for iPhone and iPad Briefly fun but ultimately forgettable, Zombie Commando will scratch an itch then be... | Read more »
Swords & Poker Adventures Review
Swords & Poker Adventures Review By Jennifer Allen on July 21st, 2014 Our Rating: :: SOULLESS POKER PLAYUniversal App - Designed for iPhone and iPad Swords & Poker Adventures is a mishmash of Poker and RPGing, but it lacks... | Read more »

Price Scanner via MacPrices.net

WaterField Designs Unveils Cycling Ride Pouch...
High end computer case and bag maker WaterField Designs of San Francisco now enters the cycling market with the introduction of the Cycling Ride Pouch – an upscale toolkit with a scratch-free iPhone... Read more
Kingston Digital Ships Large Capacity Near 1T...
Kingston Digital, Inc., the Flash memory affiliate of Kingston Technology Company, Inc.,has announced its latest addition to the SSDNow V300 series, the V310. The Kingston SSDNow V310 solid-state... Read more
15-inch 2.0GHz MacBook Pro Retina on sale for...
B&H Photo has the 15″ 2.0GHz Retina MacBook Pro on sale for $1829 including free shipping plus NY sales tax only. Their price is $170 off MSRP. B&H will also include free copies of Parallels... Read more
Apple restocks refurbished Mac minis for up t...
The Apple Store has restocked Apple Certified Refurbished Mac minis for up to $150 off the cost of new models. Apple’s one-year warranty is included with each mini, and shipping is free: - 2.5GHz Mac... Read more
Twelve South HiRise For MacBook – Height-Adju...
If you use your MacBook as a workhorse desktop substitute, as many of us do, a laptop stand combined with an external keyboard and pointing device are pretty much obligatory if you want to avoid... Read more
Why The Mac Was Not Included In The Apple/IBM...
TUAW’s Yoni Heisler cites Fredrick Paul of Network World whoi blogged last week that the Mac’s conspicuous absence from Apple and IBM’s landmark partnership agreement represents a huge squandered... Read more
Save $100 on 13-inch Retina MacBook Pros, plu...
Adorama has 13″ Retina MacBook Pros on sale for $100 off MSRP. Shipping is free, and Adorama charges sales tax in NY & NJ only: - 13″ 2.4GHz/128GB MacBook Pro with Retina Display: $1199 - 13″ 2.... Read more
Blurr it 2.3 for iOS – Quickly Blurs Selected...
Hyderabad, India based TouchLabs has announced a new update of Blurr it 2.3, their photography app for iOS users. Blurr it allows you to blur part of the image to hide potentially sensitive or... Read more
MacBook Airs on sale for $100 off MSRP, start...
Best Buy has the new 2014 MacBook Airs on sale for up to $100 off MSRP on their online store. Choose free home shipping or free local store pickup (if available). Prices valid for online orders only... Read more
Amazon Announces Kindle Unlimited: Unlimited...
Amazon.com has introduced Kindle Unlimited — a new subscription service which allows customers to freely read as much as they want from over 600,000 Kindle books, and listen as much as they want to... Read more

Jobs Board

Sr Software Lead Engineer, *Apple* Online S...
Sr Software Lead Engineer, Apple Online Store Publishing Systems Keywords: Company: Apple Job Code: E3PCAK8MgYYkw Location (City or ZIP): Santa Clara Status: Full 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* 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
Sr *Apple* Engineer - IT - Requisition #: -...
For more information about TIAA-CREF, visit our website . The Apple Engineer will provide engineering and third-level incident support for 300- 500 MacOS desktop/laptop Read more
*Apple* Systems Administrator - DISH (United...
…satellite service provider, and Dish is currently looking for an experienced Apple /Mac Systems Administrator. Apple systems administrator will be responsible for Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.