//+==========================================================================
//
//  File:       CDir.cxx
//
//  Purpose:    Define the CDirectory class.
//
//              This class is used to represent a directory name.
//              Along with maintaining the name, it can determine
//              the type of FileSystem.
//
//+==========================================================================


//  --------
//  Includes
//  --------

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <wtypes.h>

#include "CFMEx.hxx"
#include "CDir.hxx"


//+------------------------------------------------------------------------------
//
//  Function:   CDirectory::Initialize (no arguments)
//
//  Synopsis:   Generate a directory name, using the TEMP environment
//              variable, and use it to initialize this object.
//
//  Inputs:     None.
//
//  Outputs:    TRUE if the function succeeds, FALSE otherwise.
//
//+------------------------------------------------------------------------------

BOOL CDirectory::Initialize()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    // Assume failure for now.
    BOOL bSuccess = FALSE;

    // The TEMP environment variable.
    WCHAR wszSystemTempPath[ MAX_PATH + sizeof( L'\0' )];

    // Reset the error code.
    m_lError = 0L;

    //  ----------
    //  Get %TEMP% 
    //  ----------

    if( !GetTempPath( MAX_PATH,
                      wszSystemTempPath )
      )
    {
        m_lError = GetLastError();
        EXIT( L"GetTempPath() failed (%d)" );
    }

    //  ----------------------
    //  Initialize this object
    //  ----------------------

    // Initialize using the temporary path.  We must never pass a NULL here,
    // or we'll cause an infinite recursion.

    if( wszSystemTempPath == NULL )
        EXIT( L"Invalid temporary path" );
    bSuccess = Initialize( wszSystemTempPath );

    //  ----
    //  Exit
    //  ----

Exit:

    return( bSuccess );

}

//+-----------------------------------------------------------------------
//
//  Function:   CDirectory::Initialize (with an ANSI string)
//
//  Synopsis:   This function converts the ANSI string to a Unicode
//              string, then initializes the object with it.
//
//  Inputs:     A Unicode string.
//
//  Outputs:    TRUE if the function succeeds, FALSE otherwise.
//
//+-----------------------------------------------------------------------

BOOL CDirectory::Initialize( LPCSTR szDirectory )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    // Assume failure.
    BOOL bSuccess = FALSE;

    // A buffer for the Unicode path
    WCHAR wszDirectory[ MAX_UNICODE_PATH + sizeof( L'\0' )];

    //  -----
    //  Begin
    //  -----

    // Initialize the error code.
    m_lError = 0L;


    // If we were givin a NULL path, use the version of Initialize
    // that requires no path.

    if( szDirectory == NULL )
    {
        bSuccess = Initialize();
        goto Exit;
    }

    // Convert the Ansi name to Unicode.

    if( m_lError = (long) AnsiToUnicode( szDirectory,
                                         wszDirectory,
                                         strlen( szDirectory )
                                       )
      )
    {
        EXIT( L"Unable to convert directory to Unicode" );
    }

    // Initialize using the temporary path.  We must never pass a NULL here,
    // or we'll cause an infinite recursion.

    if( wszDirectory == NULL )
        EXIT( L"Invalid Directory (internal error)" );
    bSuccess = Initialize( wszDirectory );

    //  ----
    //  Exit
    //  ----

Exit:

    return( bSuccess );

}


//+-----------------------------------------------------------------------
//
//  Function:   CDirectory::Initialize (with a Unicode string)
//
//  Synopsis:   This function is the only form of Initialize
//              (there are several variations of the Initialize member)
//              which really initializes the object.  It stores the
//              directory name, and determines the type of filesystem
//              on which it resides.
//
//  Inputs:     A Unicode string.
//
//  Outputs:    TRUE if the function succeeds, FALSE otherwise.
//
//+-----------------------------------------------------------------------

