/*++

    Copyright (c) 1994  Microsoft Corporation

Module Name:

    COPYFILE.C

Abstract:

    file copy conf. dialog

Author:

    Bob Watson (a-robw)

Revision History:

    17 Feb 94    Written

--*/
//
//  Windows Include Files
//

#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <tchar.h>      // unicode macros
//
//  app include files
//
#include "otnboot.h"
#include "otnbtdlg.h"
//
//  Debugging defines
//
#define SHOW_DEBUG_INFO     0

// local windows message

#define     NCDU_START_FILE_COPY    (WM_USER +101)

//
//  Static data for this module
//
static  BOOL    bCopying;       // 1= copying, 0 = done
static  DWORD   dwBytesCopied;  // running total of bytes copied
static  DWORD   dwTotalCBytes;  // total bytes divided by 100 (for % computation)
static  DWORD   dwCurrentPercent; // current percent copied

#define MAX_DOS_FILENAME_LENGTH (8 * sizeof(TCHAR))
#define MAX_DOS_FILE_EXT_LENGTH (3 * sizeof(TCHAR))

static
BOOL
DisplayScrunchedFilePath (
    IN  HWND    hWnd,
    IN  LPCTSTR szInPath
)
/*++

Routine Description:
    
    sets the window text of hWnd to be the "scrunched" version of
        the path string in szInPath. If the path string is too long
        to fit in a single line of hWnd, then directories are removed
        from the path until it does fit. The directories are removed
        from the "top" down. The root drive\dir is kept as is the
        filename and as many directories that will fit.

Arguments:

    IN  HWND    hWnd
        handle of window to put text in

    IN  LPCTSTR szInPath
        path to display in window

Return Value:

    TRUE    if no error
    FALSE   if error

--*/
{
    LPTSTR  szOutPath;
    RECT    rWindow;
    LONG    lWindowWidth;
    HDC     hDC;
    SIZE    sText;
    LONG    lRootBs, lRootBsCount;

    LPTSTR  szRootBs, szFileBs, szSrc, szDest, szDotsBs;

    GetWindowRect (hWnd, &rWindow); // get window size
    lWindowWidth = rWindow.right - rWindow.left;

    hDC = GetDC (hWnd);             // get DC for window

    szOutPath = GlobalAlloc (GPTR, ((lstrlen(szInPath) + 8) * sizeof(TCHAR)));
    if (szOutPath == NULL) {
        return FALSE;
    } else {
        //buffer allocation succeeded, so copy path to local buffer
        lstrcpy (szOutPath, szInPath);
    }

    if (IsUncPath(szOutPath)) {
        lRootBs = 4;    // the 4th backslash is the "Root" backslash for UNC
    } else {
        lRootBs = 1;    // for DOS file paths, the 1st backslash is the Root
    }

    GetTextExtentPoint32(hDC,
        szOutPath, lstrlen(szOutPath), &sText);

    szSrc = szDest = szOutPath;
    szDotsBs = szRootBs = szFileBs = NULL;
    lRootBsCount = 0;

    while (sText.cx > lWindowWidth) {
        // take dirs out until it fits
        // go through path string
        while (*szSrc != 0) {
            // see if we've passed the root
            if (szRootBs == NULL) {
                if (*szSrc == cBackslash) lRootBsCount++;
                if (lRootBsCount == lRootBs) szDotsBs = szRootBs = szDest;
            } else {
                // root's done, now were; working on the pathname
                // so we'll scope out the rest of the string
                if (*szSrc == cBackslash) szFileBs = szDest;
            }
            *szDest++ = *szSrc++;
        }
        if (szRootBs == NULL) {
            // then this is a bogus path so exit now
            break;
        }
        if (szFileBs == NULL) {
            // if the File backslash didn't get defined, then the file is
            // in the root directory and we should leave now, since there
            // isn't much to do about it.
            szFileBs = szRootBs;
            break;
        }
        // now yank a dir or two (more than one will be pulled if the dir
        // name is < 4 chars
        // if a directory hasn't been pulled, yet, go ahead
        // and take one out
        // initialize the pointers
        if (szRootBs == szDotsBs) {
            // then the ... havent been added so see if they'll fit and add em
            if ((szDotsBs+4) < szFileBs) {
                // they'll fit so addem
                szDest = szDotsBs + 1;
                *szDest++ = cPeriod;
                *szDest++ = cPeriod;
                *szDest++ = cPeriod;
                *szDest = cBackslash;
                szDotsBs = szDest;
                szSrc = ++szDest;
            } else {
                // no room to left to pull files
                break;
            }
        } else {
            // dot's have already been added so set pointers
            szSrc = szDest = szDotsBs+1;
        }
        // go to next dir
        while (*szSrc++ != cBackslash);
        // copy the rest of the string
        while (*szSrc != 0) *szDest++ = *szSrc++;

        *szDest = 0; // terminate the new string

        // get size of new string
        GetTextExtentPoint32(hDC,
            szOutPath, lstrlen(szOutPath), &sText);
    }

    // the string is as small as it's going to get so set the window text

    SetWindowText (hWnd, szOutPath);

    FREE_IF_ALLOC (szOutPath);

    return TRUE;

}

