File Path
Volume Number: | | 7
|
Issue Number: | | 9
|
Column Tag: | | TechNotes
|
Related Info: File Manager File Mgr (PBxxx) Standard FIle
File Path Revisited
By Alexander Colwell, Redondo Beach, CA
Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.
File Path Revisited
Some of the MacTutor readers probably noticed. Hey! Somebody has already written this article before! So, why is it here again? Well the answer is yes, it has, but only half the story. The previous MacTutor articles in January 1986 Programming for HFS Compatibility by Mike Schuster and in July 1986 Make a Path Name for HFS by Tom Taylor discuss when given the volume reference number (vRefNum) obtained by the standard SFGetFile dialog then the files full file path can be derived.1 But what about when given a full file path and deriving the files vRefNum that would be identical number returned by the SFGetFile dialog?
There are cases when applications, DAs, or other resource codes may want to know where the file is located between invocations. In April 1990, The Apple Technical Journals d e v e l o p e in Macintosh Q&A section recommends saving the DirID number that references the directory.2 This scheme is desirable because its simple and easy to retrieve the files vRefNum by calling the PBOpenWD ROM routine that can be used either by the FSOpen or PBOpen ROM routines. Another advantage is that the user can change the directory name and the directory ID number will remain the same. There are cases when this scheme fails if the directory is backed up, deleted, and restored, then the directory ID number is likely to be changed. Therefore, the application must ask you for the location of the desired file by popping up the SFGetFile dialog again.
OK. So you bother the user again by reasking the location of the file. No big deal. But what about an application managing distributed databases of files where each user has mirror copy of the files and folders relationship on its hard disk? This could be a real pain to ask the user to find hundreds of files when they periodically receive database updates, and the directories are constantly changing. This was exactly the situation I encountered a few years ago during a Macintosh application development at my place of employment.
During the development cycle of that application, I poured over the MacTutor articles and Macintosh Technical Notes on how to do this seemingly simple procedure. To my disappointment, I could not find a method that somebody else might have hacked out! After a few weeks of off and on hacking, I finally figured out how to obtain the vRefNum based on the file path that is equivalent to the same vRefNum returned by the SFGetFile dialog.
The results are two procedures rewritten from Pascal to THINK C from my old Mac programming projects. The first procedure is GetFilePath (rehash from previous articles) to obtain the file path given by the vRefNum. The other inverse procedure is the GetVolRefNbr to obtain the vRefNum given by the file path.3
The key in the GetVolRefNbr procedure is the use of system working directory proc ID : ERIK. This small tidbit of information can be found in Macintosh Technical Note #77. In my case, I had to figure-out the hard way how this magic working directory proc ID ERIK was used in the SFGetFile dialog. At the time, the technical note reference about ERIK did not exist.
The conclusion is for those who need to save the file path and then later reconstruct the vRefNum for the FSOpen or PBOpen ROM routines other than using the DirID method, then these procedures are for you.
Bibliography
1 You can find additional references in Macintosh Technical Note #238.
2 Finders Desktop directory/folder hierarchy. Directories and folders are analogous.
3 I use these routines in the not quite popular shareware NotePad++ desk accessory to remember the last opened NotePad++ File.
char *GetFilePathName(vRefNum)
short vRefNum; /* File's vol/dir ref */
{
WDParam wDir; /* Working directory */
HVolumeParam wVol; /* Working HFS param blk */
DirInfo wCInfo; /* Working cat info blk */
long wDirID; /* Working dir number */
Str255 wName; /* Working directory name*/
char *wPtr; /* Working string pointer*/
long wLength; /* Working string length */
char *pathFileName;/* Working file path name*/
wDir.ioNamePtr = 0L; /* Init working directory*/
wDir.ioVRefNum = vRefNum;
wDir.ioWDIndex = 0;
wDir.ioWDProcID = 0;
wDir.ioWDVRefNum = vRefNum;
PBGetWDInfo(&wDir,FALSE); /* Get the directory ref */
vRefNum = wDir.ioWDVRefNum;/* Save working vol ref #*/
wDirID = wDir.ioWDDirID; /* Save working dir ref #*/
wVol.ioNamePtr = (StringPtr)&wName;/* Init vol block*/
wVol.ioVRefNum = vRefNum;
wVol.ioVolIndex = 0;
wLength = 0L; /* Set path length to zip*/
pathFileName = NewPtr(0L); /* Set null file's path */
if (!PBHGetVInfo(&wVol,FALSE) &&/* Got vol info? */
pathFileName) { /* Got file path pointer?*/
if (wVol.ioVSigWord == 0x4244) {/* Check if it HFS */
wCInfo.ioNamePtr = (StringPtr)&wName;/* Init it */
wCInfo.ioVRefNum = vRefNum;
wCInfo.ioFDirIndex = -1;
wCInfo.ioDrParID = wDirID;
wCInfo.ioDrDirID = wDirID;
while (wCInfo.ioDrParID != 1){/* Do full path */
wCInfo.ioDrDirID = wCInfo.ioDrParID;/*Move up dir*/
if (PBGetCatInfo(&wCInfo,FALSE))/* Get dir info */
break; /* Break-out if failed!! */
wLength += wName[0] + 1L;/* Set string length */
wPtr = NewPtr(wLength + 1L);/* Alloc new str */
if (!wPtr) { /* Didn't get str ptr? */
if (pathFileName) /* Check if got memory */
DisposPtr(pathFileName);/* Release it */
pathFileName = 0L; /* Invalidate str pointer*/
break; /* Break-out if failed!! */
}
/* Shuffle file path down*/
BlockMove(pathFileName,wPtr + wName[0] + 1,
wLength - (long)(wName[0]));
DisposPtr(pathFileName);/* Release old one */
*(wPtr + wName[0]) = ':';/* Add dir delimeter */
BlockMove(&wName[1],pathFileName = wPtr, (long)(wName[0]));
}
}
else { /* Oops, get vol info */
wLength = wName[0] + 1L; /* Set string length */
wPtr = NewPtr(wLength + 1L);/* Alloc new string */
if (wPtr) { /* Got string pointer? */
*(wPtr + wName[0]) = ':';/* Tack on dir delimeter*/
BlockMove(&wName[1],pathFileName = wPtr,
(long)(wName[0]));
}
}
if (pathFileName) /* Check if got da string*/
pathFileName[wLength] = 0;/* Set end-of-string */
}
return(pathFileName); /* Return file path name */
}
short GetFilePathVolRef(pathFileName)
char *pathFileName; /* File's path string */
{
short i; /* Working index */
char c; /* Working input char */
short vRefNum; /* Working vol/dir ref */
WDParam wDir; /* Working directory */
HVolumeParam wVol; /* Working HFS param blk */
DirInfo wCInfo; /* Working cat info block*/
long wDirID; /* Working directory ID */
Str255 wVolName; /* Working volume name */
Str255 wName; /* Working string name */
char *wPtr; /* Working string pointer*/
short wLength; /* Working string length */
vRefNum = 0; /* Invalid vol/dir ref # */
wVol.ioVRefNum = 0; /* Init working vol ID # */
wCInfo.ioDrDirID = 2; /* Init top-most dir */
i = 0; /* Init working index */
wLength = 0; /* Init string length */
c = pathFileName[i]; /* Set 1st input char */
while(c) { /* Do til get vol/dir ref*/
if (c == ':') { /* Check if got dir specs*/
wName[0] = wLength - 1; /* Set "Pascal" str len */
wLength = 0; /* Reset string length */
if (!wVol.ioVRefNum) { /* Check if need vol ref */
wVol.ioNamePtr = (StringPtr)(&wVolName);
for(wVol.ioVolIndex = 1; !PBHGetVInfo(&wVol,FALSE);
wVol.ioVolIndex++) {
if (EqualString(wName,wVolName,FALSE,FALSE))
break;
}
vRefNum = wVol.ioVRefNum;/* Save vol ID */
if (wVol.ioVSigWord != 0x4244)/* MFS ? */
return(vRefNum); /* Let's get out early! */
}
else { /* Nope, get dir ref */
wCInfo.ioNamePtr = (StringPtr)(&wName);
wCInfo.ioVRefNum = wVol.ioVRefNum;
wCInfo.ioFDirIndex = 0;
wCInfo.ioDrParID = wCInfo.ioDrDirID;
if (PBGetCatInfo(&wCInfo,FALSE))/* Check dir ? */
return(0); /* Nope, break-out!!! */
}
} /* Add to directory specs*/
c = wName[++wLength] = pathFileName[i++];
}
wDir.ioNamePtr = 0L; /* Init dir data block */
wDir.ioVRefNum = wVol.ioVRefNum;
wDir.ioWDProcID = 'ERIK'; /* Magic 'SFGetFile' ID #*/
wDir.ioWDDirID = wCInfo.ioDrDirID;
if (!PBOpenWD(&wDir,FALSE))/* Check if opened dir */
vRefNum = wDir.ioVRefNum; /* Return vol/dir ref # */
return(vRefNum); /* Return vol/dir ref # */
}