BOOL CDirectory::Initialize( LPCWSTR wszDirectory )
{

    //  ---------------
    //  Local Variables
    //  ---------------

    // Assume failure.
    BOOL bSuccess = FALSE;

    // Buffers for the root of the path and for the volume name.
    WCHAR wszDirectoryRoot[ MAX_PATH + sizeof( L'\0' )];
    WCHAR wszVolumeName[ MAX_PATH + sizeof( L'\0' )];

    // Parameters to GetVolumeInformation which we won't use.
    DWORD   dwMaxComponentLength = 0L;
    DWORD   dwFileSystemFlags = 0L;

    //  -----
    //  Begin
    //  -----

    // Initialize the error code.
    m_lError = 0L;

    // If we were given a NULL path, use the variation of Initialization()
    // which does not require one.  Note that we will then be called again,
    // but this time with a path.

    if( wszDirectory == NULL )
    {
        bSuccess = Initialize();
        goto Exit;
    }

    // Validate the path.

    if( wcslen( wszDirectory ) > MAX_PATH )
    {
        m_lError = wcslen( wszDirectory );
        EXIT( L"Input path is too long (%d)\n" );
    }

    // Save the path to our member buffer

    wcscpy( m_wszDirectory, wszDirectory );


    //  ------------------------
    //  Get the file system name
    //  ------------------------

    // Get the root path to the directory.

    wcscpy( wszDirectoryRoot, wszDirectory );
    MakeRoot( wszDirectoryRoot );

    // Get the volume information, which will include the filesystem name.

    if( !GetVolumeInformation(  wszDirectoryRoot,   // Root path name.
                                wszVolumeName,      // Buffer for volume name
                                MAX_PATH,           // Length of the above buffer
                                NULL,               // Buffer for serial number
                                                    // Longest filename length.
                                &dwMaxComponentLength,
                                &dwFileSystemFlags, // Compression, etc.
                                m_wszFileSystemName,// Buffer for the FS name.
                                MAX_PATH )          // Length of above buffer
      )
    {
        m_lError = GetLastError();
        EXIT( L"GetVolumeInformation() failed" );
    }


    // Determine the file system type from the name.

    if( !wcscmp( m_wszFileSystemName, L"FAT" ))
        m_FileSystemType = fstFAT;

    else if( !wcscmp( m_wszFileSystemName, L"NTFS" ))
        m_FileSystemType = fstNTFS;

    else if( !wcscmp( m_wszFileSystemName, L"OFS" ))
        m_FileSystemType = fstOFS;

    else
        m_FileSystemType = fstUnknown;

    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CDirectory::Initialize( wszDirectory )" );
    return( bSuccess );

}



//
//  GetRootLength
//
//  This routine was simply copied from private\windows\shell\shelldll\tracker.cxx,
//  and should not be modified here.
//

unsigned
CDirectory::GetRootLength(const WCHAR *pwszPath)
{
    ULONG   cwcRoot = 0;
    m_lError = 0L;

    if (pwszPath == 0)
        pwszPath = L"";

    if (*pwszPath == L'\\')
    {
        //  If the first character is a path separator (backslash), this
        //  must be a UNC drive designator which must be of the form:
        //      <path-separator><path-separator>(<alnum>+)
        //          <path-separator>(<alnum>+)<path-separator>
        //
        //  This covers drives like these: \\worf\scratch\ and
        //  \\savik\win4dev\.
        //
        pwszPath++;
        cwcRoot++;

        BOOL    fMachine = FALSE;
        BOOL    fShare   = FALSE;

        if (*pwszPath == L'\\')
        {
            cwcRoot++;
            pwszPath++;

            while (*pwszPath != '\0' && *pwszPath != L'\\')
            {
                cwcRoot++;
                pwszPath++;

                fMachine = TRUE;
            }

            if (*pwszPath == L'\\')
            {
                cwcRoot++;
                pwszPath++;

                while (*pwszPath != '\0' && *pwszPath != L'\\')
                {
                    cwcRoot++;
                    pwszPath++;

                    fShare = TRUE;
                }

                //  If there weren't any characters in the machine or
                //  share portions of the UNC name, then the drive
                //  designator is bogus.
                //
                if (!fMachine || !fShare)
                {
                    cwcRoot = 0;
                }
            }
            else
            {
                cwcRoot = 0;
            }
        }
        else
        {
            cwcRoot = 0;
        }
    }
    else
    if (iswalpha(*pwszPath))
    {
        //  If the first character is an alphanumeric, we must have
        //  a drive designator of this form:
        //      (<alnum>)+<drive-separator><path-separator>
        //
        //  This covers drives like these: a:\, c:\, etc
        //

        pwszPath++;
        cwcRoot++;

        if (*pwszPath == L':')
        {
            cwcRoot++;
            pwszPath++;
        }
        else
        {
            cwcRoot = 0;
        }
    }

    //  If we have counted one or more characters in the root and these
    //  are followed by a component separator, we need to add the separator
    //  to the root length.  Otherwise this is not a valid root and we need
    //  to return a length of zero.
    //
    if ((cwcRoot > 0) && (*pwszPath == L'\\'))
    {
        cwcRoot++;
    }
    else
    {
        cwcRoot = 0;
    }

    return (cwcRoot);
}

//
//  MakeRoot
//
//  This routine was simply copied from private\windows\shell\shelldll\tracker.cxx,
//  and should not be modified here.
//


VOID
CDirectory::MakeRoot(WCHAR *pwszPath)
{
    unsigned rootlength = GetRootLength(pwszPath);
    m_lError = 0L;

    if (rootlength)
    {
        pwszPath[rootlength] = L'\0';
    }
}