LONG
CreateDirectoryFromPath (
    IN  LPCTSTR                 szPath,
    IN  LPSECURITY_ATTRIBUTES   lpSA
)
/*++

Routine Description:

    Creates the directory specified in szPath and any other "higher"
        directories in the specified path that don't exist.

Arguments:

    IN  LPCTSTR szPath
        directory path to create (assumed to be a DOS path, not a UNC)

    IN  LPSECURITY_ATTRIBUTES   lpSA
        pointer to security attributes argument used by CreateDirectory


Return Value:

    TRUE    if directory(ies) created
    FALSE   if error (GetLastError to find out why)

--*/
{
    LPTSTR   szLocalPath;
    LPTSTR   szEnd;
    LONG     lReturn = 0L;

    szLocalPath = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if (szLocalPath == NULL) {
        SetLastError (ERROR_OUTOFMEMORY);
        return FALSE;
    } else {
        // so far so good...
        SetLastError (ERROR_SUCCESS); // initialize error value to SUCCESS
    }

    lstrcpy (szLocalPath, szPath);

    szEnd = &szLocalPath[3];

    if (*szEnd != 0) {
        // then there are sub dirs to create
        while (*szEnd != 0) {
            // go to next backslash
            while ((*szEnd != cBackslash) && (*szEnd != 0)) szEnd++;
            if (*szEnd == cBackslash) {
                // terminate path here and create directory
                *szEnd = 0;
                if (!CreateDirectory (szLocalPath, lpSA)) {
                    // see what the error was and "adjust" it if necessary
                    if (GetLastError() == ERROR_ALREADY_EXISTS) {
                        // this is OK
                        SetLastError (ERROR_SUCCESS);
                    } else {
                        lReturn = 0;
                    }
                } else {
                    // directory created successfully so update count
                    lReturn++;
                }
                // replace backslash and go to next dir
                *szEnd++ = cBackslash;
            }
        }
        // create last dir in path now
        if (!CreateDirectory (szLocalPath, lpSA)) {
            // see what the error was and "adjust" it if necessary
            if (GetLastError() == ERROR_ALREADY_EXISTS) {
                // this is OK
                SetLastError (ERROR_SUCCESS);
                lReturn++;
            } else {
                lReturn = 0;
            }
        } else {
            // directory created successfully
            lReturn++;
        }
    } else {
        // else this is a root dir only so return success.
        lReturn = 1;
    }
    FREE_IF_ALLOC (szLocalPath);
    return lReturn;

}

