home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 3 / Info_Mac_1994-01.iso / Development / Source / Dirent / dirent.c next >
Text File  |  1993-07-04  |  17KB  |  638 lines

  1. /****************************************************************************************
  2.  *
  3.  *    File:        dirent.c
  4.  *    Created:    7/3/93        By:    George T. Talbot
  5.  *    Purpose:    Implements UNIX-like directory reading for the Macintosh.
  6.  *
  7.  *    Modifications:
  8.  *
  9.  *    Notes:
  10.  *            1) These routines will NOT work under A/UX.
  11.  *            2) WD = working directory
  12.  *            3) CD = change directory
  13.  *            4) FS = file system
  14.  *            5) Mac filesystems allow spaces as part of pathnames!
  15.  *            6) All routines which return a path use the default Macintosh path separator,
  16.  *               a colon (":").
  17.  *
  18.  ****************************************************************************************/
  19.  
  20. #include "dirent.h"
  21. #include <pascal.h>
  22. #include <string.h>
  23.  
  24. OSErr    dd_errno;                /*    Global errno to check after calls to dirent routines    */
  25. char    *dd_separator = ":";    /*    If you're feeling brave, change this to "/"    */
  26. int        dd_xform_seps = false;
  27.  
  28. /****************************************************************************************
  29.  *
  30.  *    This function, given a Macintosh-style pathname, will open a directory to that path.
  31.  *    NOTES:    1)    passing in nil will get you the current directory.
  32.  *            2)    ".:", "..:" & "≈:" are supported at the beginning of paths ONLY
  33.  *                by this routine.
  34.  *            3)    "/" will be turned into ":" by this routine.
  35.  *
  36.  *    Calls:            PBHGetVol(), PBHGetCatInfo(), PBHSetVol(), hopendir(), CtoPstr()
  37.  *    Called By:        <general purpose>
  38.  *    Globals Used:    dd_errno
  39.  *    Parameters:        pointer to C-string pathname or nil for current directory
  40.  *    Returns:        pointer to directory management block or nil & dd_errno will be set
  41.  *
  42.  ****************************************************************************************/
  43.  
  44. DIR    *opendir(char *dirname)
  45.     {
  46.     WDPBRec            pb;
  47.     CInfoPBRec        cpb;
  48.     short            vRefNum;
  49.     long            dirID;
  50.     char            *dname;
  51.     DIR                *temp;
  52.     char            path_temp[MAXPATHLEN+1];    /*    Temporary area for building pathname    */
  53.  
  54.     /*    Save the current path    */
  55.     pb.ioCompletion    = nil;
  56.     pb.ioNamePtr    = nil;
  57.     
  58.     if (dd_errno = PBHGetVol(&pb, false))
  59.         return nil;
  60.  
  61.     vRefNum    = pb.ioWDVRefNum;
  62.     dirID    = pb.ioWDDirID;
  63.  
  64.     /*    dname points to the desired pathname    */
  65.     dname    = dirname;
  66.  
  67.     /*    If no pathname was passed in, or there are no ".", ".." or "≈" special directory
  68.      *    names, then handle the pathname as normal.
  69.      */
  70.     if (dirname == nil)
  71.         goto opendir_fallthrough;
  72.  
  73.     /*    If there's not '.', '..' or '≈', fall through    */
  74.     if ((dirname[0] != '.') && (dirname[0] != '≈'))
  75.         goto opendir_fallthrough;
  76.  
  77.     /*    If there's a '≈', treat it like '..'    */
  78.     if (dirname[0] == '≈')
  79.         {
  80.         dname = &(dirname[1]);
  81.         goto path_dotdot;
  82.         }
  83.  
  84.     /*    If the pathname has "." (current directory) in front of it...    */
  85.     if (dirname[1] != '.')
  86.         {
  87.         /*    Skip over the "." and fall through    */
  88.         dname    = &(dirname[1]);
  89.         goto opendir_fallthrough;
  90.         }
  91.  
  92.     /*    Skip over the ".."    */
  93.     dname    = &(dirname[2]);
  94.  
  95. path_dotdot:
  96.     /*    If we get here, the directory has ".." in front of it...    */
  97.     
  98.     /*    First, get the directory info on the current directory.  We do this so
  99.      *    that we can get the directory's parent
  100.      */
  101.     cpb.dirInfo.ioCompletion    = nil;
  102.     cpb.dirInfo.ioNamePtr        = path_temp;    /* Unused, but must be set because of
  103.                                                  * bug in Apple File Sharing.
  104.                                                  */
  105.     cpb.dirInfo.ioVRefNum        = vRefNum;
  106.     cpb.dirInfo.ioFDirIndex        = -1;
  107.     cpb.dirInfo.ioDrDirID        = dirID;
  108.  
  109.     if (dd_errno = PBGetCatInfo(&cpb, false))
  110.         return nil;
  111.  
  112.     /*    Temporarily CD to the parent directory    */
  113.     pb.ioCompletion                = nil;
  114.     pb.ioNamePtr                = nil;
  115.     pb.ioVRefNum                = pb.ioWDVRefNum;
  116.     pb.ioWDDirID                = cpb.dirInfo.ioDrParID;
  117.     
  118.     if (dd_errno = PBHSetVol(&pb, false))
  119.         return nil;
  120.  
  121.     /*    This is the common code for all three cases above    */
  122. opendir_fallthrough:
  123.     /*    If the pathname is too long (this is a Macintosh FS constraint), then return    */
  124.     if (strlen(dname) > MAXPATHLEN)
  125.         {
  126.         /*    Set the error    */
  127.         dd_errno    = bdNamErr;
  128.         temp        = nil;
  129.         
  130.         /*    Go to the common exit, where we CD back to the saved WD    */
  131.         goto opendir_exit;
  132.         }
  133.  
  134.     /*    If this call was passed a pathname    */
  135.     if (dname != nil)
  136.         {
  137.         /*    Copy the pathname into a temp    */
  138.         strcpy(path_temp, dname);
  139.         
  140.         /*    Turn it into a Pascal string for the Mac FS    */
  141.         CtoPstr(path_temp);
  142.  
  143.         /*    Change any "/" to ":" for the Mac FS    */
  144.         if (dd_xform_seps)
  145.             {
  146.             int i;
  147.             
  148.             for (i=1; i<= path_temp[0]; ++i)
  149.                 if (path_temp[i] == '/')
  150.                     path_temp[i] = ':';
  151.             }
  152.  
  153.         /*    Try and open the directory    */
  154.         temp = hopendir(path_temp, 0, 0);
  155.         }
  156.     else
  157.         /*    If this call wasn't passed a pathname, then we call hopendir() with nil to
  158.          *    tell it to open the current working directory.
  159.          */
  160.         temp = hopendir(nil, 0, 0);
  161.  
  162.     /*    This is the common exit code which restores the current WD    */
  163. opendir_exit:
  164.     pb.ioCompletion                = nil;
  165.     pb.ioNamePtr                = nil;
  166.     pb.ioVRefNum                = vRefNum;
  167.     pb.ioWDDirID                = dirID;
  168.     
  169.     if (dd_errno = PBHSetVol(&pb, false))
  170.         {
  171.         /*    If this call failed, then get rid of the structures created by hopendir()    */
  172.         closedir(temp);
  173.         return nil;
  174.         }
  175.  
  176.     return temp;
  177.     }
  178.  
  179. /****************************************************************************************
  180.  *
  181.  *    This function actually opens the directory.  If you feel brave, you can call it.
  182.  *    If you pass in a dirname, then set vRefNum and dirID to 0.  All named opens are
  183.  *    relative to the current WD.  If you pass in vRefNum and dirID, then don't bother
  184.  *    passing in a name.  This routine WILL CHANGE YOUR CURRENT WORKING DIRECTORY!
  185.  *
  186.  *    Calls:            NewHandle(), PBHGetCatInfo(), PBHSetVol(), PtoCstr(), BlockMove(),
  187.  *                    DisposHandle(), MoveHHi(), HLock(), MemError()
  188.  *    Called By:        opendir(), and you if you feel brave.
  189.  *    Globals Used:    dd_errno
  190.  *    Parameters:        pointer to Pascal-string pathname, vRefNum, dirID of desired
  191.  *                    directory.  If you pass in a WDRefNum as the vRefNum, set dirID to 0
  192.  *    Returns:        pointer to directory management block or nil & dd_errno will be set
  193.  *
  194.  ****************************************************************************************/
  195.  
  196. DIR    *hopendir(char *dirname, short vRefNum, long dirID)
  197.     {
  198.     DIR                **curh, *cur;
  199.     CInfoPBRec        cpb;
  200.     WDPBRec            pb;
  201.     Str63            name;
  202.  
  203.     /*    Get memory for the directory structure    */
  204.     curh    = (DIR **) NewHandle(sizeof(DIR));
  205.  
  206.     /*    Did we get it?    */
  207.     if (curh == nil)
  208.         {
  209.         dd_errno    = MemError();
  210.         return nil;
  211.         }
  212.  
  213.     /*    Move it high and lock it    */
  214.     MoveHHi(curh);
  215.     HLock(curh);
  216.     cur        = *curh;
  217.  
  218.     /*    If we're supposed to open anything but the current directory, set the current
  219.      *    working directory to the desired directory.
  220.      */
  221.     if ((dirname != nil) || (vRefNum != 0) || (dirID != 0))
  222.         {
  223.         pb.ioCompletion                = nil;
  224.         pb.ioNamePtr                = dirname;
  225.         pb.ioVRefNum                = vRefNum;
  226.         pb.ioWDDirID                = dirID;
  227.         
  228.         if (dd_errno = PBHSetVol(&pb, false))
  229.             goto failure_exit;
  230.         }
  231.  
  232.     cur->dd_buf    = nil;
  233.     
  234.     /*    Get info on the desired directory (its name, etc.)    */
  235.     cpb.dirInfo.ioCompletion    = nil;
  236.     cpb.dirInfo.ioNamePtr        = name;
  237.     cpb.dirInfo.ioVRefNum        = vRefNum;
  238.     cpb.dirInfo.ioFDirIndex        = -1;
  239.     cpb.dirInfo.ioDrDirID        = dirID;
  240.     
  241.     if (dd_errno = PBGetCatInfo(&cpb, false))
  242.         goto failure_exit;
  243.  
  244.     /*    Save the directory info    */
  245.     cur->dir_fsp.vRefNum    = vRefNum;
  246.     cur->dd_fd                = cpb.dirInfo.ioDrDirID;
  247.     cur->dd_parent            = cpb.dirInfo.ioDrParID;
  248.  
  249.     BlockMove(name, cur->dir_fsp.name, sizeof(Str63));
  250.  
  251.     /*    Convert the name to a C-style string    */
  252.     PtoCstr(cur->dir_fsp.name);
  253.  
  254.     /*    Set up our directory structure to read the first entry    */
  255.     cur->dd_off                = 1;
  256.     cur->dd_numents            = cpb.dirInfo.ioDrNmFls;
  257.     cur->dd_cached            = false;
  258.  
  259.     return cur;
  260.  
  261.     /*    This code is branched-to in case of error.  It frees up the memory and returns.    */
  262. failure_exit:
  263.     DisposHandle((Ptr) curh);
  264.     return nil;
  265.     }
  266.  
  267. /****************************************************************************************
  268.  *
  269.  *    This function returns the index of the directory entry to be next read.
  270.  *
  271.  *    Calls:            nothing
  272.  *    Called By:        <general purpose>
  273.  *    Globals Used:    none
  274.  *    Parameters:        pointer to the directory management block
  275.  *    Returns:        index of the next directory entry to be read.
  276.  *
  277.  ****************************************************************************************/
  278.  
  279. long    telldir(DIR *dirp)
  280.     {
  281.     if (dirp->dd_off > dirp->dd_numents)
  282.         return -1;
  283.     else
  284.         return dirp->dd_off-1;    /* The -1 is because Macs start at 1 & not 0 for dir index,
  285.                                  * and this is a little more POSIX.
  286.                                  */
  287.     }
  288.  
  289. /****************************************************************************************
  290.  *
  291.  *    This function closes the directory opened with opendir() or hopendir()
  292.  *
  293.  *    Calls:            DisposHandle(), RecoverHandle()
  294.  *    Called By:        <general purpose>
  295.  *    Globals Used:    none
  296.  *    Parameters:        pointer to the directory management block
  297.  *    Returns:        0 (always successful)
  298.  *
  299.  ****************************************************************************************/
  300.  
  301. int    closedir(DIR *dirp)
  302.     {
  303.     struct dirent    **cur;
  304.     
  305.     /*    Dispose of any directory entries read in.    */
  306.     cur    = dirp->dd_buf;
  307.     
  308.     dd_errno    = noErr;
  309.  
  310.     while (cur)
  311.         {
  312.         struct dirent    **next;
  313.         
  314.         next    = (*cur)->next;
  315.         
  316.         DisposHandle((Handle) cur);
  317.         
  318.         if (dd_errno == noErr)
  319.             dd_errno    = MemError();
  320.  
  321.         cur        = next;
  322.         }
  323.  
  324.     /*    Dispose of the directory managment block    */
  325.     DisposHandle(RecoverHandle((Ptr) dirp));
  326.  
  327.     if (dd_errno == noErr)
  328.         dd_errno    = MemError();
  329.  
  330.     return dd_errno?-1:0;
  331.     }
  332.  
  333. /****************************************************************************************
  334.  *
  335.  *    This function sets the index of the next-read directory entry.  It will also search
  336.  *    the list of read entries so that an entry won't be read from disk more than once.
  337.  *
  338.  *    Calls:            nothing
  339.  *    Called By:        <general purpose>
  340.  *    Globals Used:    none
  341.  *    Parameters:        pointer to the directory management block, index of directory
  342.  *    Returns:        nothing
  343.  *
  344.  ****************************************************************************************/
  345.  
  346. void    seekdir(DIR *dirp, long loc)
  347.     {
  348.     struct dirent    **cur;
  349.     
  350.     dirp->dd_off        = loc+1;    /* The +1 is because the Mac indexes directories
  351.                                      * from 1 and not 0 and we want to be a little bit
  352.                                      * POSIX
  353.                                      */
  354.  
  355.     /*    Search through the entries that we've read already    */
  356.     cur    = dirp->dd_buf;
  357.     
  358.     while (cur)
  359.         {
  360.         /*    If we find the entry that we've seeked to, set up so that readdir() will
  361.          *    return this one instead of reading a new one.
  362.          */
  363.         if (loc == (*cur)->d_off)
  364.             {
  365.             dirp->dd_cached        = true;
  366.             dirp->dd_cache_hint    = cur;
  367.  
  368.             return;
  369.             }
  370.  
  371.         cur    = (*cur)->next;
  372.         }
  373.  
  374.     /*    If we didn't find it, then tell readdir() to get the entry from the FS    */
  375.     dirp->dd_cached    = false;
  376.     }
  377.  
  378. /****************************************************************************************
  379.  *
  380.  *    This function will read the next directory entry from disk.  It will return nil and
  381.  *    set dd_errno to noErr when the end of the directory is reached.  It will avoid
  382.  *    reading directory entries from disk more than once.
  383.  *
  384.  *    Calls:            nothing
  385.  *    Called By:        <general purpose>
  386.  *    Globals Used:    none
  387.  *    Parameters:        pointer to the directory management block
  388.  *    Returns:        pointer to directory entry or nil if an error occurred and dd_errno
  389.  *                    will be set.  If the last entry has already been read, this will
  390.  *                    return nil and dd_errno will be set to noErr.
  391.  *
  392.  ****************************************************************************************/
  393.  
  394. struct dirent    *readdir(DIR *dirp)
  395.     {
  396.     CInfoPBRec        cpb;
  397.     struct dirent    **meh, *me;
  398.     
  399.     /*    If the entry has been read already, then return the already present entry    */
  400.     if (dirp->dd_cached)
  401.         me    = *(dirp->dd_cache_hint);
  402.     else
  403.         /*    Otherwise, read it from disk...    */
  404.         {
  405.         /*    Past the end of the directory?    */
  406.         if (dirp->dd_off > dirp->dd_numents)
  407.             {
  408.             dd_errno    = noErr;
  409.             return nil;
  410.             }
  411.  
  412.         /*    Allocate space for a new entry    */
  413.         meh    = (struct dirent **) NewHandle(sizeof(struct dirent));
  414.         
  415.         /*    Enough memory?    */
  416.         if (meh == nil)
  417.             {
  418.             dd_errno    = MemError();
  419.             return nil;
  420.             }
  421.  
  422.         /*    Lock the entry    */
  423.         MoveHHi((Handle) meh);
  424.         HLock((Handle) meh);
  425.  
  426.         me    = *meh;
  427.  
  428.         /*    Get the entry's info from disk    */
  429.         me->fsp.name[0]                = 0;
  430.  
  431.         cpb.dirInfo.ioCompletion    = nil;
  432.         cpb.dirInfo.ioNamePtr        = me->fsp.name;
  433.         cpb.dirInfo.ioVRefNum        = dirp->dir_fsp.vRefNum;
  434.         cpb.dirInfo.ioFDirIndex        = dirp->dd_off;
  435.         cpb.dirInfo.ioDrDirID        = dirp->dd_fd;
  436.  
  437.         if (dd_errno = PBGetCatInfo(&cpb, false))
  438.             {
  439.             DisposHandle((Handle) meh);
  440.             return nil;
  441.             }
  442.     
  443.         /*    Set up the dirent structure    */
  444.         me->d_off            = dirp->dd_off-1;
  445.         me->fsp.vRefNum        = cpb.dirInfo.ioVRefNum;
  446.         me->d_fileno        = cpb.dirInfo.ioDrDirID;
  447.         me->d_parent        = cpb.dirInfo.ioDrParID;
  448.         
  449.         /*    C strings only!    */
  450.         PtoCstr(me->fsp.name);
  451.  
  452.         /*    Add it to the list for this directory    */
  453.         me->next            = dirp->dd_buf;
  454.         
  455.         dirp->dd_buf        = meh;
  456.         }
  457.  
  458.     /*    Seek to the next entry    */
  459.     seekdir(dirp, dirp->dd_off);
  460.  
  461.     /*    Return what we've found    */
  462.     return me;
  463.     }
  464.  
  465. /****************************************************************************************
  466.  *
  467.  *    This function will give an absolute pathname to a given directory.
  468.  *
  469.  *    Calls:            NewPtr(), DisposPtr(), PBGetCatInfo()
  470.  *    Called By:        <general purpose>
  471.  *    Globals Used:    none
  472.  *    Parameters:        vRefNum and startDirID of desired path, pointer to path name storage,
  473.  *                    length of path name storage, pointer to C-string separator.
  474.  *    Returns:        bdNamErr if the path would overflow the storage,
  475.  *                    some other error code if something else happened,
  476.  *                    or noErr on success.
  477.  *
  478.  ****************************************************************************************/
  479.  
  480. OSErr    hgetwd(short vRefNum, long startDirID, char *path, int max_path_len, char *sep)
  481.     {
  482.     long        curDirID;
  483.     OSErr        err;
  484.     CInfoPBRec    pb;
  485.     Str63        name;
  486.     char        *temp_path;
  487.  
  488.     /*    Start with an empty path    */
  489.     path[0]    = 0;
  490.  
  491.     /*    Get memory for a temporary path    */
  492.     temp_path    = (char *) NewPtr(max_path_len);
  493.     
  494.     if (temp_path == nil)
  495.         return MemError();
  496.  
  497.     /*    Start at the given directory    */
  498.     curDirID    = startDirID;
  499.  
  500.     do    {
  501.         /*    Get cat info for the current directory    */
  502.         name[0]    = 0;
  503.  
  504.         pb.dirInfo.ioCompletion    = nil;
  505.         pb.dirInfo.ioNamePtr    = name;
  506.         pb.dirInfo.ioVRefNum    = vRefNum;
  507.         pb.dirInfo.ioFDirIndex    = -1;
  508.         pb.dirInfo.ioDrDirID    = curDirID;
  509.         
  510.         if (err = PBGetCatInfo(&pb, false))
  511.             {
  512.             DisposPtr((Ptr) temp_path);
  513.             return err;
  514.             }
  515.  
  516.         /*    Convert name to a C string    */
  517.         PtoCstr(name);
  518.  
  519.         /*    Check that we don't overflow storage    */
  520.         if ((strlen(name) + strlen(path) + strlen(sep)) >= max_path_len)
  521.             {
  522.             DisposPtr((Ptr) temp_path);
  523.             return bdNamErr;
  524.             }
  525.  
  526.         /*    Prepend the name and separator    */
  527.         strcpy(temp_path, path);
  528.         strcpy(path, name);
  529.         strcat(path, sep);
  530.         strcat(path, temp_path);
  531.  
  532.         /*    Move "up" one directory    */
  533.         curDirID    = pb.dirInfo.ioDrParID;
  534.         }
  535.     /*    Until we hit the root directory    */
  536.     while (pb.dirInfo.ioDrDirID != fsRtDirID);
  537.  
  538.     /*    Get rid of our temp storage and return    */
  539.     DisposPtr((Ptr) temp_path);
  540.  
  541.     return MemError();
  542.     }
  543.  
  544. /****************************************************************************************
  545.  *
  546.  *    This function will change the current working directory.
  547.  *
  548.  *    Calls:            opendir(), closedir(), PBHSetVol()
  549.  *    Called By:        <general purpose>
  550.  *    Globals Used:    none
  551.  *    Parameters:        C-string pathname.
  552.  *    Returns:        -1 on failure, 0 on success.  Sets dd_errno on failure.
  553.  *
  554.  ****************************************************************************************/
  555.  
  556. int    chdir(char *path)
  557.     {
  558.     DIR        *d;
  559.     short    vRefNum;
  560.     long    dirID;
  561.     WDPBRec    pb;
  562.  
  563.     /*    Open the directory    */
  564.     d    = opendir(path);
  565.     
  566.     if (d == nil)
  567.         return -1;
  568.  
  569.     /*    Get the Mac FS identification for this directory    */
  570.     vRefNum    = d->dd_volume;
  571.     dirID    = d->dd_fd;
  572.     
  573.     /*    Close the directory    */
  574.     closedir(d);
  575.  
  576.     /*    CD to the new directory    */
  577.     pb.ioCompletion    = nil;
  578.     pb.ioNamePtr    = nil;
  579.     pb.ioVRefNum    = vRefNum;
  580.     pb.ioWDDirID    = dirID;
  581.     
  582.     dd_errno = PBHSetVol(&pb, false);
  583.     
  584.     return dd_errno?-1:0;
  585.     }
  586.  
  587. /****************************************************************************************
  588.  *
  589.  *    This function will get the current working directory's path.
  590.  *
  591.  *    Calls:            PBHGetVol(), hgetwd()
  592.  *    Called By:        <general purpose>
  593.  *    Globals Used:    none
  594.  *    Parameters:        pointer to a buffer of MAXPATHLEN bytes.
  595.  *    Returns:        pointer to the buffer on success, on failure, nil and dd_errno will
  596.  *                    be set.
  597.  *
  598.  ****************************************************************************************/
  599.  
  600. char *getwd(char *path)
  601.     {
  602.     WDPBRec    pb;
  603.  
  604.     /*    Get the current working directory    */
  605.     pb.ioCompletion    = nil;
  606.     pb.ioNamePtr    = nil;
  607.     
  608.     if (dd_errno = PBHGetVol(&pb, false))
  609.         return nil;
  610.  
  611.     /*    Transform it into a path    */
  612.     if (dd_errno = hgetwd(pb.ioWDVRefNum, pb.ioWDDirID, path, MAXPATHLEN-1, dd_separator))
  613.         return nil;
  614.  
  615.     return path;
  616.     }
  617.  
  618. /****************************************************************************************
  619.  *
  620.  *    This function will get the path to a given (already opened) directory.
  621.  *
  622.  *    Calls:            hgetwd()
  623.  *    Called By:        <general purpose>
  624.  *    Globals Used:    none
  625.  *    Parameters:        pointer to a buffer of MAXPATHLEN bytes.
  626.  *    Returns:        pointer to the buffer on success, on failure, nil and dd_errno will
  627.  *                    be set.
  628.  *
  629.  ****************************************************************************************/
  630.  
  631. char *pathdir(DIR *dirp, char *path)
  632.     {
  633.     if (dd_errno = hgetwd(dirp->dd_volume, dirp->dd_fd, path, MAXPATHLEN-1, dd_separator))
  634.         return nil;
  635.  
  636.     return path;
  637.     }
  638.