/*static char *SCCSID = "@(#)fs_chdir.c 6.2 91/01/14";*/
#define SCCSID "@(#)fs_chdir.c  6.2 91/01/14"
/*
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 */

/*
 *  FS_CHDIR.C
 *
 *    DESCRIPTION
 *
 *      Change/Verify the directory path.
 *
 *
 *    FUNCTIONS DEFINED
 *
 *      FS_CHDIR () -   Change/Verify the directory path.
 *
 *
 *  REVISION HISTORY (in reverse chronological order)
 *  -----------------------------------------------------------------------
 *   1/21/93    Kathy Muzzio    Added local RamSem for sDirSem to
 *                              correct semaphore hang on return from
 *                              subdirectory to root on Subst/Joined
 *                              drive is mvdm session
 *
 *  11/14/89    Brian Andrew    Added DBCS support (ForwardDirDBCS)
 *  10/18/89    Brian Andrew    Allocate/Save/Deallocate a Path Entry
 *                              structure for the current directory.
 *
 *  8/25/89     Brian Andrew    Optimize path using CDI_ISVALID (ignore
 *                              CDI_ISCURRENT)
 *
 *  8/23/89     Brian Andrew    Initial working version
 *
 *  8/4/89      Brian Andrew    Initial Blank Template
 *
 *
 *  TO DO
 *  -------------------------------------------------------------------------
 *  Consider saving the directory entry for the current directory in the
 *  dependent area.  It will save time for all searches later that use the
 *  current directory.
 */

#define INCL_DOSERRORS
#define INCL_NOPMAPI
#include <os2.h>
#include <fsd.h>
#include <fsh.h>

#include "cdfs.h"
#include "sem.h"
#include "vol.h"
#include "path.h"
#include "buffer.h"
#include "mem.h"
#include "heap.h"
#include "message.h"

#ifdef STATS_H
#include "stats.h"
#endif

#include "fsdata.h"
#include "pr_memin.h"
#include "pr_path.h"
#include "pr_strng.h"
#include "pr_dbcs.h"
#include "file.h"

#ifdef DEBUG
#include "pr_DEBUG.h"
#endif


/***    FS_CHDIR () -   Change/Verify the directory path.
 *
 *  DESCRIPTION
 *
 *      This function is called to change a directory, verify the current
 *      directory or free resources allocated with a directory.  The actions
 *      taken in verifying a directory are the same as those for changing
 *      directories except that the full path name is stored in the
 *      directory independent structure instead of the arguement name.
 *
 *      If the action is verify then we point the arguement name to the name
 *      in the cdfsi and change the operation flag to CD_EXPLICIT.
 *
 *      At this point we don't use the directory dependent area to store
 *      any far pointers, so there is no action we take on freeing old
 *      resources.
 *
 *      If we are verifying or changing directories, we first determine
 *      where in the path we must start (root dir or current dir) and the
 *      path number.  We call TracePath with that starting point and the
 *      path name from that point on.  It looks in the path table to see
 *      if the directory exists.
 *
 *
 *  ENTRY
 *
 *      flag        =   Flag for type of operation to perform.
 *      pcdfsi      =   Pointer to directory independent area.
 *      pcdfsd      =   Pointer to directory dependent area.
 *      pDir        =   Pointer to full path name including <drive>:\
 *      iCurDirEnd  =   Index in full path name of the end of the current
 *                      directory.
 *
 *
 *  LOCAL
 *
 *      usRet       =   Function return value.
 *      pvpfsi      =   Pointer to volume independent data.
 *      pvddVolData =   Pointer to volume dependent data.
 *      cbStepLen   =   Length to step over drive name in path name.
 *      cbDirLen    =   Full length of directory name.
 *      pnDirStart  =   Directory number for directory to start search in.
 *      ppePathEntry =  Pointer to Path Entry structure
 *      fVerify     =   Flag indicating if this is a verify operation.
 *
 *
 *  EXIT
 *
 *      NO_ERROR  if operation is successful.
 *      Return code from GETVOLPARM if it fails.
 *      Return code from TracePath if it fails.
 *
 */


