//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       filelist.h
//
//--------------------------------------------------------------------------

#ifndef _INC_CSCVIEW_FILELIST_H
#define _INC_CSCVIEW_FILELIST_H
//////////////////////////////////////////////////////////////////////////////
/*  File: filelist.h

    Description: Simplifies the transmission of a list of share and 
        associated file names between components of the CSC UI.  See
        description further below for details.

        Classes:

            CscFilenameList
            CscFilenameList::HSHARE
            CscFilenameList::ShareIter
            CscFilenameList::FileIter

        Note:  This module was written to be used by any part of the CSCUI,
               not just the viewer.  Therefore, I don't assume that the
               new operator will throw an exception on allocation failures.
               I don't like all the added code to detect allocation failures
               but it's not reasonable to expect code in the other components
               to become exception-aware with respect to "new" failures.

    Revision History:

    Date        Description                                          Programmer
    --------    ---------------------------------------------------  ----------
    11/28/97    Initial creation.                                    BrianAu
*/
///////////////////////////////////////////////////////////////////////////////
#ifndef _WINDOWS_
#   include <windows.h>
#endif

//
// This set of classes is designed to transfer a list of network share
// names and network file names between components of the CSC UI
// (i.e. wizard, viewer, onestop etc).  Data is transferred through
// a formatted byte buffer that is essentially an opaque blob
// to both the sender and receiver.  The class CscFilenameList
// was created to prevent the sender and receiver from having
// to understand the buffer format.  The buffer is formatted as follows:
//
// +----------------+---------------------------------+
// | <namelist hdr> |  <share descriptors>            |
// +----------------+---------------------------------+
// |                                                  |
// |                                                  |
// |         <share and file names>                   |
// |                                                  |
// |                                                  |
// +--------------------------------------------------+
//
//
// Where: <namelist hdr> is a single block of type CSC_NAMELIST_HDR.
//              This block describes the size of the buffer and the
//              share count.
//
//        <share descriptors> is an array of type CSC_NAMELIST_SHARE_DESC,
//              one for each share in the buffer.  Each share descriptor
//              describes the offset to the share name, the offset
//              to the first file name and the number of file names
//              in the buffer.  All offsets are byte offsets from the
//              start of the buffer.
//
//        <share and file names> is an array of TCHARs containing the
//              names of the shares and files stored in the buffer.
//              Each name string is nul-terminated.
//
//
// The following outline describes how I see someone using this feature:
//
//  1. Instantiate a CscFilenameList object (no ctor arguments).
//  2. Call AddShare() whenever a share is to be added.
//  3. Call AddFile() whenever a file is to be added.  Note that
//     saving the HSHARE returned by AddShare() can make file name addition
//     more efficient as it eliminates the need for an internal lookup
//     of the associated share name each time a file name is added.
//
//      i.e.:  CscFilenameList fnl;
//             HSHARE hShare = fnl.AddShare("\\\\server\\share");
//             fnl.AddFile(hShare, "foo.txt");
//             fnl.AddFile(hShare, "bar.txt");
//
//             is more efficient than...
//
//             CscFilenameList fnl;
//             fnl.AddFile("\\\\server\\share", "foo.txt");
//             fnl.AddFile("\\\\server\\share", "bar.txt");
//
//             ...which is slightly more efficient than...
//
//             CscFilenameList fnl;
//             fnl.AddFile("\\\\server\\share\\foo.txt");
//             fnl.AddFile("\\\\server\\share\\bar.txt");
//
//             ...although all 3 methods are supported.
//
//  4. Once all shares and files are added, call CreateListBuffer() to retrieve
//     the information formatted in a byte buffer.  The buffer is allocated
//     on the heap.
//  5. Pass the buffer address to the desired program component.
//  6. The receiving component instantiates a CscFilenameList object passing
//     the address of the buffer to the ctor.  This initializes the namelist
//     so that it merely references the information in the buffer rather than
//     duplicating the name information in memory.  Note that the buffer must
//     remain in memory while this namelist object is in use.
//  7. The receiving component creates a Share Iterator by calling
//     CreateShareIterator().  The returned iterator enumerates each of the
//     shares contained in the namelist object.  
//  8. The receiving component enumerates the shares (receiving an HSHARE) for 
//     each.  To get a share's name string, call GetShareName() passing the
//     HSHARE for the desired share.
//  9. For each share, the recieving component creates a File Iterator by
//     calling CreateFileIterator() passing the HSHARE for the desired share.
//     The returned iterator enumerates each of the file names associated 
//     with the share.
// 10. Once the operation is complete, FreeListBuffer is called to delete
//     the byte buffer created by CreateListBuffer().
//
//
// The following example illustrates this process:
//
//
//  void Foo(void)
//  {
//      CscFilenameList         fnl;     // Namelist object.
//      CscFilenameList::HSHARE hShare;  // Share handle.
//
//      // Add a share and some files
//
//      fnl.AddShare(TEXT("\\\\worf\\ntspecs"), &hShare);
//      fnl.AddFile(hShare, TEXT("foo.txt"));
//      fnl.AddFile(hShare, TEXT("bar.txt"));
//
//      // Add another share and some files.
//
//      fnl.AddShare(TEXT("\\\\msoffice\\products"), &hShare);
//      fnl.AddFile(hShare, TEXT("word.doc"));
//      fnl.AddFile(hShare, TEXT("excel.doc"));
//      fnl.AddFile(hShare, TEXT("powerpoint.doc"));
//      fnl.AddFile(hShare, TEXT("access.doc"));
//
//      // Add another share and more files using the less-efficient
//      // method.  It's valid, just less efficient.
//
//      fnl.AddFile(TEXT("\\\\performance\\poor"), TEXT("turtle.doc"));
//      fnl.AddFile(TEXT("\\\\performance\\poor"), TEXT("snail.doc"));
//      fnl.AddFile(TEXT("\\\\performance\\poor"), TEXT("inchworm.doc"));
//
//      // Create the byte buffer from the namelist and pass it to
//      // the receiving component.
//
//      LPBYTE pbBuffer = fnl.CreateListBuffer();
//      Bar(pbBuffer);
//
//      // Delete the byte buffer when we're done.
//
//      FreeListBuffer(pbBuffer);
//  }
//
//
//  void Bar(LPBYTE pbBuffer)
//  {
//      // Create a new namelist object from the byte buffer.
//
//      CscFileNameList fnl(pbBuffer);
//
//      // Create a share iterator.
//      
//      CscFilenameList::ShareIter si = fnl.CreateShareIterator();
//      CscFilenameList::HSHARE hShare;
//
//      // Iterate over the shares in the namelist collection.
//
//      while(si.Next(&hShare))
//      {
//          _tprintf(TEXT("Share..: \"%s\"\n"), fnl.GetShareName(hShare));
//
//          // Create a file iterator for the share.
//
//          CscFilenameList::FileIter fi = fl.CreateFileIterator(hShare);
//          LPCTSTR pszFile;
//
//          // Iterate over the filenames associated with the share.
//
//          while(pszFile = fi.Next())
//          {
//              _tprintf(TEXT("\tFile..: \"%s\"\n"), pszFile);
//          }
//      }
//  }
//
//  [brianau - 11/28/97]
//

