home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************************
- *
- * File: dirent.c
- * Created: 7/3/93 By: George T. Talbot
- * Purpose: Implements UNIX-like directory reading for the Macintosh.
- *
- * Modifications:
- *
- * Notes:
- * 1) These routines will NOT work under A/UX.
- * 2) WD = working directory
- * 3) CD = change directory
- * 4) FS = file system
- * 5) Mac filesystems allow spaces as part of pathnames!
- * 6) All routines which return a path use the default Macintosh path separator,
- * a colon (":").
- *
- ****************************************************************************************/
-
- #include "dirent.h"
- #include <pascal.h>
- #include <string.h>
-
- OSErr dd_errno; /* Global errno to check after calls to dirent routines */
- char *dd_separator = ":"; /* If you're feeling brave, change this to "/" */
- int dd_xform_seps = false;
-
- /****************************************************************************************
- *
- * This function, given a Macintosh-style pathname, will open a directory to that path.
- * NOTES: 1) passing in nil will get you the current directory.
- * 2) ".:", "..:" & "≈:" are supported at the beginning of paths ONLY
- * by this routine.
- * 3) "/" will be turned into ":" by this routine.
- *
- * Calls: PBHGetVol(), PBHGetCatInfo(), PBHSetVol(), hopendir(), CtoPstr()
- * Called By: <general purpose>
- * Globals Used: dd_errno
- * Parameters: pointer to C-string pathname or nil for current directory
- * Returns: pointer to directory management block or nil & dd_errno will be set
- *
- ****************************************************************************************/
-
- DIR *opendir(char *dirname)
- {
- WDPBRec pb;
- CInfoPBRec cpb;
- short vRefNum;
- long dirID;
- char *dname;
- DIR *temp;
- char path_temp[MAXPATHLEN+1]; /* Temporary area for building pathname */
-
- /* Save the current path */
- pb.ioCompletion = nil;
- pb.ioNamePtr = nil;
-
- if (dd_errno = PBHGetVol(&pb, false))
- return nil;
-
- vRefNum = pb.ioWDVRefNum;
- dirID = pb.ioWDDirID;
-
- /* dname points to the desired pathname */
- dname = dirname;
-
- /* If no pathname was passed in, or there are no ".", ".." or "≈" special directory
- * names, then handle the pathname as normal.
- */
- if (dirname == nil)
- goto opendir_fallthrough;
-
- /* If there's not '.', '..' or '≈', fall through */
- if ((dirname[0] != '.') && (dirname[0] != '≈'))
- goto opendir_fallthrough;
-
- /* If there's a '≈', treat it like '..' */
- if (dirname[0] == '≈')
- {
- dname = &(dirname[1]);
- goto path_dotdot;
- }
-
- /* If the pathname has "." (current directory) in front of it... */
- if (dirname[1] != '.')
- {
- /* Skip over the "." and fall through */
- dname = &(dirname[1]);
- goto opendir_fallthrough;
- }
-
- /* Skip over the ".." */
- dname = &(dirname[2]);
-
- path_dotdot:
- /* If we get here, the directory has ".." in front of it... */
-
- /* First, get the directory info on the current directory. We do this so
- * that we can get the directory's parent
- */
- cpb.dirInfo.ioCompletion = nil;
- cpb.dirInfo.ioNamePtr = path_temp; /* Unused, but must be set because of
- * bug in Apple File Sharing.
- */
- cpb.dirInfo.ioVRefNum = vRefNum;
- cpb.dirInfo.ioFDirIndex = -1;
- cpb.dirInfo.ioDrDirID = dirID;
-
- if (dd_errno = PBGetCatInfo(&cpb, false))
- return nil;
-
- /* Temporarily CD to the parent directory */
- pb.ioCompletion = nil;
- pb.ioNamePtr = nil;
- pb.ioVRefNum = pb.ioWDVRefNum;
- pb.ioWDDirID = cpb.dirInfo.ioDrParID;
-
- if (dd_errno = PBHSetVol(&pb, false))
- return nil;
-
- /* This is the common code for all three cases above */
- opendir_fallthrough:
- /* If the pathname is too long (this is a Macintosh FS constraint), then return */
- if (strlen(dname) > MAXPATHLEN)
- {
- /* Set the error */
- dd_errno = bdNamErr;
- temp = nil;
-
- /* Go to the common exit, where we CD back to the saved WD */
- goto opendir_exit;
- }
-
- /* If this call was passed a pathname */
- if (dname != nil)
- {
- /* Copy the pathname into a temp */
- strcpy(path_temp, dname);
-
- /* Turn it into a Pascal string for the Mac FS */
- CtoPstr(path_temp);
-
- /* Change any "/" to ":" for the Mac FS */
- if (dd_xform_seps)
- {
- int i;
-
- for (i=1; i<= path_temp[0]; ++i)
- if (path_temp[i] == '/')
- path_temp[i] = ':';
- }
-
- /* Try and open the directory */
- temp = hopendir(path_temp, 0, 0);
- }
- else
- /* If this call wasn't passed a pathname, then we call hopendir() with nil to
- * tell it to open the current working directory.
- */
- temp = hopendir(nil, 0, 0);
-
- /* This is the common exit code which restores the current WD */
- opendir_exit:
- pb.ioCompletion = nil;
- pb.ioNamePtr = nil;
- pb.ioVRefNum = vRefNum;
- pb.ioWDDirID = dirID;
-
- if (dd_errno = PBHSetVol(&pb, false))
- {
- /* If this call failed, then get rid of the structures created by hopendir() */
- closedir(temp);
- return nil;
- }
-
- return temp;
- }
-
- /****************************************************************************************
- *
- * This function actually opens the directory. If you feel brave, you can call it.
- * If you pass in a dirname, then set vRefNum and dirID to 0. All named opens are
- * relative to the current WD. If you pass in vRefNum and dirID, then don't bother
- * passing in a name. This routine WILL CHANGE YOUR CURRENT WORKING DIRECTORY!
- *
- * Calls: NewHandle(), PBHGetCatInfo(), PBHSetVol(), PtoCstr(), BlockMove(),
- * DisposHandle(), MoveHHi(), HLock(), MemError()
- * Called By: opendir(), and you if you feel brave.
- * Globals Used: dd_errno
- * Parameters: pointer to Pascal-string pathname, vRefNum, dirID of desired
- * directory. If you pass in a WDRefNum as the vRefNum, set dirID to 0
- * Returns: pointer to directory management block or nil & dd_errno will be set
- *
- ****************************************************************************************/
-
- DIR *hopendir(char *dirname, short vRefNum, long dirID)
- {
- DIR **curh, *cur;
- CInfoPBRec cpb;
- WDPBRec pb;
- Str63 name;
-
- /* Get memory for the directory structure */
- curh = (DIR **) NewHandle(sizeof(DIR));
-
- /* Did we get it? */
- if (curh == nil)
- {
- dd_errno = MemError();
- return nil;
- }
-
- /* Move it high and lock it */
- MoveHHi(curh);
- HLock(curh);
- cur = *curh;
-
- /* If we're supposed to open anything but the current directory, set the current
- * working directory to the desired directory.
- */
- if ((dirname != nil) || (vRefNum != 0) || (dirID != 0))
- {
- pb.ioCompletion = nil;
- pb.ioNamePtr = dirname;
- pb.ioVRefNum = vRefNum;
- pb.ioWDDirID = dirID;
-
- if (dd_errno = PBHSetVol(&pb, false))
- goto failure_exit;
- }
-
- cur->dd_buf = nil;
-
- /* Get info on the desired directory (its name, etc.) */
- cpb.dirInfo.ioCompletion = nil;
- cpb.dirInfo.ioNamePtr = name;
- cpb.dirInfo.ioVRefNum = vRefNum;
- cpb.dirInfo.ioFDirIndex = -1;
- cpb.dirInfo.ioDrDirID = dirID;
-
- if (dd_errno = PBGetCatInfo(&cpb, false))
- goto failure_exit;
-
- /* Save the directory info */
- cur->dir_fsp.vRefNum = vRefNum;
- cur->dd_fd = cpb.dirInfo.ioDrDirID;
- cur->dd_parent = cpb.dirInfo.ioDrParID;
-
- BlockMove(name, cur->dir_fsp.name, sizeof(Str63));
-
- /* Convert the name to a C-style string */
- PtoCstr(cur->dir_fsp.name);
-
- /* Set up our directory structure to read the first entry */
- cur->dd_off = 1;
- cur->dd_numents = cpb.dirInfo.ioDrNmFls;
- cur->dd_cached = false;
-
- return cur;
-
- /* This code is branched-to in case of error. It frees up the memory and returns. */
- failure_exit:
- DisposHandle((Ptr) curh);
- return nil;
- }
-
- /****************************************************************************************
- *
- * This function returns the index of the directory entry to be next read.
- *
- * Calls: nothing
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to the directory management block
- * Returns: index of the next directory entry to be read.
- *
- ****************************************************************************************/
-
- long telldir(DIR *dirp)
- {
- if (dirp->dd_off > dirp->dd_numents)
- return -1;
- else
- return dirp->dd_off-1; /* The -1 is because Macs start at 1 & not 0 for dir index,
- * and this is a little more POSIX.
- */
- }
-
- /****************************************************************************************
- *
- * This function closes the directory opened with opendir() or hopendir()
- *
- * Calls: DisposHandle(), RecoverHandle()
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to the directory management block
- * Returns: 0 (always successful)
- *
- ****************************************************************************************/
-
- int closedir(DIR *dirp)
- {
- struct dirent **cur;
-
- /* Dispose of any directory entries read in. */
- cur = dirp->dd_buf;
-
- dd_errno = noErr;
-
- while (cur)
- {
- struct dirent **next;
-
- next = (*cur)->next;
-
- DisposHandle((Handle) cur);
-
- if (dd_errno == noErr)
- dd_errno = MemError();
-
- cur = next;
- }
-
- /* Dispose of the directory managment block */
- DisposHandle(RecoverHandle((Ptr) dirp));
-
- if (dd_errno == noErr)
- dd_errno = MemError();
-
- return dd_errno?-1:0;
- }
-
- /****************************************************************************************
- *
- * This function sets the index of the next-read directory entry. It will also search
- * the list of read entries so that an entry won't be read from disk more than once.
- *
- * Calls: nothing
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to the directory management block, index of directory
- * Returns: nothing
- *
- ****************************************************************************************/
-
- void seekdir(DIR *dirp, long loc)
- {
- struct dirent **cur;
-
- dirp->dd_off = loc+1; /* The +1 is because the Mac indexes directories
- * from 1 and not 0 and we want to be a little bit
- * POSIX
- */
-
- /* Search through the entries that we've read already */
- cur = dirp->dd_buf;
-
- while (cur)
- {
- /* If we find the entry that we've seeked to, set up so that readdir() will
- * return this one instead of reading a new one.
- */
- if (loc == (*cur)->d_off)
- {
- dirp->dd_cached = true;
- dirp->dd_cache_hint = cur;
-
- return;
- }
-
- cur = (*cur)->next;
- }
-
- /* If we didn't find it, then tell readdir() to get the entry from the FS */
- dirp->dd_cached = false;
- }
-
- /****************************************************************************************
- *
- * This function will read the next directory entry from disk. It will return nil and
- * set dd_errno to noErr when the end of the directory is reached. It will avoid
- * reading directory entries from disk more than once.
- *
- * Calls: nothing
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to the directory management block
- * Returns: pointer to directory entry or nil if an error occurred and dd_errno
- * will be set. If the last entry has already been read, this will
- * return nil and dd_errno will be set to noErr.
- *
- ****************************************************************************************/
-
- struct dirent *readdir(DIR *dirp)
- {
- CInfoPBRec cpb;
- struct dirent **meh, *me;
-
- /* If the entry has been read already, then return the already present entry */
- if (dirp->dd_cached)
- me = *(dirp->dd_cache_hint);
- else
- /* Otherwise, read it from disk... */
- {
- /* Past the end of the directory? */
- if (dirp->dd_off > dirp->dd_numents)
- {
- dd_errno = noErr;
- return nil;
- }
-
- /* Allocate space for a new entry */
- meh = (struct dirent **) NewHandle(sizeof(struct dirent));
-
- /* Enough memory? */
- if (meh == nil)
- {
- dd_errno = MemError();
- return nil;
- }
-
- /* Lock the entry */
- MoveHHi((Handle) meh);
- HLock((Handle) meh);
-
- me = *meh;
-
- /* Get the entry's info from disk */
- me->fsp.name[0] = 0;
-
- cpb.dirInfo.ioCompletion = nil;
- cpb.dirInfo.ioNamePtr = me->fsp.name;
- cpb.dirInfo.ioVRefNum = dirp->dir_fsp.vRefNum;
- cpb.dirInfo.ioFDirIndex = dirp->dd_off;
- cpb.dirInfo.ioDrDirID = dirp->dd_fd;
-
- if (dd_errno = PBGetCatInfo(&cpb, false))
- {
- DisposHandle((Handle) meh);
- return nil;
- }
-
- /* Set up the dirent structure */
- me->d_off = dirp->dd_off-1;
- me->fsp.vRefNum = cpb.dirInfo.ioVRefNum;
- me->d_fileno = cpb.dirInfo.ioDrDirID;
- me->d_parent = cpb.dirInfo.ioDrParID;
-
- /* C strings only! */
- PtoCstr(me->fsp.name);
-
- /* Add it to the list for this directory */
- me->next = dirp->dd_buf;
-
- dirp->dd_buf = meh;
- }
-
- /* Seek to the next entry */
- seekdir(dirp, dirp->dd_off);
-
- /* Return what we've found */
- return me;
- }
-
- /****************************************************************************************
- *
- * This function will give an absolute pathname to a given directory.
- *
- * Calls: NewPtr(), DisposPtr(), PBGetCatInfo()
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: vRefNum and startDirID of desired path, pointer to path name storage,
- * length of path name storage, pointer to C-string separator.
- * Returns: bdNamErr if the path would overflow the storage,
- * some other error code if something else happened,
- * or noErr on success.
- *
- ****************************************************************************************/
-
- OSErr hgetwd(short vRefNum, long startDirID, char *path, int max_path_len, char *sep)
- {
- long curDirID;
- OSErr err;
- CInfoPBRec pb;
- Str63 name;
- char *temp_path;
-
- /* Start with an empty path */
- path[0] = 0;
-
- /* Get memory for a temporary path */
- temp_path = (char *) NewPtr(max_path_len);
-
- if (temp_path == nil)
- return MemError();
-
- /* Start at the given directory */
- curDirID = startDirID;
-
- do {
- /* Get cat info for the current directory */
- name[0] = 0;
-
- pb.dirInfo.ioCompletion = nil;
- pb.dirInfo.ioNamePtr = name;
- pb.dirInfo.ioVRefNum = vRefNum;
- pb.dirInfo.ioFDirIndex = -1;
- pb.dirInfo.ioDrDirID = curDirID;
-
- if (err = PBGetCatInfo(&pb, false))
- {
- DisposPtr((Ptr) temp_path);
- return err;
- }
-
- /* Convert name to a C string */
- PtoCstr(name);
-
- /* Check that we don't overflow storage */
- if ((strlen(name) + strlen(path) + strlen(sep)) >= max_path_len)
- {
- DisposPtr((Ptr) temp_path);
- return bdNamErr;
- }
-
- /* Prepend the name and separator */
- strcpy(temp_path, path);
- strcpy(path, name);
- strcat(path, sep);
- strcat(path, temp_path);
-
- /* Move "up" one directory */
- curDirID = pb.dirInfo.ioDrParID;
- }
- /* Until we hit the root directory */
- while (pb.dirInfo.ioDrDirID != fsRtDirID);
-
- /* Get rid of our temp storage and return */
- DisposPtr((Ptr) temp_path);
-
- return MemError();
- }
-
- /****************************************************************************************
- *
- * This function will change the current working directory.
- *
- * Calls: opendir(), closedir(), PBHSetVol()
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: C-string pathname.
- * Returns: -1 on failure, 0 on success. Sets dd_errno on failure.
- *
- ****************************************************************************************/
-
- int chdir(char *path)
- {
- DIR *d;
- short vRefNum;
- long dirID;
- WDPBRec pb;
-
- /* Open the directory */
- d = opendir(path);
-
- if (d == nil)
- return -1;
-
- /* Get the Mac FS identification for this directory */
- vRefNum = d->dd_volume;
- dirID = d->dd_fd;
-
- /* Close the directory */
- closedir(d);
-
- /* CD to the new directory */
- pb.ioCompletion = nil;
- pb.ioNamePtr = nil;
- pb.ioVRefNum = vRefNum;
- pb.ioWDDirID = dirID;
-
- dd_errno = PBHSetVol(&pb, false);
-
- return dd_errno?-1:0;
- }
-
- /****************************************************************************************
- *
- * This function will get the current working directory's path.
- *
- * Calls: PBHGetVol(), hgetwd()
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to a buffer of MAXPATHLEN bytes.
- * Returns: pointer to the buffer on success, on failure, nil and dd_errno will
- * be set.
- *
- ****************************************************************************************/
-
- char *getwd(char *path)
- {
- WDPBRec pb;
-
- /* Get the current working directory */
- pb.ioCompletion = nil;
- pb.ioNamePtr = nil;
-
- if (dd_errno = PBHGetVol(&pb, false))
- return nil;
-
- /* Transform it into a path */
- if (dd_errno = hgetwd(pb.ioWDVRefNum, pb.ioWDDirID, path, MAXPATHLEN-1, dd_separator))
- return nil;
-
- return path;
- }
-
- /****************************************************************************************
- *
- * This function will get the path to a given (already opened) directory.
- *
- * Calls: hgetwd()
- * Called By: <general purpose>
- * Globals Used: none
- * Parameters: pointer to a buffer of MAXPATHLEN bytes.
- * Returns: pointer to the buffer on success, on failure, nil and dd_errno will
- * be set.
- *
- ****************************************************************************************/
-
- char *pathdir(DIR *dirp, char *path)
- {
- if (dd_errno = hgetwd(dirp->dd_volume, dirp->dd_fd, path, MAXPATHLEN-1, dd_separator))
- return nil;
-
- return path;
- }
-