int far pascal
FS_CHDIR (flag, pcdfsi, pcdfsd, pDir, iCurDirEnd)
USHORT                  flag;
PCDFSI                  pcdfsi;
PCDFSD                  pcdfsd;
PCHAR                   pDir;
USHORT                  iCurDirEnd;
{

        USHORT              usRet;
        PVPFSI              pvpfsi;
        PVOL_DEP_DATA       pvddVolData;
        USHORT              cbStepLen;
        USHORT              cbDirLen;
        PATH_NUMBER         pnDirStart;
        PPATH_ENTRY         ppePathEntry;
        BOOL                fVerify;
        SEM                 sDirSem = 0;        // @5235 use loacl stack RamSEM

#ifdef DEBUG
        dprintf ("FS_CHDIR: flag=%d", flag);

        if (flag < CD_FREE)
                dprintf (", Root=%d, Valid=%d",
                         pcdfsi->cdi_flags & CDI_ISROOT,
                         pcdfsi->cdi_flags & CDI_ISVALID);

        if (flag < CD_VERIFY)
                dprintf (", Index=%d", iCurDirEnd);


        if (flag == CD_EXPLICIT)
                dprintf (", %Fs", pDir);

        dprintf ("\n");
#endif

#ifdef STATS_H
                CurSt.st_chdir++;
#endif

        /* Check for initialization */
        if (usRet = CHECK_INIT)
                return usRet;


        /*
         *  If flag is CD_VERIFY then by pointing to the value in the
         *  cdi_text and computing its length, we can tread this as
         *  CS_EXPLICIT.
         */
        /* Assume this is not a verify operation */
        fVerify = FALSE;

        if (flag == CD_VERIFY)
        {
                pDir        = pcdfsi->cdi_curdir;
                iCurDirEnd  = NO_PREV_DIR;
                flag        = CD_EXPLICIT;
                fVerify     = TRUE;

        } /* end if flag is VERIFY */

        /*
         *  If the flag is CD_EXPLICIT, then get the volume data, step over
         *  the leading drive name and colon.  Then call TracePath to
         *  see if the directory exists.
         */

        switch (flag)
        {
                case CD_EXPLICIT :

                        /* Try to get the volume dependent data */
                        if (!(usRet = FSH_GETVOLPARM (
                                                                pcdfsi->cdi_hVPB,
                                                                &pvpfsi,
                                                                (PPVPFSD) &pvddVolData)))
                        {
                                /* Now try to get a structure for the Path Entry */
                                if (usRet =
                                        GetPathEntry (&ppePathEntry))
                                {
#ifdef DEBUG
                                        dprintf ("\tFS_CHDIR:   GetPathEntry    --  %d\n",
                                                                usRet);
#endif
                                }

                        } /* end got volume parameters */

#ifdef DEBUG
                        /* Else couldn't get volume parameters */
                        else
                        {
                                dprintf ("\tFS_CHDIR: GETVOLPARMS  -- %d\n", usRet);

                        } /* end else get vol params failed */
#endif

                        /* Continue search if no errors */
                        if (!usRet)
                        {
                                /* What distance to step over drive name and : */
                                /* Distance is either to the first '\' or if optimization
                                 * allowed, the iCurDirEnd - 1
                                 */

                                cbDirLen = istrlen (pDir);

                                if ((iCurDirEnd != NO_PREV_DIR) &&
                                        (pcdfsi->cdi_flags & CDI_ISVALID))
                                {
                                        pnDirStart = ((PDIR_DEP_DATA) pcdfsd)->ppePathEntry->pnThisEntry;
                                        cbStepLen = iCurDirEnd - 1;

                                } /* end use previous */

                                /* Else use the root directory */
                                else
                                {
                                        cbStepLen = ForwardDirDBCS (pDir, cbDirLen);
                                        pnDirStart = ROOT_DIR_NUM;

                                } /* else start from root */

                                /*
                                 *  Search for the directory and store the directory
                                 *  number in the directory dependent area if found.
                                 */

                                if (!(usRet = TracePath (pvddVolData, pnDirStart,
                                                                                 pDir + cbStepLen,
                                                                                 cbDirLen - cbStepLen,
                                                                                 ppePathEntry,
                                                                                 pcdfsi->cdi_hVPB)))
                                {
                                        /*
                                         *  Free the existing PE if there and if this operation
                                         *  is really a VERIFY operation.
                                         */
                                        if ((pcdfsi->cdi_flags & CDI_ISVALID) &&
                                                        fVerify &&
                                                        (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry))
                                        {
                                                SEM_REQ (sDirSem);      // @5235 use loacl stack RamSEM

                                                /* Check for whether there is a DirEntry attached */
                                                if (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry->pdeDirEntry)
                                                {
                                                        FREE_DIR_ENTRY (
                                                                ((PDIR_DEP_DATA) pcdfsd)->ppePathEntry->pdeDirEntry);

                                                } /* end if DirEntry attached */

                                                FREE_PATH_ENTRY (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry);

                                                SEM_CLEAR (sDirSem);    // @5235 use loacl stack RamSEM

                                        } /* end if existing PE */

                                        ((PDIR_DEP_DATA) pcdfsd)->ppePathEntry = ppePathEntry;
                                } /* end found explicit */

                                /* Else path not found */
                                else
                                {
                                        /* Free the newly allocated PE */
                                        FREE_PATH_ENTRY (ppePathEntry);

#ifdef DEBUG
                                        dprintf ("\tFS_CHDIR:  TracePath  --  %d\n", usRet);
#endif

                                } /* end path not found */

                        } /* end if no error */

                        break;

                case CD_FREE :

                        /* Free the PE if it exists */
                        if (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry)
                        {
                                SEM_REQ (sDirSem);      // @5235 use loacl stack RamSEM

                                /* Check for whether there is a DirEntry attached */
                                if (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry->pdeDirEntry)
                                {
                                        FREE_DIR_ENTRY (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry->pdeDirEntry);

                                } /* end if DirEntry attached */

                                FREE_PATH_ENTRY (((PDIR_DEP_DATA) pcdfsd)->ppePathEntry);

                                SEM_CLEAR (sDirSem);    // @5235 use loacl stack RamSEM

                        } /* end if PE exists */

                        break;

                default :

                        usRet = ERROR_INVALID_PARAMETER;
                        break;

        } /* end switch */

       /* Free the semaphore if it is being held */
       if (sDirSem)                     // @5235 use loacl stack RamSEM
       {
               SEM_CLEAR (sDirSem);    // @5235  use local stack RamSEM

       } /* end if semaphore is being held */


#ifdef DEBUG
        dprintf ("    FS_CHDIR: Exit = %d\n", usRet);
#endif

        return usRet;

}   /* end FS_CHDIR */