//
// Namelist byte buffer header block (offset 0).
//
typedef struct 
{
    DWORD cbSize;
    DWORD cShares;

} CSC_NAMELIST_HDR, *PCSC_NAMELIST_HDR;

//
// Namelist byte buffer share descriptor block.  Array (one per share)
// starting at offset 12 (immediately following the header block).
//
typedef struct
{
    DWORD cbOfsShareName;
    DWORD cbOfsFileNames;
    DWORD cFiles;

} CSC_NAMELIST_SHARE_DESC, *PCSC_NAMELIST_SHARE_DESC;



class CscFilenameList
{
    private:
        class Share;

    public:
        class ShareIter;
        class FileIter;
        class HSHARE;

        // --------------------------------------------------------------------
        // CscFilenameList::HSHARE
        //
        // Share "handle" to communicate the identity of an internal "share"
        // object with the client without exposing the share object directly.
        // The client just knows an HSHARE.  Clients get one as the return
        // value from AddShare().
        //
        class HSHARE
        {
            public:
                HSHARE(void);
                HSHARE(const HSHARE& rhs);
                HSHARE& operator = (const HSHARE& rhs);
                ~HSHARE(void) { }

                bool operator ! ()
                    { return NULL == m_pShare; }

            private:
                //
                // Private so only we can create meaninful HSHARE objects.
                //
                HSHARE(Share *pShare)
                    : m_pShare(pShare) { }

                Share *m_pShare;  // ptr to the actual share object.

                friend class CscFilenameList;
                friend class ShareIter;
        };