static
DWORD
UpdatePercentComplete (
    IN  HWND    hwndDlg,
    IN  LPCTSTR  szFileName
)
/*++

Routine Description:

    Adds the size of the specified file to the running total of
        bytes copied and computes the current percentage of total
        copied. The display string is updated if the new percentage is
        different from the current percentage

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog Box window

    IN  LPTSTR  szFileName
        filename (& path) of file whose size should be added to the
        current total bytes copied value.

Return Value:

    returns the current percentage of total bytes that have been
        copied (including this file)

--*/
{
    HANDLE  hFile;

    DWORD   dwFileSizeLow, dwFileSizeHigh;
    DWORD   dwPercent = 0;

    LPTSTR  szOutBuff;

    szOutBuff = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH_BYTES);

    if (szOutBuff == NULL) return 0;

    if (dwTotalCBytes == 0) {
        SetDlgItemText (hwndDlg, NCDU_PERCENT_COMPLETE,
            GetStringResource (FMT_WORKING));
        dwPercent = 0;
    } else {
        hFile = CreateFile (
            szFileName,
            GENERIC_READ,
            (FILE_SHARE_READ | FILE_SHARE_WRITE),
            NULL,
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL);

        if (hFile != INVALID_HANDLE_VALUE) {
            dwFileSizeLow = GetFileSize (hFile, &dwFileSizeHigh);
            if (dwFileSizeLow != 0xFFFFFFFF) {
                dwBytesCopied  += dwFileSizeLow;
                dwPercent = dwBytesCopied / dwTotalCBytes;
                if (dwPercent != dwCurrentPercent) {
                    if (dwPercent > 100) dwPercent = 100;
                    dwCurrentPercent = dwPercent;
                    _stprintf (szOutBuff,
                        GetStringResource (FMT_PERCENT_COMPLETE),
                        dwPercent);
                    SetDlgItemText (hwndDlg, NCDU_PERCENT_COMPLETE, szOutBuff);
                }
            }
            CloseHandle (hFile);
        }
    }

    FREE_IF_ALLOC (szOutBuff);

    return dwPercent;
}

static
BOOL
IsDosFileName (
    LPCTSTR   szName
)
/*++

Routine Description:

    examines string to see if it conforms to the DOS filename length
        conventions

Arguments:

        szName  filename and extension to parse


Return Value:

    TRUE     if it passes
    FALSE   if not

--*/
{
    LPTSTR   szNameBegin, szNameEnd;
    LPTSTR   szExtBegin, szExtEnd;
    LPTSTR   szDot;
    LPTSTR   szTmp;
    LPTSTR   szBack_slash;


    szBack_slash = szDot = NULL;
    szTmp = (LPTSTR)szName;

    while (*szTmp) {
        if (*szTmp == '.') szDot = szTmp;
        if (*szTmp == '\\') szBack_slash = szTmp;
        szTmp++;
    }

    // find beginning and end of each component

    if (szBack_slash) {
        // backslash char found in string, pointer points to
        // last occurance, name starts immediately after
        szNameBegin = szBack_slash + 1;
    } else {
        // no backslash char found so name starts at beginning
        // of string
        szNameBegin = (LPTSTR)szName;
    }

    if (szDot) {
        // dot char found in string
        if (szDot == szName) {
            // it's the first char in the string (i.e.
            // no filename)
            szNameEnd = (LPTSTR)szName;
            // a dot was found, then the extension starts right
            // after the dot
            szExtBegin = szDot + 1;
        } else if (szDot < szNameBegin) {
            // then there's no dot in the filename, but
            // it's somewhere else in the path
            szNameEnd = szTmp;
            // no dot so ext "begins" at the end of the string
            szExtBegin = szTmp;
        } else {
            // not the first char, and not before the filename
            // so the name ends with the dot
            szNameEnd = szDot;
            // a dot was found, then the extension starts right
            // after the dot
            szExtBegin = szDot + 1;
        }
    } else {
        // no dot was found in the string so there's no
        // file extension in this string. The end of the string
        // must be the end of the file name , and the beginning of
        // the extension (and also the end of the extension)
        szNameEnd = szTmp;
        szExtBegin = szTmp;
    }

    // the end of the file extension is always the end of the string
    szExtEnd = szTmp;

    // check the components for correct length:
    // 0 <= filename <= MAX_DOS_FILENAME_LENGTH (8)
    // 0 <= ext <= MAX_DOS_FILE_EXT_LENGTH (3)

    if ((LONG)(szNameEnd-szNameBegin) <= MAX_DOS_FILENAME_LENGTH) {
        // name is ok, check extension
        if ((LONG)(szExtEnd-szExtBegin) <= MAX_DOS_FILE_EXT_LENGTH) {
            return TRUE;
        } else {
            return FALSE;
        }
    } else {
        return FALSE;
    }
}

static
LONG
CopyDir (
    IN  HWND        hwndDlg,
    IN  LPCTSTR     szFromDir,
    IN  LPCTSTR     szToDir,
    IN  DWORD       dwFlags,
    IN  PDWORD      pdwFiles,
    IN  PDWORD      pdwDirs
)
/*++

Routine Description:

    Copies all files in the specified from directory to the specified
        to directory. Specific behavior is controlled by the flags as
        documented below.

Arguments:

    hwndDlg         window handle to dialog box
    szFromDir       directory containing files to copy
    szToDir         directory to recieve files
    dwFlags         Flags that control routine's behavior

                    CD_FLAGS_COPY_SUB_DIR   copies all sub dir's as well
                    CD_FLAGS_DONT_CREATE    default is to create dirs as needed
                    CD_FLAGS_IGNORE_ATTR    ignore attributes
                    CD_FLAGS_COPY_ATTR      copy attributes as well (default
                                                is for dest fils to be normal)
                    CD_FLAGS_IGNORE_ERROR   continue with copy even if errors occur
                    CD_FLAGS_LONG_NAMES     allows filenames longer than FAT
                    other bits are ignored
    pdwFiles        Pointer to DWORD that will get count of files copied
    pdwDirs         Pointer to DWORD that will get count of dirs created

Return Value:

    Win 32 status value
        ERROR_SUCCESS   routine completed normally

--*/
{
    LPTSTR   szFromPathName; // full path of FromDir
    LPTSTR   szFromFileName; // full path of source file
    LPTSTR   szFromFileStart; // pointer to where to attach file name to path
    LPTSTR   szSearchName;   // search file name
    LPTSTR   szToPathName;   // full path of destdir
    LPTSTR   szToFileName;   // full path of detination file name
    LPTSTR   szToFileStart;  // pointer to where to attach file name to path

    DWORD   dwFileAttributes;   // attributes of source file

    PWIN32_FIND_DATA    pwfdSearchData; // buffer used for file find ops
    HANDLE              hSearch;

    int     nMbResult;

    BOOL    bStatus;
    LONG    lStatus;

    MSG msg;

    DWORD   dwFileCopyCount;    // local counter variables
    DWORD   dwDirCreateCount;


    // allocate buffers

    szFromPathName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH * sizeof(TCHAR));
    szFromFileName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH * sizeof(TCHAR));
    szSearchName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH * sizeof(TCHAR));
    szToPathName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH * sizeof(TCHAR));
    szToFileName = (LPTSTR)GlobalAlloc (GPTR, MAX_PATH * sizeof(TCHAR));
    pwfdSearchData = (PWIN32_FIND_DATA)GlobalAlloc (GPTR, sizeof(WIN32_FIND_DATA));

    if ((BOOL)szFromPathName &&
        (BOOL)szFromFileName &&
        (BOOL)szSearchName &&
        (BOOL)szToPathName &&
        (BOOL)szToFileName &&
        (BOOL)pwfdSearchData) {

        // initialize counter fields (to support recursive calls)

        if (pdwFiles != NULL) {
            dwFileCopyCount = *pdwFiles;
        } else {
            dwFileCopyCount = 0;
        }

        if (pdwDirs != NULL) {
            dwDirCreateCount = *pdwDirs;
        } else {
            dwDirCreateCount = 0;
        }
        // get full pathnames of from & to files

        GetFullPathName (
            szFromDir,
            GlobalSize(szFromPathName) / sizeof(TCHAR),
            szFromPathName,
            NULL);

        GetFullPathName (
            szToDir,
            GlobalSize(szToPathName) / sizeof(TCHAR),
            szToPathName,
            NULL);

        lStatus = ERROR_SUCCESS;
    } else {
        lStatus = ERROR_OUTOFMEMORY;
    }

    if (lStatus == ERROR_SUCCESS) {
        // validate from dir and create target if valid

        dwFileAttributes = QuietGetFileAttributes (
            szFromPathName);

        if ((dwFileAttributes != 0xFFFFFFFF) &&
            (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
            // from directory is for real so create or check
            // target dir now

            if (dwFlags & CD_FLAGS_DONT_CREATE) {
                // if don't create, then at least validate
                dwFileAttributes = QuietGetFileAttributes (
                    szToPathName);

                if ((dwFileAttributes != 0xFFFFFFFF) &&
                    (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
                    lStatus = ERROR_SUCCESS;
                } else {
                    lStatus = ERROR_DIRECTORY;
                }
            } else {
                // create sub dirs if necessary
                if (!(dwFlags & CD_FLAGS_LONG_NAMES)) {
                    // check to see if name conforms to DOS 8.3 format
                    if (!IsDosFileName(szFromPathName)) {
                        lStatus = ERROR_FILENAME_EXCED_RANGE;
                    } else {
                        lStatus = ERROR_SUCCESS;
                    }

                } else {
                    lStatus = ERROR_SUCCESS;
                }

                if (lStatus == ERROR_SUCCESS) {
                    lStatus = CreateDirectoryFromPath (
                        szToPathName, NULL);
                    if (lStatus == 0) {
                        lStatus = GetLastError();
                        if (lStatus == ERROR_ALREADY_EXISTS) {
                            // this is OK
                            lStatus = ERROR_SUCCESS;
                            // no dirs were created so don't change the
                            // count.
                        }
                    } else {
                        // if lStatus is not 0, then it's then number of
                        // directories that were created
                        dwDirCreateCount += lStatus;
                        // now set it to the Error Status value the rest of the function
                        // is expecting
                        lStatus = ERROR_SUCCESS;
                    }
                }
            }
        } else {
            lStatus = ERROR_DIRECTORY;
        }
    }

    if (lStatus == ERROR_SUCCESS) {
        // if target directory is valid, then
        // create filename bases and start copying files

        lstrcpy (szFromFileName, szFromPathName);
        if (szFromFileName[lstrlen(szFromFileName)-1] != cBackslash) lstrcat (szFromFileName, cszBackslash);
        szFromFileStart = szFromFileName + lstrlen(szFromFileName);

        lstrcpy (szToFileName, szToPathName);
        if (szToFileName[lstrlen(szToFileName)-1] != cBackslash) lstrcat (szToFileName, cszBackslash);
        szToFileStart = szToFileName + lstrlen(szToFileName);

        // create search name

        lstrcpy (szSearchName, szFromPathName);
        lstrcat (szSearchName, cszWildcardFile);

        hSearch = FindFirstFile (
            szSearchName,
            pwfdSearchData);

        if (hSearch != INVALID_HANDLE_VALUE) {
            lStatus = ERROR_SUCCESS;
            bStatus = TRUE;
            while (((lStatus == ERROR_SUCCESS) && bStatus)  && bCopying) {
                // check & save file attributes of each file, if not
                // normal, then ignore unless flag set
                //
                lstrcpy (szFromFileStart, pwfdSearchData->cFileName); //make full path

                if (!DotOrDotDotDir(pwfdSearchData->cFileName)) { //ignore these dirs
                    dwFileAttributes = QuietGetFileAttributes(
                        szFromFileName);

                    if (dwFileAttributes != 0xFFFFFFFF) {
                        // attributes are valid, so
                        // make full pathname of source file found
                        // and dest. file to be created
                        lstrcpy (szToFileStart, pwfdSearchData->cFileName); //make full path

                        if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                            // if it's a dir and subdirs is true, then
                            // copy them too if the copy sub dir flag is set

                            if (dwFlags & CD_FLAGS_COPY_SUB_DIR) {
                                lStatus = CopyDir (
                                    hwndDlg,
                                    szFromFileName,
                                    szToFileName,
                                    dwFlags,
                                    &dwFileCopyCount,
                                    &dwDirCreateCount);
                            } else {
                                // ignore directories if flag not set
                            }
                        } else { // not a dir, so see if we can copy it
                            if (lStatus == ERROR_SUCCESS) {
                                // copy the file if either the ignore bit is
                                // set or the attributes are OK
                                //
                                if (!(dwFlags & CD_FLAGS_LONG_NAMES)) {
                                    // check to see if name conforms to DOS 8.3 format
                                    if (!IsDosFileName(szFromFileName)) {
                                        lStatus = ERROR_FILENAME_EXCED_RANGE;
                                    } else {
                                        lStatus = ERROR_SUCCESS;
                                    }
                                }

                                DisplayScrunchedFilePath (
                                    GetDlgItem (hwndDlg, NCDU_FROM_PATH),
                                    (LPCTSTR)_tcslwr(szFromFileName));

                                DisplayScrunchedFilePath (
                                    GetDlgItem (hwndDlg, NCDU_TO_PATH),
                                    (LPCTSTR)_tcslwr(szToFileName));

                                if (lStatus == ERROR_SUCCESS) {
                                    bStatus = CopyFile(
                                        szFromFileName,
                                        szToFileName,
                                        FALSE);         // overwrite existing file

                                    //verify file was created
                                    if (bStatus) {
                                        if (QuietGetFileAttributes(szToFileName) == 0xFFFFFFFF) {
                                            // unable to read attributes of created file
                                            // so return error
                                            lStatus = ERROR_CANNOT_MAKE;
                                        }
                                    } else {
                                        // get copy error
                                        lStatus = BOOL_TO_STATUS (bStatus);
                                    }
                                }

                                // if copy successful reset source file attributes
                                // and optionally destination file attributes

                                if (lStatus == ERROR_SUCCESS) {
                                    // set file attributes to NORMAL
                                    SetFileAttributes (
                                        szToFileName,
                                        FILE_ATTRIBUTE_NORMAL);
                                    // update filesize
                                    UpdatePercentComplete(hwndDlg, szFromFileName);
                                    // update count
                                    dwFileCopyCount++;
                                } else {
                                    // bail out here since there was a copy error
                                    nMbResult = MessageBox (
                                        hwndDlg,
                                        GetStringResource (CSZ_UNABLE_COPY),
                                        szFromFileName,
                                        MB_OKCANCEL_TASK_EXCL);
                                    if (nMbResult == IDCANCEL) {
                                        bCopying = FALSE;
                                    }
                                    // the error has already been handled so return
                                    // success to prevent the calling routine from
                                    // signalling this error
                                    lStatus = ERROR_SUCCESS;
                                }

                                // check for messages

                                while (PeekMessage (&msg, 0, 0, 0, PM_REMOVE)) {
                                    TranslateMessage (&msg);
                                    DispatchMessage (&msg);
                                }
                            }
                        }
                    } else {
                        lStatus = GetLastError();
                    }
                }

                if (dwFlags & CD_FLAGS_IGNORE_ERROR) {
                    // if ignore error, then set to success
                    lStatus = ERROR_SUCCESS;
                }

                if (lStatus == ERROR_SUCCESS) {
                    bStatus = FindNextFile (
                        hSearch,
                        pwfdSearchData);
                } else {
                    bStatus = FALSE;    // abort loop
                }
            } // end while files in dir
            FindClose (hSearch);
        } else {
            // invalid find handle so return error
            lStatus = GetLastError();
        }
    } // end of valid directory block

    FREE_IF_ALLOC(szFromPathName);
    FREE_IF_ALLOC(szFromFileName);
    FREE_IF_ALLOC(szSearchName);
    FREE_IF_ALLOC(szToPathName);
    FREE_IF_ALLOC(szToFileName);
    FREE_IF_ALLOC(pwfdSearchData);

    // set the counter fields if they were passed in

    if (pdwFiles != NULL) {
        *pdwFiles = dwFileCopyCount;
    }

    if (pdwDirs != NULL) {
        *pdwDirs = dwDirCreateCount;
    }

    return lStatus;
}