        // --------------------------------------------------------------------
        // CscFilenameList::FileIter
        //
        // Iterator for enumerating each file name associated with a 
        // particular share. Clients create one using CreateFileIterator().
        //
        class FileIter
        {
            public:
                FileIter(void);
                ~FileIter(void) { }
                FileIter(const FileIter& rhs);
                FileIter& operator = (const FileIter& rhs);

                LPCTSTR Next(void);
                void Reset(void);

            private:
                FileIter(const Share *pShare);

                const Share *m_pShare; // ptr to associated share object.
                int          m_iFile;  // current file iteration index.

                friend class CscFilenameList;
        };

        // --------------------------------------------------------------------
        // CscFilenameList::ShareIter
        //
        // Iterator for enumerating each share in the namelist collection.
        // Clients create one using CreateShareIterator().
        //
        class ShareIter
        {
            public:
                ShareIter(void);
                ShareIter(const CscFilenameList& fnl);
                ~ShareIter(void) { }
                ShareIter(const ShareIter& rhs);
                ShareIter& operator = (const ShareIter& rhs);

                bool Next(HSHARE *phShare);
                void Reset(void);

            private:
                const CscFilenameList *m_pfnl; // ptr to filename collection obj.
                int   m_iShare;                // current share iteration index.
        };

        // --------------------------------------------------------------------
        // Namelist object public interface.
        //
        // Create an empty namelist collection ready to accept share and
        // file names.
        //
        CscFilenameList(void);
        //
        // Create a namelist collection and initialize it with the contents
        // of a byte buffer created by CreateListBuffer().  
        // If bCopy is false, the subsequent namelist object merely references 
        // the data in the byte buffer rather than duplicating the namestrings 
        // in memory.  If bCopy is true, name strings are created as if the
        // names had been added using AddShare() and AddFile().  Note that
        // additional share and file name strings may be added at any time.
        // However, they are added to internal structures and not to the 
        // byte buffer.  Call CreateListBuffer() to add them to a new byte
        // buffer.  
        //
        CscFilenameList(PCSC_NAMELIST_HDR pbNames, bool bCopy);
        ~CscFilenameList(void);
        //
        // Add a share name to the collection.  Does not create a 
        // duplicate share entry if one already exists.  Returns a handle
        // the a share object.
        //
        bool AddShare(LPCTSTR pszShare, HSHARE *phShare, bool bCopy = true);
        //
        // Add a file for a share.  More efficient to use the first
        // version taking a share handle rather than a share name.
        //
        bool AddFile(HSHARE& hShare, LPCTSTR pszFile, bool bDirectory = false, bool bCopy = true);
        bool AddFile(LPCTSTR pszShare, LPCTSTR pszFile, bool bDirectory = false, bool bCopy = true);
        bool AddFile(LPCTSTR pszFullPath, bool bDirectory = false, bool bCopy = true);
        bool RemoveFile(HSHARE& hShare, LPCTSTR pszFile);
        bool RemoveFile(LPCTSTR pszShare, LPCTSTR pszFile);
        bool RemoveFile(LPCTSTR pszFullPath);
        //
        // Retrieve miscellaneous information about the collection.
        //
        int GetShareCount(void) const;
        int GetFileCount(void) const;
        LPCTSTR GetShareName(HSHARE& hShare) const;
        int GetShareFileCount(HSHARE& hShare) const;
        bool GetShareHandle(LPCTSTR pszShare, HSHARE *phShare) const;
        //
        // Determine if a given share or file exists in the collection.
        // For the FileExists() functions, if bExact is true (the default)
        // only exact character-for-character matches return true.  If
        // bExact is false, the filename "\\server\share\dirA\dirB\foo.txt"
        // will match if any of the following four entries exist in the
        // namelist:
        //
        //     "\\server\share\dirA\dirB\foo.txt"  (exact match)
        //     "\\server\share\*"                  (wildcard match)
        //     "\\server\share\dirA\*"             (wildcard match)
        //     "\\server\share\dirA\dirB\*"        (wildcard match)
        //
        bool ShareExists(LPCTSTR pszShare) const;
        bool FileExists(HSHARE& hShare, LPCTSTR pszFile, bool bExact = true) const;
        bool FileExists(LPCTSTR pszShare, LPCTSTR pszFile, bool bExact = true) const;
        bool FileExists(LPCTSTR pszFullPath, bool bExact = true) const;