static
BOOL
CopyFileDlg_NCDU_START_FILE_COPY (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Formats values from Dialog Box parameter structure to argument list of
        copy directory function

Arguments:

    IN  HWND    hwndDlg
        handle to dialog box window

    IN  WPARAM  wParam
        Not Used

    IN  LPARAM  lParam
        address of copy file structure.

Return Value:

    FALSE, always

--*/
{
    PCF_DLG_DATA pCF;

    pCF = (PCF_DLG_DATA)lParam;

#if SHOW_DEBUG_INFO
    // debug message box
    {
        LPTSTR  szMessageBuffer;
        DWORD   dwSourceAttr, dwDestAttr;
        UINT    nMbReturn;

        szMessageBuffer = GlobalAlloc (GPTR, SMALL_BUFFER_SIZE);
        if (szMessageBuffer != NULL) {
            dwSourceAttr = QuietGetFileAttributes (pCF->szSourceDir);
            dwDestAttr = QuietGetFileAttributes (pCF->szDestDir);
            _stprintf (szMessageBuffer,
                fmtPrepareToCopy,
                pCF->szDisplayName,
                pCF->szSourceDir, dwSourceAttr,
                pCF->szDestDir, dwDestAttr,
                pCF->dwCopyFlags);
            nMbReturn = MessageBox (hwndDlg,
                szMessageBuffer,
                cszDebug,
                MB_OKCANCEL_TASK_INFO);
            FREE_IF_ALLOC (szMessageBuffer);
        } else {
            nMbReturn = IDOK;
        }

        if (nMbReturn == IDCANCEL) {
            // then bail here
            EndDialog (hwndDlg, IDCANCEL);
            return TRUE;
        }
    }
#endif

    if (CopyDir (
        hwndDlg,
        pCF->szSourceDir,
        pCF->szDestDir,
        pCF->dwCopyFlags,
        &pCF->dwFilesCopied,
        &pCF->dwDirsCreated)  != ERROR_SUCCESS) {

        // display error message
        DisplayMessageBox (
            hwndDlg,
            CSZ_COPY_ERROR,
            0L,
            MB_OK_TASK_EXCL);
        bCopying = FALSE;  // to indicate error or non-completion

    }

    EndDialog (hwndDlg, (bCopying ? IDOK : IDCANCEL));

    return TRUE;
}

static
BOOL
CopyFileDlg_WM_INITDIALOG (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Dialog box initialization routine.

Arguments:

    IN  HWND    hwndDlg
        Handle to dialog box window

    IN  WPARAM  wParam
        not used

    IN  LPARAM  lParam
        address of Copy file data structure passed by calling routine.

Return Value:

    FALSE   if valid param block address
    TRUE    if not

--*/
{
    PCF_DLG_DATA pCF;

    pCF = (PCF_DLG_DATA)lParam;

    if (pCF != NULL) {
        // intialize Global data
        bCopying = TRUE;

        dwBytesCopied = 0;
        dwCurrentPercent = 0;
        dwTotalCBytes = (pCF->dwTotalSize + 50) / 100;

        PositionWindow  (hwndDlg);
        SetDlgItemText (hwndDlg, NCDU_COPY_APPNAME, pCF->szDisplayName);
        SetDlgItemText (hwndDlg, NCDU_FROM_PATH, cszEmptyString);
        SetDlgItemText (hwndDlg, NCDU_TO_PATH, cszEmptyString);
        SetDlgItemText (hwndDlg, NCDU_PERCENT_COMPLETE,
            GetStringResource (FMT_ZERO_PERCENT_COMPLETE));
        SetFocus (GetDlgItem(hwndDlg, IDCANCEL));
        // start copying files
        PostMessage (hwndDlg, NCDU_START_FILE_COPY, 0, lParam);
        return FALSE;
    } else {
        // illegal parameter
        EndDialog (hwndDlg, IDCANCEL);
        return TRUE;
    }
}

static
BOOL
CopyFileDlg_WM_COMMAND (
    IN  HWND    hwndDlg,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Processes WM_COMMAND messages to dialog box.
        Only IDCANCEL button is processed here (ceasing copy function)
        all other button commands are ignored (since there aren't any)

Arguments:

    IN  HWND    hwndDlg
        handle to dialog box window

    IN  WPARAM  wParam
        LOWORD   has the id of the control that issued the message

    IN  LPARAM  lParam
        Not used.

Return Value:

    if button is IDCANCEL, then FALSE
    otherwise TRUE (i.e. not processed.)

--*/
{
    switch (LOWORD(wParam)) {
        case IDCANCEL:
            switch (HIWORD(wParam)) {
                case BN_CLICKED:
                    if (DisplayMessageBox(hwndDlg,
                        NCDU_RU_SURE, 0,
                        MB_OKCANCEL_TASK_EXCL_DEF2) == IDOK) {
                        bCopying = FALSE;
                    }
                    return TRUE;

                default:
                    return FALSE;
            }

        default:    return FALSE;
    }
}

BOOL CALLBACK
CopyFileDlgProc (
    IN  HWND    hwndDlg,
    IN  UINT    message,
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Main Dialog box window proc. Processes the following windows messages:
        WM_INITDIALOG:          dialog box initialization procedure
        WM_COMMAND:             windows messages (resulting from user commands)
        NCDU_START_FILE_COPY:   local message to begin copying files

    all other messages are processed by the DefDialogProc

Arguments:

    Standard WNDPROC arguments

Return Value:

    FALSE if not processed, otherwise value returned by
        called routine.

--*/
{
    switch (message) {
        case WM_INITDIALOG: return (CopyFileDlg_WM_INITDIALOG (hwndDlg, wParam, lParam));
        case WM_COMMAND:    return (CopyFileDlg_WM_COMMAND (hwndDlg, wParam, lParam));
        case NCDU_START_FILE_COPY: return (CopyFileDlg_NCDU_START_FILE_COPY (hwndDlg, wParam, lParam));
        default:            return FALSE;
    }
}