        //
        // Create iterators for enumerating collection contents.
        //
        ShareIter CreateShareIterator(void) const;
        FileIter CreateFileIterator(HSHARE& hShare) const;
        //
        // Create/free a byte buffer containing the contents of the collection.
        //
        PCSC_NAMELIST_HDR CreateListBuffer(void) const;
        static void FreeListBuffer(PCSC_NAMELIST_HDR pbNames);
        //
        // Check after initializing object from byte buffer.
        //
        bool IsValid(void) const
            { return m_bValid; }

#ifdef FILELIST_TEST
        void Dump(void) const;
        void DumpListBuffer(PCSC_NAMELIST_HDR pbBuffer) const;
#endif // FILELIST_TEST


    private:
        // --------------------------------------------------------------------
        // CscFilenameList::NamePtr
        //
        // Simple wrapper around a string pointer to add a notion of "ownership".
        // This lets us store a string address as either a pointer to dynamic
        // heap memory (that later must be freed) or the address of a string
        // in a character buffer owned by someone else (owner frees it if 
        // necessary).
        //
        class NamePtr
        {
            public:
                NamePtr(void)
                    : m_pszName(NULL), 
                      m_bOwns(false) { }

                NamePtr(LPCTSTR pszName, bool bCopy);
                ~NamePtr(void);

                NamePtr(NamePtr& rhs);
                NamePtr& operator = (NamePtr& rhs);

                bool IsValid(void) const
                    { return NULL != m_pszName; }

                operator LPCTSTR () const
                    { return m_pszName; }

            private:
                LPCTSTR m_pszName; // address of string.
                bool    m_bOwns;   // do we need to free it on destruction?

                friend class CscFilenameList;
                friend class Share;
        };


        // --------------------------------------------------------------------
        // CscFilenameList::Share
        //
        // Represents a share in the namelist collection.  It's really just
        // a convenient container for a share name and a list of file names
        // associated with the share.
        //
        class Share
        {
            public:
                Share(LPCTSTR pszShare, bool bCopy = true);
                ~Share(void);

                bool AddFile(LPCTSTR pszFile, bool bDirectory = false, bool bCopy = true);
                bool RemoveFile(LPCTSTR pszFile);

                int FileCount(void) const
                    { return m_cFiles; }

                int ByteCount(void) const
                    { return (m_cchShareName + m_cchFileNames) * sizeof(TCHAR); }

                int Write(LPBYTE pbBufferStart, 
                          CSC_NAMELIST_SHARE_DESC *pDesc, 
                          LPTSTR pszBuffer, 
                          int cchBuffer) const;

#ifdef FILELIST_TEST
                void Dump(void) const;
#endif // FILELIST_TEST

            private:
                int    m_cFiles;          // Cnt of files in share.
                int    m_cAllocated;      // Cnt of share ptrs allocated.
                int    m_cchShareName;    // Bytes req'd to hold share name.
                int    m_cchFileNames;    // Bytes req'd to hold file names.
                NamePtr m_pszShareName;    // Address of share name string.
                NamePtr *m_rgpszFileNames; // Array of ptrs to file name strings
                static int m_cGrow;       // File name array growth increment.

                int WriteFileNames(LPTSTR pszBuffer, int cchBuffer, DWORD *pcFilesWritten) const;
                int WriteName(LPTSTR pszBuffer, int cchBuffer) const;
                bool GrowFileNamePtrList(void);

                friend class CscFilenameList;
                friend class FileIter;
        };

        // --------------------------------------------------------------------
        // Namelist object private members.
        //
        int     m_cShares;    // How many shares in collection.
        int     m_cAllocated; // Allocated size of m_rgpShares[].
        Share **m_rgpShares;  // Dynamic array of ptrs to Share objects.
        bool    m_bValid;     // Ctor completion check.
        static int m_cGrow;   // How much to grow array when necessary.

        //
        // Prevent copy.
        //
        CscFilenameList(const CscFilenameList& rhs);
        CscFilenameList& operator = (const CscFilenameList& rhs);

        bool GrowSharePtrList(void);
        bool LoadFromBuffer(PCSC_NAMELIST_HDR pbBuffer, bool bCopy);

        void ParseFullPath(LPTSTR pszFullPath, 
                           LPTSTR *ppszShare, 
                           LPTSTR *ppszFile) const;

        static bool Compare(LPCTSTR pszTemplate, LPCTSTR pszFile, bool *pbExact);

        friend class ShareIter;
        friend class Share;
};

#endif // _INC_CSCVIEW_FILELIST_H

