copy.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 273k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. // Copyright (c) <1995-1999> Microsoft Corporation
  2. #include "shellprv.h"
  3. #include "mmhelper.h"
  4. #pragma  hdrstop
  5. #include "shlwapip.h" // for SHGlobalCounterDecrement
  6. #define INTERNAL_COPY_ENGINE
  7. #include "copy.h"
  8. #include "shell32p.h"
  9. #include "control.h"
  10. //--------------------//
  11. //  Helper macros
  12. #ifndef RECTWIDTH
  13. #define RECTWIDTH(rc)  ((rc).right - (rc).left)
  14. #endif//RECTWIDTH
  15. #ifndef RECTHEIGHT
  16. #define RECTHEIGHT(rc) ((rc).bottom - (rc).top)
  17. #endif//RECTHEIGHT
  18. #ifdef WINNT
  19. #include <iofs.h>
  20. #endif
  21. #define REG_VAL_GENERAL_RENAMEHTMLFILE  TEXT("RenameHtmlFile")
  22. // BUGBUG - Might want this for Nashville too...
  23. #ifdef WINNT
  24. #define COPY_USE_COPYFILEEX
  25. #endif
  26. #define TF_DEBUGCOPY 0x00800000
  27. #define VERBOSE_STATUS
  28. // REVIEW, we should tune this size down as small as we can
  29. // to get smoother multitasking (without effecting performance)
  30. #define COPYMAXBUFFERSIZE       0x10000 // 0xFFFF this is 32-bit code!
  31. #define MIN_MINTIME4FEEDBACK    5       // is it worth showing estimated time to completion feedback?
  32. #define MS_RUNAVG               10000   // ms, window for running average time to completion estimate
  33. #define MS_TIMESLICE             2000    // ms, (MUST be > 1000!) first average time to completion estimate
  34. #define MAXDIRDEPTH             128     // # of directories we will deal with recursivly
  35. #define SHOW_PROGRESS_TIMEOUT   1000    // 1 second
  36. #define MINSHOWTIME             1000    // 1 sec
  37. // progress dialog message
  38. #define PDM_SHUTDOWN     WM_APP
  39. #define PDM_NOOP        (WM_APP + 1)
  40. #define PDM_UPDATE      (WM_APP + 2)
  41. #define OPER_MASK           0x0F00
  42. #define OPER_ENTERDIR       0x0100
  43. #define OPER_LEAVEDIR       0x0200
  44. #define OPER_DOFILE         0x0300
  45. #define OPER_ERROR          0x0400
  46. #define FOFuncToStringID(wFunc) (IDS_UNDO_FILEOP + wFunc)
  47. // The following are the file and folder suffixes.
  48. // BUGBUG: These are hard-coded now; See if we need to read these from the registry. 
  49. static const TCHAR  c_szDotHtm[]        = TEXT(".htm");   // The extension for HTML files.
  50. static const TCHAR  c_szDotHtml[]       = TEXT(".html");  // A different extension for HTML files.
  51. static const TCHAR  c_szDotHtmQuestion[]= TEXT(".htm?");  // Wild card extension for .htm and .html
  52. static const TCHAR  c_szStart[]         = TEXT("*");      // Wild card for all suffixes.
  53. //
  54. //  The following is a list of folder suffixes in all international languages. This list is NOT
  55. // read from a resource because we do NOT want the strings in this list to be mistakenly localized.
  56. // This list will allow NT5 shell to operate on files created by any international version of 
  57. // office 9.
  58. //  This list is taken from "http://officeweb/specs/webclient/files.htm"
  59. //
  60. //  WARNING: Do not localize the strings in this table. Do not make any changes to this table 
  61. //  without consulting AlanRa (Office9 PM)
  62. //
  63. static const LPCTSTR c_apszSuffixes[] = 
  64. {
  65.     TEXT(".files"),
  66.     TEXT("_files"),
  67.     TEXT("-Dateien"),
  68.     TEXT("_fichiers"),
  69.     TEXT("_bestanden"),
  70.     TEXT("_file"),
  71.     TEXT("_archivos"),
  72.     TEXT("-filer"),
  73.     TEXT("_tiedostot"),
  74.     TEXT("_pliki"),
  75.     TEXT("_soubory"),
  76.     TEXT("_elemei"),
  77.     TEXT("_ficheiros"),
  78.     TEXT("_arquivos"),
  79.     TEXT("_dosyalar"),
  80.     TEXT("_datoteke"),
  81.     TEXT("_fitxers"),
  82.     TEXT("_failid"),
  83.     TEXT("_fails"),
  84.     TEXT("_bylos"),
  85.     TEXT("_fajlovi"),
  86.     TEXT("_fitxategiak"),
  87. };
  88. // The reg value under HKCUREGSTR_PATH_EXPLORER that specifies Connection ON/OFF switch
  89. #define REG_VALUE_NO_FILEFOLDER_CONNECTION  TEXT("NoFileFolderConnection")
  90. ////////////////////////////////////////////////////////////////////////////
  91. ///// directory tree cache.
  92. // this is set if pdtnChild has not been traversed (as opposed to NULL which means
  93. // there are no children
  94. #define DTN_DELAYED ((PDIRTREENODE)-1)
  95. // DIRTREENODE is a node in a linked list/tree cache of the directory structure.
  96. // except for the top level (which is specified by the caller of the api), the order
  97. // are all files first, then all directories.
  98. typedef struct _dirtreenode {
  99.     
  100.     struct _dirtreenode *pdtnNext; // sibling
  101.     struct _dirtreenode *pdtnChild; // head of children linked list
  102.     struct _dirtreenode *pdtnParent;
  103.     
  104.     DWORD dwFileAttributes;
  105.     FILETIME ftCreationTime;
  106.     FILETIME ftLastWriteTime;
  107.     DWORD nFileSizeLow;
  108.     DWORD nFileSizeHigh;
  109.     
  110.     DWORD nFileSizeCopied;
  111.     BOOL  fNewRoot : 1;
  112.     BOOL  fDummy : 1;   // this marks the node as a dummy node (a wildcard that didn't match anything)
  113.     BOOL  fConnectedElement : 1; // this marks the node as an element that was implicitly added
  114.     // to the Move/Copy source list because of an office9 type of
  115.     // connection established in the registry.
  116.     
  117.     //The following is a union because not all nodes need all the fields.
  118.     union {
  119.         // The following is valid only if fConnectedElement is FALSE.
  120.         struct  _dirtreenode *pdtnConnected;  
  121.         
  122.         // The following structure is valid only if fConnectedElemet is TRUE.
  123.         struct  {
  124.             LPTSTR  pFromConnected;     // if fNewRoot && fConnectedElement, then these two elements
  125.             LPTSTR  pToConnected;       // have the pFrom and pTo.
  126.             DWORD   dwConfirmation;     // The result of confirnation given by end-user
  127.         } ConnectedInfo;
  128.     };
  129.     
  130.     TCHAR szShortName[14];
  131.     TCHAR szName[1]; // this struct is dynamic
  132.     
  133. } DIRTREENODE, *PDIRTREENODE;
  134. typedef struct {
  135.     BOOL  fChanged;
  136.     DWORD dwFiles; // number of files
  137.     DWORD dwFolders; // number of folders
  138.     DWORD dwSize; // total size of all files
  139. } DIRTOTALS, *PDIRTOTALS;
  140. typedef struct {
  141.     UINT oper;
  142.     DIRTOTALS dtAll; // totals for all files
  143.     DIRTOTALS dtDone; // totals of what's done
  144.     BOOL fChangePosted;
  145.     
  146.     PDIRTREENODE pdtn; // first directory tree node
  147.     PDIRTREENODE pdtnCurrent;
  148.     PDIRTREENODE pdtnConnectedItems;  //Pointer to the begining of connected elements node.
  149.     TCHAR    bDiskCheck[26];
  150.     
  151.     // how much does each operation cost in the progress...
  152.     int iFilePoints;
  153.     int iFolderPoints;
  154.     int iSizePoints;
  155.     
  156.     
  157.     LPTSTR pTo;  // this holds the top level target list
  158.     LPTSTR pFrom; // this holds the top level source list
  159.     BOOL    fMultiDest;
  160.     
  161.     TCHAR szSrcPath[MAX_PATH];
  162.     TCHAR szDestPath[MAX_PATH]; // this is the current destination for pdtn and all it's children (not siblings)
  163.     // lpszDestPath includes pdtn's first path component
  164.     
  165.     HDSA hdsaRenamePairs;
  166.     
  167. } DIRTREEHEADER, *PDIRTREEHEADER;
  168. typedef struct {
  169.     int          nSourceFiles;
  170.     LPTSTR       lpCopyBuffer; // global file copy buffer
  171.     UINT         uSize;         // size of this buffer
  172.     FILEOP_FLAGS fFlags;        // from SHFILEOPSTRUCT
  173.     HWND         hwndProgress;  // dialog/progress window
  174.     HWND         hwndDlgParent; // parent window for message boxes
  175.     CONFIRM_DATA cd;            // confirmation stuff
  176.     
  177.     LPUNDOATOM  lpua;           // the undo atom that this file operation will make
  178.     BOOL        fNoConfirmRecycle;
  179.     BOOL        bAbort;
  180.     BOOL        fMerge;   // are we doing a merge of folders
  181.     
  182.     BOOL        fDone;
  183.     BOOL        fProgressOk;
  184.     BOOL        fDTBuilt;
  185.     
  186.     // folowing fields are used for giving estimated time for completion
  187.     // feedback to the user during longer than MINTIME4FEEDBACK operations
  188.     BOOL  fFlushWrites;     // Should we flush writes for destinations on slow links
  189.     
  190.     DWORD dwPreviousTime;       // calculate transfer rate
  191.     int  iLastProgressPoints;   // how many progress points we had the last time we updated the time est
  192.     DWORD dwPointsPerSec;
  193.     LPCTSTR lpszProgressTitle;
  194.     LPSHFILEOPSTRUCT lpfo;
  195.     
  196.     DIRTREEHEADER dth;
  197. #ifdef COPY_USE_COPYFILEEX
  198.     BOOL        fInitialize;
  199.     const WIN32_FIND_DATA* pfd;
  200. #endif
  201.     BOOL        bStreamLossPossible;    // Could stream loss happen in this directory?
  202. } COPY_STATE, *LPCOPY_STATE;
  203. // function declarations
  204. void _ProcessNameMappings(LPTSTR pszTarget, HDSA hdsaRenamePairs);
  205. int GetNameDialog(HWND hwnd, COPY_STATE *pcs, BOOL fMultiple,UINT wOp, LPTSTR pFrom, LPTSTR pTo);
  206. void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs);
  207. BOOL FOQueryAbort(COPY_STATE *pcs);
  208. UINT DTAllocConnectedItemNodes(PDIRTREEHEADER pdth, COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR pszPath, BOOL fRecurse, PDIRTREENODE *ppdtnConnectedItems);
  209. void CALLBACK FOUndo_Invoke(LPUNDOATOM lpua);
  210. BOOL DTDiskCheck(PDIRTREEHEADER pdth, COPY_STATE *pcs, LPTSTR pszPath)
  211. {
  212.     int iDrive = PathGetDriveNumber(pszPath);
  213.     if (iDrive != -1)
  214.     {
  215.         if (!pdth->bDiskCheck[iDrive])
  216.         {
  217.             HWND hwnd = pcs->hwndDlgParent;
  218.             TCHAR szDrive[] = TEXT("A:\");
  219.             szDrive[0] += (CHAR)iDrive;
  220.             // Sometimes pszPath is a dir and sometimes it's a file.  All we really care about is if the
  221.             // drive is ready (inserted, formated, net path mapped, etc).  We know that we don't have a
  222.             // UNC path because PathGetDriveNumber would have failed and we are already busted in terms
  223.             // of mounted volumes, again because we use PathGetDriveNumber, so we don't have to worry about
  224.             // these two cases.  As such we build the root path and use that instead.
  225.             pdth->bDiskCheck[iDrive] = SUCCEEDED(SHPathPrepareForWrite(((pcs->fFlags & FOF_NOERRORUI) ? NULL : hwnd), NULL, szDrive, 0));
  226.         }
  227.         return pdth->bDiskCheck[iDrive];
  228.     }
  229.     return TRUE;    // always succeed for net drives
  230. }
  231. //--------------------------------------------------------------------------------------------
  232. // ConvertToConnectedItemname:
  233. //      Given a file/folder name, this function checks to see if it has any connection and if 
  234. // there is a connection, then it will convert the given name to that of the connected element
  235. // and return length of the prefix. If no connection exists, it returns zero.
  236. //  The fDirectory parameter specifies if the given filename is a FOLDER or not!
  237. //
  238. //  dwBuffSize: The size of pszFileName buffer in CHARACTERS.
  239. //
  240. //  Examples:
  241. //      "foo.htm"   =>  "foo*"  (returns 3 because the prefix("foo") length is 3)
  242. //      "foobar files" =>  "foobar.htm?"  (returns 6 as the prefix length)
  243. //                                          
  244. //--------------------------------------------------------------------------------------------
  245. int ConvertToConnectedItemName(LPTSTR pszFileName, DWORD dwBuffSize, BOOL fDirectory)
  246. {
  247.     LPTSTR  pszDest, pszConnectedElemSuffix;
  248.     int     iPrefixLength;
  249.     
  250.     if (fDirectory)
  251.     {
  252.         // Look for a suffix which is one of the standard suffixes.
  253.         if (!(pszDest = (LPTSTR)PathFindSuffixArray(pszFileName, c_apszSuffixes, ARRAYSIZE(c_apszSuffixes))))
  254.             return FALSE;
  255.         
  256.         // " files" suffix is found. Replace it with ".htm?"
  257.         pszConnectedElemSuffix = (LPTSTR)c_szDotHtmQuestion;
  258.     }
  259.     else
  260.     {
  261.         // Look for the extension ".htm" or ".html" and replace it with "*".
  262.         if (!(pszDest = PathFindExtension(pszFileName)))
  263.             return FALSE;
  264.         
  265.         if (lstrcmpi(pszDest, c_szDotHtm) && (lstrcmpi(pszDest, c_szDotHtml)))
  266.             return(FALSE);
  267.         
  268.         // Extension ".htm" or ".html" is found. Replace it with "*"
  269.         pszConnectedElemSuffix = (LPTSTR)c_szStar;
  270.     }
  271.     
  272.     iPrefixLength = (int)(pszDest - pszFileName);
  273.     
  274.     //Check if the input buffer is big enough to over-write the suffix in-place
  275.     if ((((int)dwBuffSize - iPrefixLength) - 1) < lstrlen(pszConnectedElemSuffix))
  276.         return 0;
  277.     
  278.     //Replace the source suffix with the connected element's suffix.
  279.     lstrcpy(pszDest, pszConnectedElemSuffix);
  280.     
  281.     return(iPrefixLength);
  282. }
  283. PDIRTREENODE DTAllocNode(PDIRTREEHEADER pdth, WIN32_FIND_DATA* pfd, PDIRTREENODE pdtnParent, PDIRTREENODE pdtnNext, BOOL fConnectedElement)
  284. {
  285.     // BOBDAY: does this lstrlen need to be something else for unicode?
  286.     int iLen = pfd ? lstrlen(pfd->cFileName) * sizeof(TCHAR) : 0;
  287.     PDIRTREENODE pdtn = (PDIRTREENODE)LocalAlloc(LPTR, sizeof(DIRTREENODE) + iLen);
  288.     if (pdtn)
  289.     {
  290.         pdtn->fConnectedElement = fConnectedElement;
  291.         
  292.         // Initializing the following to NULL is not needed because of the LPTR (zero init) done
  293.         // above.
  294.         // if (fConnectedElement)
  295.         //{
  296.         //    pdtn->ConnectedInfo.pFromConnected = pdtn->ConnectedInfo.pToConnected = NULL;
  297.         //    pdtn->ConnectedInfo.dwConfirmation = 0;
  298.         //}
  299.         //else
  300.         //    pdtn->pdtnConnected = NULL;
  301.         
  302.         pdtn->pdtnParent = pdtnParent;
  303.         pdtn->pdtnNext   = pdtnNext;
  304.         
  305.         if (pfd)
  306.         {
  307.             pdtn->dwFileAttributes = pfd->dwFileAttributes;
  308.             pdtn->ftCreationTime   = pfd->ftCreationTime;
  309.             pdtn->ftLastWriteTime  = pfd->ftLastWriteTime;
  310.             pdtn->nFileSizeLow     = pfd->nFileSizeLow;
  311.             pdtn->nFileSizeHigh    = pfd->nFileSizeHigh;
  312.             
  313.             // only the stuff we care about
  314.             lstrcpy(pdtn->szShortName, pfd->cAlternateFileName);
  315.             lstrcpy(pdtn->szName, pfd->cFileName);
  316.             
  317.             
  318.             if (ISDIRFINDDATA(*pfd))
  319.             {
  320.                 pdth->dtAll.dwFolders++;
  321.                 pdtn->pdtnChild = DTN_DELAYED;
  322.             }
  323.             else
  324.             {
  325.                 pdth->dtAll.dwSize += pfd->nFileSizeLow;
  326.                 pdth->dtAll.dwFiles++;
  327.             }
  328.             // increment the header stats
  329.             pdth->dtAll.fChanged = TRUE;
  330.         }
  331.     }
  332.     
  333.     return pdtn;
  334. }
  335. #if defined(DEBUG)  /// && defined(DEBUGCOPY)
  336. void DebugDumpPDTN(PDIRTREENODE pdtn, LPTSTR ptext)
  337. {
  338.     DebugMsg(TF_DEBUGCOPY, TEXT("***** PDTN %x  (%s)"), pdtn, ptext);
  339.     //Safe-guard against pdtn being NULL!
  340.     if (pdtn)
  341.     {
  342.         DebugMsg(TF_DEBUGCOPY, TEXT("** %s %s"), pdtn->szShortName, pdtn->szName);
  343.         DebugMsg(TF_DEBUGCOPY, TEXT("** %x %d"), pdtn->dwFileAttributes, pdtn->nFileSizeLow);
  344.         DebugMsg(TF_DEBUGCOPY, TEXT("** %x %x %x"), pdtn->pdtnParent, pdtn->pdtnNext, pdtn->pdtnChild);
  345.         DebugMsg(TF_DEBUGCOPY, TEXT("** NewRoot:%x, Connected:%x, Dummy:%x"), pdtn->fNewRoot, pdtn->fConnectedElement, pdtn->fDummy);
  346.         if (pdtn->fConnectedElement)
  347.         {
  348.             DebugMsg(TF_DEBUGCOPY, TEXT("**** Connected: pFromConnected:%s, pToConnected:%s, dwConfirmation:%x"), pdtn->ConnectedInfo.pFromConnected, 
  349.                     pdtn->ConnectedInfo.pToConnected, pdtn->ConnectedInfo.dwConfirmation);
  350.         }
  351.         else
  352.         {
  353.             DebugMsg(TF_DEBUGCOPY, TEXT("**** Origin: pdtnConnected:%x"), pdtn->pdtnConnected);
  354.         }
  355.     }
  356.     else
  357.     {
  358.         DebugMsg(TF_DEBUGCOPY, TEXT("** NULL pointer(PDTN)"));
  359.     }
  360. }
  361. #else
  362. #define DebugDumpPDTN(p, x) 0
  363. #endif
  364. BOOL  DoesSuffixMatch(LPTSTR  lpSuffix, const LPCTSTR *apSuffixes, int iArraySize)
  365. {
  366.     while(iArraySize--)
  367.     {
  368.         // Note: This must be a case sensitive compare, because we don't want to pickup 
  369.         // "Program Files".
  370.         if (!lstrcmp(lpSuffix, *apSuffixes++))
  371.             return(TRUE);
  372.     }
  373.     
  374.     return(FALSE);
  375. }
  376. //--------------------------------------------------------------------------------------------
  377. //
  378. //  DTPathToDTNode:
  379. //      This function is used to build a list of nodes that correspond to the given pszPath.
  380. // This list is built under "ppdtn".  If ppdtnConnectedItems is given, another list of nodes that
  381. // correspond to the connected elements(files/folders) of the nodes in the first list is also built
  382. // under "ppdtnConnectedItems".
  383. //
  384. // WARNING: This parties directly on pszPath and pfd so that it doesn't need to allocate
  385. // on the stack.  This recurses, so we want to use as little stack as possible
  386. //
  387. // this will wack off one component from pszPath
  388. //
  389. //
  390. // ppdtn: Points to where the header of the list being built will be stored.
  391. // ppdtnConnectedItems: If this is NULL, then we are not interested in finding and building the 
  392. //                      connected elements. If this is NOT null, it points to where the header of
  393. //                      the connected items list will be stored.
  394. // fConnectedElement: Each node being built under ppdtn needs to be marked with this bit.
  395. // iPrefixLength: This parameter is zero if fConnectedElement is FALSE. Otherwise, it contains the
  396. //              Length of the prefix part of the file or foldername (path is NOT included).
  397. //              For example, if "c:windowsfoo*" is passed in, iPrefixLength is 3 (length of "foo")
  398. //
  399. // dwFilesOrFolders parameter can specify if we need to look for only FILES or FOLDERs or BOTH.
  400. #define     DTF_FILES_ONLY      0x00000001      //Operate only on Files.
  401. #define     DTF_FOLDERS_ONLY    0x00000002      //Operate only on Folders.
  402. #define     DTF_FILES_AND_FOLDERS  (DTF_FILES_ONLY | DTF_FOLDERS_ONLY)  //Operate on files AND folders.
  403. UINT DTPathToDTNode(PDIRTREEHEADER pdth, COPY_STATE *pcs, LPTSTR pszPath, BOOL fRecurse,
  404.                     DWORD dwFilesOrFolders, PDIRTREENODE* ppdtn, WIN32_FIND_DATA *pfd,
  405.                     PDIRTREENODE pdtnParent, PDIRTREENODE* ppdtnConnectedItems, BOOL fConnectedElement,
  406.                     int iPrefixLength)
  407. {
  408.     int iError = 0;
  409.     // this points to the var where all items are inserted.
  410.     // folders are placed after it, files are placed before
  411.     // keep the stack vars to a minimum because this is recursive
  412.     PDIRTREENODE *ppdtnMiddle = ppdtn;
  413.     HANDLE hfind;
  414.     BOOL    fNeedToFindNext;
  415.     DebugMsg(TF_DEBUGCOPY, TEXT("DTPathToDTNode Entering %s"), pszPath);
  416.     *ppdtnMiddle = NULL; // in case there are no children
  417.     hfind = FindFirstFile(pszPath, pfd);
  418.     if (hfind == INVALID_HANDLE_VALUE)
  419.     {
  420.         // this is allowable only if the path is wild...
  421.         // and the parent exists
  422.         if (PathIsWild(pszPath))
  423.         {
  424.             PathRemoveFileSpec(pszPath);
  425.             if (PathFileExists(pszPath))
  426.             {
  427.                 return 0;
  428.             }
  429.         }
  430.         return OPER_ERROR | DE_FILENOTFOUND;
  431.     }
  432.     
  433.     //Remove the filespec before passing it onto DTAllocConnectedItemNodes.
  434.     PathRemoveFileSpec(pszPath);
  435.     
  436.     fNeedToFindNext = TRUE;
  437.     
  438.     do
  439.     {
  440.         // We skip the following files:
  441.         //      "." and ".." filenames
  442.         //      Folders when DTF_FILES_ONLY is set
  443.         //      Files when DTF_FOLDERS_ONLY is set
  444.         
  445.         if (!PathIsDotOrDotDot(pfd->cFileName) &&
  446.             (((dwFilesOrFolders & DTF_FILES_ONLY) && !ISDIRFINDDATA(*pfd)) || 
  447.             ((dwFilesOrFolders & DTF_FOLDERS_ONLY) && ISDIRFINDDATA(*pfd)))) 
  448.         {
  449.             //Check if we are looking for connected elements
  450.             if ((!pdtnParent) && fConnectedElement)
  451.             {
  452.                 // We found what we are looking for. If we are looking for a top-level connected item and 
  453.                 // if it is a folder, then we need to make sure that the suffix exactly matches one of the
  454.                 // suffixes in the array c_apszSuffixes[].
  455.                 LPTSTR  lpSuffix = (LPTSTR)(pfd->cFileName + iPrefixLength);
  456.                 
  457.                 if (ISDIRFINDDATA(*pfd))  
  458.                 {
  459.                     // What we found is a directory!
  460.                     // See if it has one of the standard suffixes for connected folders.
  461.                     if (!DoesSuffixMatch(lpSuffix, c_apszSuffixes, ARRAYSIZE(c_apszSuffixes)))
  462.                         continue; //This is not what we look for. So, find next.
  463.                 }
  464.                 else
  465.                 {
  466.                     // What we found is a file (i.e Not a directory)
  467.                     // See if it has one of the standard suffixes for html files.
  468.                     if (lstrcmpi(lpSuffix, c_szDotHtm) && lstrcmpi(lpSuffix, c_szDotHtml))
  469.                         continue; //This is not what we look for. So, find next.
  470.                 }
  471.                 
  472.                 // Now we know that we found the connected element that we looked for.
  473.                 // So, no need to FindNext again. We can get out of the loop after processing
  474.                 // it once.
  475.                 fNeedToFindNext = FALSE;
  476.             }
  477.             *ppdtnMiddle = DTAllocNode(pdth, pfd, pdtnParent, *ppdtnMiddle, fConnectedElement);
  478.             if (!*ppdtnMiddle)
  479.                 return OPER_ERROR | DE_INSMEM;
  480.             // make sure that the parent's pointer always points to the head of
  481.             // this linked list
  482.             if (*ppdtn == (*ppdtnMiddle)->pdtnNext)
  483.                 *ppdtn = (*ppdtnMiddle);
  484.             DebugDumpPDTN(*ppdtnMiddle, TEXT("DTPathToDTNode, DTAllocNode"));
  485.             
  486.             //We need to check for Connected elements only for the top level items
  487.             if ((!(pcs->fFlags & FOF_NO_CONNECTED_ELEMENTS)) && ppdtnConnectedItems)
  488.             {
  489.                 //Make sure this is a top level item
  490.                 ASSERT(!pdtnParent);
  491.                 //Create a list of connected items and attach it to the head of the list.
  492.                 iError = DTAllocConnectedItemNodes(pdth, pcs, pfd, pszPath, fRecurse, ppdtnConnectedItems);
  493.                 
  494.                 DebugDumpPDTN(*ppdtnConnectedItems, TEXT("DTPathToDTNode, DTAllocConnectedNodes"));
  495.                 
  496.                 // It is possible that the connected files do not exist. That condition is not really
  497.                 // an error. So, we check for insufficient memory error condition alone here.
  498.                 if (iError == (OPER_ERROR | DE_INSMEM))
  499.                     return(iError);
  500.                 
  501.                 //If a connected item exists, then make the origin item point to this connected item.
  502.                 if (*ppdtnConnectedItems)
  503.                 {
  504.                     (*ppdtnMiddle)->pdtnConnected = *ppdtnConnectedItems;
  505.                     // Also by default, set the Confirmation result to NO so that the connected element
  506.                     // will not be copied/moved etc., in case of a conflict. However, if the origin had
  507.                     // a conflict, we would put up a confirmation dlg and the result of that dlg will 
  508.                     // over-write this value.
  509.                     (*ppdtnConnectedItems)->ConnectedInfo.dwConfirmation = IDNO;
  510.                 }
  511.                 
  512.                 //Move to the last node in the connected items list.
  513.                 while(*ppdtnConnectedItems)
  514.                     ppdtnConnectedItems = &((*ppdtnConnectedItems)->pdtnNext);
  515.             }
  516.             else
  517.             {
  518.                 // This should have been initialized to zero during allocation, but lets be paranoid
  519.                 ASSERT( NULL == (*ppdtnMiddle)->pdtnConnected );
  520.             }
  521.             
  522.             // if this is not a directory, move the ppdtnMiddle up one
  523.             if (!ISDIRFINDDATA(*pfd))
  524.             {
  525.                 ppdtnMiddle = &(*ppdtnMiddle)->pdtnNext;
  526.             }
  527.             
  528.         }
  529.         
  530.     } while (fNeedToFindNext && !FOQueryAbort(pcs) && FindNextFile(hfind, pfd));
  531.     
  532.     iError = 0;  //It is possible that iError contains other errors value now! So, reset it!
  533.     
  534.     FindClose(hfind);
  535.     // now go and recurse into folders (if desired)
  536.     // we don't have to check to see if these pdtn's are dirs, because the
  537.     // way we inserted them above ensures that everything in from of
  538.     // ppdtnMiddle are folders
  539.     
  540.     // we're going to tack on a specific child
  541.     // then add the *.* after that
  542.     
  543.     while (!FOQueryAbort(pcs) && *ppdtnMiddle)
  544.     {
  545.         if (fRecurse)
  546.         {
  547.             if (PathAppend(pszPath, (*ppdtnMiddle)->szName))
  548.             {
  549.                 if (PathAppend(pszPath, c_szStarDotStar))
  550.                 {
  551.                     
  552.                     // NULL indicates that we do not want to get the connected elements.
  553.                     // This is because we want the connected elements only for the top-level items.
  554.                     iError = DTPathToDTNode(pdth, pcs, pszPath, TRUE, DTF_FILES_AND_FOLDERS,
  555.                         &((*ppdtnMiddle)->pdtnChild), pfd, *ppdtnMiddle, NULL, fConnectedElement, 0);
  556.                     
  557.                 }
  558.                 else
  559.                 {
  560.                     iError = OPER_ERROR | DE_INVALIDFILES;
  561.                 }
  562.                 
  563.                 PathRemoveFileSpec(pszPath);
  564.             }
  565.             else
  566.             {
  567.                 iError = OPER_ERROR | DE_INVALIDFILES;
  568.             }
  569.         }
  570.         else
  571.         {
  572.             // of we don't want to recurse, just mark them all as having no children
  573.             (*ppdtnMiddle)->pdtnChild = NULL;
  574.         }
  575.         
  576.         if (iError)
  577.         {
  578.             return iError;
  579.         }
  580.         
  581.         ppdtnMiddle = &(*ppdtnMiddle)->pdtnNext;
  582.     }
  583.     
  584.     return 0;
  585. }
  586. UINT DTAllocConnectedItemNodes(PDIRTREEHEADER pdth, COPY_STATE *pcs, WIN32_FIND_DATA *pfd, LPTSTR pszPath, BOOL fRecurse, PDIRTREENODE *ppdtnConnectedItems)
  587. {
  588.     // Since DTAllocConnectedItemNodes() gets called only for the top-level items in the src list,
  589.     // there is no danger of this function getting called recursively. Hence, I didn't worry about
  590.     // allocating the following on the stack.
  591.     // If "too-much-stack-is-used" problem arises, we can optimize the stack usage by splitting
  592.     // the following function into two such that the most common case (of no connection) 
  593.     // doesn't use much stack. 
  594.     DWORD   dwFileOrFolder;
  595.     TCHAR   szFullPath[MAX_PATH];
  596.     TCHAR   szFileName[MAX_PATH];
  597.     WIN32_FIND_DATA  fd;
  598.     int     iPrefixLength;  //This is the length of "foo" if the filename is "foo.htm" or "foo files"
  599.     
  600.     //Make a copy of the filename; This copy will get munged by ConvertToConnectedItemName().
  601.     lstrcpy(szFileName, pfd->cFileName);
  602.     // Convert the given file/foder name into the connected item's name with wild card characters.
  603.     if (!(iPrefixLength = ConvertToConnectedItemName(szFileName, ARRAYSIZE(szFileName), ISDIRFINDDATA(*pfd))))
  604.         return 0; //No connections exist for the given folder/file.
  605.     
  606.     // Now szFileName has the name of connected element with wildcard character.
  607.     
  608.     // If the given element is a directory, we want to look for connected FILES only  and
  609.     // if the given element is a file, we want to look for connected FOLDERS only.
  610.     dwFileOrFolder = ISDIRFINDDATA(*pfd) ? DTF_FILES_ONLY : DTF_FOLDERS_ONLY;
  611.     
  612.     // Form the file/folder name with the complete path!
  613.     lstrcpy(szFullPath, pszPath);
  614.     PathAppend(szFullPath, szFileName); 
  615.     
  616.     // The file-element has some "connected" items.
  617.     DebugMsg(TF_DEBUGCOPY, TEXT("DTAllocConnectedItemNodes Looking for %s"), szFullPath);
  618.     
  619.     return(DTPathToDTNode(pdth, pcs, szFullPath, fRecurse, dwFileOrFolder, ppdtnConnectedItems, &fd, NULL, NULL, TRUE, iPrefixLength));
  620. }
  621. void DTInitProgressPoints(PDIRTREEHEADER pdth, COPY_STATE *pcs)
  622. {
  623.     pdth->iFilePoints = 1;
  624.     pdth->iFolderPoints = 1;
  625.     
  626.     switch (pcs->lpfo->wFunc) {
  627.     case FO_RENAME:
  628.     case FO_DELETE:
  629.         pdth->iSizePoints = 0;
  630.         break;
  631.         
  632.     case FO_COPY:
  633.         pdth->iSizePoints = 1;
  634.         break;
  635.         
  636.     case FO_MOVE:
  637.         if (PathIsSameRoot(pcs->lpfo->pFrom, pcs->lpfo->pTo))
  638.         {
  639.             pdth->iSizePoints = 0;
  640.         }
  641.         else
  642.         {
  643.             // if it's across volumes, these points increase
  644.             // because we need to nuke the source as well as
  645.             // create the target...
  646.             // whereas we don't need to nuke the "size" of the source
  647.             pdth->iFilePoints = 2;
  648.             pdth->iFolderPoints = 2;
  649.             pdth->iSizePoints = 1;
  650.         }
  651.         break;
  652.     }
  653. }
  654. UINT DTBuild(COPY_STATE* pcs)
  655. {
  656.     PDIRTREEHEADER pdth = &pcs->dth;
  657.     WIN32_FIND_DATA fd;
  658.     TCHAR szPath[MAX_PATH];
  659.     PDIRTREENODE *ppdtn;
  660.     PDIRTREENODE *ppdtnConnectedItems;
  661.     int iError = 0;
  662.     
  663.     pcs->dth.pFrom = (LPTSTR)pcs->lpfo->pFrom;
  664.     pcs->dth.pTo = (LPTSTR)pcs->lpfo->pTo;
  665.     // A tree of original items will be built under ppdtn.
  666.     ppdtn = &pdth->pdtn;
  667.     // A tree of items "connected" to the orginal items will be built under ppdtnConnectedItems.
  668.     ppdtnConnectedItems = &pdth->pdtnConnectedItems;
  669.     
  670.     DTInitProgressPoints(pdth, pcs);
  671.     while (!FOQueryAbort(pcs) && *pdth->pFrom)
  672.     {
  673.         BOOL fRecurse = TRUE;
  674.         
  675.         switch (pcs->lpfo->wFunc)
  676.         {
  677.         case FO_MOVE:
  678.             // The move operation doesn't need to recurse if we are moving from and to the same
  679.             // volume.  In this case we know that we don't need to display any warnings for
  680.             // things like LFN to 8.3 filename conversion or stream loss.  Instead, we can do
  681.             // the operation with one single win32 file operation that just does a rename.
  682.             // MAJOR BUGBUG (toddb): This is only true if we don't cross a mount point!  If we cross
  683.             // a mount point then we might have to warn about these things.
  684.             if ((pcs->fFlags & FOF_NORECURSION) || PathIsSameRoot(pdth->pFrom, pdth->pTo))
  685.             {
  686.                 fRecurse = FALSE;
  687.             }
  688.             break;
  689.             
  690.         case FO_COPY:
  691.             // For a copy we always recurse unless we're told not to.
  692.             if (pcs->fFlags & FOF_NORECURSION)
  693.             {
  694.                 fRecurse = FALSE;
  695.             }
  696.             break;
  697.             
  698.         case FO_RENAME:
  699.             // for a rename we never recurse
  700.             fRecurse = FALSE;
  701.             break;
  702.             
  703.         case FO_DELETE:
  704.             // for a delete we don't need to recurse IF the recycle bin will be able to handle
  705.             // the given item.  If the recycle bin handles the delete then we can undo from
  706.             // the recycle bin if we need to.
  707.             if ((pcs->fFlags & FOF_ALLOWUNDO) && BBWillRecycle(pdth->pFrom, NULL))
  708.             {
  709.                 fRecurse = FALSE;
  710.             }
  711.             break;
  712.         }
  713.         lstrcpy(szPath, pdth->pFrom);
  714.         DebugMsg(TF_DEBUGCOPY, TEXT("DTBuild: %s"), szPath);
  715.         // If the file is on removable media, we need to check for media in the drive.
  716.         // Prompt the user to insert the media if it's missing.
  717.         if (!DTDiskCheck(pdth, pcs, szPath))
  718.         {
  719.             iError = ERROR_CANCELLED;
  720.             break;
  721.         }
  722.         iError = DTPathToDTNode(pdth, pcs, szPath, fRecurse,
  723.             ((PathIsWild(pdth->pFrom) && (pcs->lpfo->fFlags & FOF_FILESONLY)) ? DTF_FILES_ONLY : DTF_FILES_AND_FOLDERS), 
  724.             ppdtn,&fd, NULL, ppdtnConnectedItems, FALSE, 0);
  725.         DebugMsg(TF_DEBUGCOPY, TEXT("DTBuild: returned %d"), iError);
  726.         // BUGBUG: If an error occured we should allow the user to skip the file that caused the error.  That way
  727.         // if one of the source files doesn't exists the rest will still get copied.  Do this only in the multi-
  728.         // source case, blah blah blah.  This helps in the case where one of the source files cannot be moved or
  729.         // copied (usually due to Access Denied, could be insuffecent permissions or file is in use, etc).
  730.         if (iError)
  731.             break;
  732.         
  733.         if (!(*ppdtn) && PathIsWild(pdth->pFrom))
  734.         {
  735.             // no files are associated with this path... this
  736.             // can happen when we have wildcards...
  737.             // alloc a dummy node
  738.             *ppdtn = DTAllocNode(pdth, NULL, NULL, NULL, FALSE);
  739.             if (*ppdtn)
  740.             {
  741.                 (*ppdtn)->fDummy = TRUE;
  742.             }
  743.         }
  744.         if (*ppdtn)
  745.         {
  746.             // mark this as the start of a root spec... this is
  747.             // necessary in case we have several wild specs
  748.             (*ppdtn)->fNewRoot = TRUE;
  749.         }
  750.         
  751.         if (*ppdtnConnectedItems)
  752.         {
  753.             // Mark this as the start of a root spec.
  754.             (*ppdtnConnectedItems)->fNewRoot = TRUE;
  755.             // For connected items, we need to remember the path.
  756.             (*ppdtnConnectedItems)->ConnectedInfo.pFromConnected = pdth->pFrom;
  757.             (*ppdtnConnectedItems)->ConnectedInfo.pToConnected = pdth->pTo;
  758.         }
  759.         while (*ppdtn)
  760.         {
  761.             ppdtn = &(*ppdtn)->pdtnNext;
  762.         }
  763.         
  764.         while (*ppdtnConnectedItems)
  765.         {
  766.             ppdtnConnectedItems = &(*ppdtnConnectedItems)->pdtnNext;
  767.         }
  768.         
  769.         pdth->pFrom += lstrlen(pdth->pFrom) + 1;
  770.         if (pcs->lpfo->wFunc != FO_DELETE && (pcs->lpfo->fFlags & FOF_MULTIDESTFILES))
  771.         {
  772.             pdth->pTo += lstrlen(pdth->pTo) + 1;
  773.         }
  774.     }
  775.     
  776.     //Attach the "ConnectedElements" Tree to the end of the source element tree.
  777.     *ppdtn = pcs->dth.pdtnConnectedItems;
  778.     
  779.     pcs->dth.pFrom = (LPTSTR)pcs->lpfo->pFrom;
  780.     pcs->dth.pTo = (LPTSTR)pcs->lpfo->pTo;
  781.     pcs->fDTBuilt = TRUE;
  782.     
  783.     // set up the initial time information
  784.     pcs->dwPreviousTime = GetTickCount();
  785.     pcs->dwPointsPerSec = 0;
  786.     pcs->iLastProgressPoints = 0;
  787.     return iError;
  788. }
  789. #define DTNIsRootNode(pdtn) ((pdtn)->pdtnParent == NULL)
  790. #define DTNIsDirectory(pdtn) (pdtn->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  791. // This macro determines if the given node is an "Origin" of a connection. i.e. Does this node 
  792. // point to a connected element that needs to be moved/copied etc., along with it?
  793. // For example, if "foo.htm" is moved, "foo files" is also moved. 
  794. // Here, "foo.htm" is the "Connect origin" (fConnectedElement = FALSE; pdtnConnected is valid)
  795. // and "foo files" is the "connected element". (fConnectedElement = TRUE; )
  796. #define DTNIsConnectOrigin(pdtn) ((!pdtn->fConnectedElement) && (pdtn->pdtnConnected != NULL))
  797. #define DTNIsConnected(pdtn)    (pdtn && (pdtn->fConnectedElement))
  798. //
  799. UINT DTEnumChildren(PDIRTREEHEADER pdth, COPY_STATE *pcs, BOOL fRecurse, DWORD dwFileOrFolder)
  800. {
  801.     int iError = 0;
  802.     if (pdth->pdtnCurrent->pdtnChild == DTN_DELAYED)
  803.     {
  804.         WIN32_FIND_DATA fd;
  805.         
  806.         // fill in all the children and update the stats in pdth
  807.         if (PathAppend(pdth->szSrcPath, c_szStarDotStar))
  808.         {
  809.             iError = DTPathToDTNode(pdth, pcs, pdth->szSrcPath, fRecurse, dwFileOrFolder,
  810.                 &pdth->pdtnCurrent->pdtnChild, &fd, pdth->pdtnCurrent, NULL, pdth->pdtnCurrent->fConnectedElement, 0);
  811.         }
  812.         else
  813.         {
  814.             iError = OPER_ERROR | DE_INVALIDFILES;
  815.         }
  816.         
  817.         // If we get "File Not Found" Error now and if it is a connected item, then this item 
  818.         // must have already been moved/renamed/deleted etc., So, this is not really an error. 
  819.         // All this means is that this connected item was also explicitly selected and hence appeared
  820.         // as or "Origin" item earlier in the list and it had already been operated upon.
  821.         // So, reset the error here.
  822.         // (Example: If end-user selects "foo.htm" AND "foo files" folder and moves them, then we
  823.         // will get a file-not-found error when we attempt to move the connected items. To avoid
  824.         // this error dialog, we reset the error here.)
  825.         
  826.         if (DTNIsConnected(pdth->pdtnCurrent) && (iError == (OPER_ERROR | DE_FILENOTFOUND)))
  827.             iError = 0;  
  828.     }
  829.     return iError;
  830. }
  831. //
  832. // DTNGetConfirmationResult:
  833. //    When a file("foo.htm") is moved/copied, we may put up a confirmation dialog in case 
  834. // of a conflict and the end-user might have responded saying "Yes", "no" etc., When the 
  835. // corresponding connected element ("foo files") is also moved/copied etc., we should NOT put up
  836. // a confirmation dialog again. We must simply store the answer to the original confirmation and
  837. // use it later. 
  838. //  This function retries the result of the original confirmation from the top-level connected 
  839. // element.
  840. int  DTNGetConfirmationResult(PDIRTREENODE pdtn)
  841. {
  842.     //Confirmation results are saved only for Connected items; Not for Connection Origins.
  843.     if (!pdtn || !DTNIsConnected(pdtn))
  844.         return 0;
  845.     
  846.     //Confirmation results are stored only at the top-level node. So, go there.
  847.     while(pdtn->pdtnParent)
  848.         pdtn = pdtn->pdtnParent;
  849.     
  850.     return(pdtn->ConnectedInfo.dwConfirmation);
  851. }
  852. void DTGetWin32FindData(PDIRTREENODE pdtn, WIN32_FIND_DATA* pfd)
  853. {
  854.     // only the stuff we care about
  855.     lstrcpy(pfd->cAlternateFileName, pdtn->szShortName);
  856.     lstrcpy(pfd->cFileName, pdtn->szName);
  857.     
  858.     pfd->dwFileAttributes = pdtn->dwFileAttributes;
  859.     pfd->ftCreationTime   = pdtn->ftCreationTime;
  860.     pfd->ftLastWriteTime  = pdtn->ftLastWriteTime;
  861.     pfd->nFileSizeLow     = pdtn->nFileSizeLow;
  862.     pfd->nFileSizeHigh     = pdtn->nFileSizeHigh;
  863. }
  864. void DTSetFileCopyProgress(PDIRTREEHEADER pdth, DWORD dwRead)
  865. {
  866.     DWORD dwDelta;
  867.     
  868.     dwDelta = (dwRead - pdth->pdtnCurrent->nFileSizeCopied);
  869.     DebugMsg(TF_DEBUGCOPY, TEXT("DTSetFileCopyProgress %d %d %d"), dwDelta, dwRead, pdth->dtDone.dwSize);
  870.     pdth->pdtnCurrent->nFileSizeCopied += dwDelta;
  871.     pdth->dtDone.dwSize += dwDelta;
  872.     DebugMsg(TF_DEBUGCOPY, TEXT("DTSetFileCopyProgress %d %d"), dwDelta, pdth->dtDone.dwSize);
  873.     pdth->dtDone.fChanged = TRUE;
  874. }
  875. void DTFreeNode(PDIRTREEHEADER pdth, PDIRTREENODE pdtn)
  876. {
  877.     if (pdth)
  878.     {
  879.         ASSERT(pdtn->pdtnChild == NULL || pdtn->pdtnChild == DTN_DELAYED);
  880.         
  881.         // we're done with this node..  update the header totals
  882.         if (DTNIsDirectory(pdtn)) {
  883.             pdth->dtDone.dwFolders++;
  884.         } else {
  885.             pdth->dtDone.dwFiles++;
  886.             pdth->dtDone.dwSize += (pdtn->nFileSizeLow - pdtn->nFileSizeCopied);
  887.         }
  888.         
  889.         pdth->dtDone.fChanged = TRUE;
  890.         
  891.         // repoint parent pointer
  892.         if (!pdtn->pdtnParent) {
  893.             
  894.             // no parent... must be a root type thing
  895.             ASSERT(pdth->pdtn == pdtn);
  896.             pdth->pdtn = pdtn->pdtnNext;
  897.             
  898.         } else {
  899.             
  900.             ASSERT(pdtn->pdtnParent->pdtnChild == pdtn);
  901.             if (pdtn->pdtnParent->pdtnChild == pdtn) {
  902.                 // if my parent was pointing to me, point him to my sib
  903.                 pdtn->pdtnParent->pdtnChild = pdtn->pdtnNext;
  904.             }
  905.         }
  906.     }
  907.     
  908.     LocalFree(pdtn);
  909. }
  910. // this frees all children of (but NOT including) the current node.
  911. // it doesn' free the current node because it's assumed that
  912. // DTGoToNextNode will be called right afterwards, and that will
  913. // free the current node
  914. void DTFreeChildrenNodes(PDIRTREEHEADER pdth, PDIRTREENODE pdtn)
  915. {
  916.     PDIRTREENODE pdtnChild = pdtn->pdtnChild;
  917.     while (pdtnChild && pdtnChild != DTN_DELAYED)
  918.     {
  919.         PDIRTREENODE pdtnNext = pdtnChild->pdtnNext;
  920.         
  921.         // recurse and free these children
  922.         if (DTNIsDirectory(pdtnChild))
  923.         {
  924.             DTFreeChildrenNodes(pdth, pdtnChild);
  925.         }
  926.         
  927.         DTFreeNode(pdth, pdtnChild);
  928.         pdtnChild = pdtnNext;
  929.     }
  930.     
  931.     pdtn->pdtnChild = NULL;
  932. }
  933. void DTForceEnumChildren(PDIRTREEHEADER pdth)
  934. {
  935.     if (!pdth->pdtnCurrent->pdtnChild)
  936.         pdth->pdtnCurrent->pdtnChild = DTN_DELAYED;
  937. }
  938. void DTAbortCurrentNode(PDIRTREEHEADER pdth)
  939. {
  940.     DTFreeChildrenNodes((pdth), (pdth)->pdtnCurrent);
  941.     if (pdth->oper == OPER_ENTERDIR)
  942.         pdth->oper = OPER_LEAVEDIR;
  943. }
  944. void DTCleanup(PDIRTREEHEADER pdth)
  945. {
  946.     PDIRTREENODE pdtn;
  947.     
  948.     while (pdth->pdtnCurrent && pdth->pdtnCurrent->pdtnParent)
  949.     {
  950.         // in case we bailed deep in a tree
  951.         pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnParent;
  952.     }
  953.     
  954.     while (pdth->pdtnCurrent)
  955.     {
  956.         pdtn = pdth->pdtnCurrent;
  957.         pdth->pdtnCurrent = pdtn->pdtnNext;
  958.         DTFreeChildrenNodes(NULL, pdtn);
  959.         DTFreeNode(NULL, pdtn);
  960.     }
  961. }
  962. BOOL DTInitializePaths(PDIRTREEHEADER pdth, COPY_STATE *pcs)
  963. {
  964.     ASSERT( pdth->pdtnCurrent );    // If we have no current node then how can we Initialize its paths?
  965.     
  966.     lstrcpyn(pdth->szSrcPath, pdth->pFrom, ARRAYSIZE(pdth->szSrcPath));
  967.     
  968.     // For the "Origins" we need to do this only if a wild card exists. However, for connected elements,
  969.     // we need to do this everytime because connected elements may not exist for every "Origins"
  970.     if (PathIsWild(pdth->pFrom) || (pdth->pdtnCurrent->fNewRoot && DTNIsConnected(pdth->pdtnCurrent)))
  971.     {
  972.         PathRemoveFileSpec(pdth->szSrcPath);
  973.         if (!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName))
  974.             return FALSE;
  975.     }
  976.     
  977.     if (!pdth->pTo)
  978.     {
  979.         // no dest, make it the same as the source and we're done
  980.         lstrcpyn(pdth->szDestPath, pdth->szSrcPath, ARRAYSIZE(pdth->szSrcPath));
  981.         return TRUE;
  982.     }
  983.     
  984.     if (pdth->pTo)
  985.     {
  986.         lstrcpyn(pdth->szDestPath, pdth->pTo, ARRAYSIZE(pdth->szSrcPath));
  987.     }
  988.     
  989.     if (!pdth->fMultiDest)
  990.     {
  991.         if (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
  992.             return FALSE;
  993.     }
  994.     else
  995.     {
  996.         //When undo of a move operation is done, fMultiDest is set.
  997.         // When fMultiDest is set, we need to strip out the filename given by pTo and 
  998.         // append the current filename.
  999.         // For RENAME operations, the source and destination names are different. This is handled 
  1000.         // seperately below. So, we handle only other operations here where source and dest names are the same.
  1001.         if ((pcs->lpfo->wFunc != FO_RENAME) && pdth->pdtnCurrent->fNewRoot && DTNIsConnected(pdth->pdtnCurrent))
  1002.         {
  1003.             PathRemoveFileSpec(pdth->szDestPath);
  1004.             if (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
  1005.                 return FALSE;
  1006.         }
  1007.     }
  1008.     //We will never try to rename a connected element! Make sure we don't hit this!
  1009.     ASSERT(!((pcs->lpfo->wFunc == FO_RENAME) && DTNIsConnected(pdth->pdtnCurrent)));
  1010.     
  1011.     // BUGBUG: chee implement me
  1012.     
  1013.     return TRUE;
  1014.     
  1015. }
  1016. UINT DTValidatePathNames(PDIRTREEHEADER pdth, UINT operation, COPY_STATE * pcs)
  1017. {
  1018.     if (pcs->lpfo->wFunc != FO_DELETE)
  1019.     {
  1020.         // Why process name mappings?  Here's why.  If we are asked to copy directory "c:foo" and
  1021.         // file "c:foofile" to another directory (say "d:") we might have a name confilct when
  1022.         // we copy "c:foo" so instead we create "d:Copy Of foo".  Later, we walk to the second
  1023.         // dirtree node and we are asked to copy "c:foofile" to "d:foo", all of which is valid.
  1024.         // HOWEVER, it's not what we want to do.  We use _ProccessNameMappings to convert
  1025.         // "d:foofile" into "d:Copy of foofile".
  1026.         _ProcessNameMappings(pdth->szDestPath, pdth->hdsaRenamePairs);
  1027.         
  1028.         // REVIEW, do we need to do the name mapping here or just let the
  1029.         // VFAT do it?  if vfat does it we need to rip out all of the GetNameDialog() stuff.
  1030.         
  1031.         if ((operation != OPER_LEAVEDIR) &&
  1032.             !IsLFNDrive(pdth->szDestPath) &&
  1033.             PathIsLFNFileSpec(PathFindFileName(pdth->szSrcPath)) &&
  1034.             PathIsLFNFileSpec(PathFindFileName(pdth->szDestPath)))
  1035.         {
  1036.             
  1037.             int iRet;
  1038.             TCHAR szOldDest[MAX_PATH];
  1039.             
  1040.             lstrcpy(szOldDest, pdth->szDestPath);
  1041.             iRet = GetNameDialog(pcs->hwndDlgParent, pcs,
  1042.                 (pcs->nSourceFiles != 1 ) || !DTNIsRootNode(pdth->pdtnCurrent), // if we're entering a dir, multiple spec, or not at root
  1043.                 operation, pdth->szSrcPath, pdth->szDestPath);
  1044.             
  1045.             switch (iRet) {
  1046.             case IDNO:
  1047.             case IDCANCEL:
  1048.                 return iRet;
  1049.                 
  1050.             default:
  1051.                 AddRenamePairToHDSA(szOldDest, pdth->szDestPath, &pcs->dth.hdsaRenamePairs);
  1052.                 break;
  1053.             }
  1054.         }
  1055.         
  1056.         if (operation == OPER_ENTERDIR)
  1057.         {
  1058.             // Make sure the new directory is not a subdir of the original...
  1059.             
  1060.             int cchFrom = lstrlen(pdth->szSrcPath);
  1061.             
  1062.             // BUGBUG: Shouldn't we get the short names for both these directories and compair those?
  1063.             // Otherwise I can copy "C:Long Directory Name" to "C:LongDi~1foo" without error.
  1064.             if (!(pcs->fFlags & FOF_RENAMEONCOLLISION) &&
  1065.                 !StrCmpNI(pdth->szSrcPath, pdth->szDestPath, cchFrom))
  1066.             {
  1067.                 TCHAR chNext = pdth->szDestPath[cchFrom]; // Get the next char in the dest.
  1068.                 
  1069.                 if (!chNext)
  1070.                 {
  1071.                     return OPER_ERROR | DE_DESTSAMETREE;
  1072.                 }
  1073.                 else if (chNext == TEXT('\'))
  1074.                 {
  1075.                     // The two fully qualified strings are equal up to the end
  1076.                     // of the source directory ==> the destination is a subdir.
  1077.                     // Must return an error.
  1078.                     
  1079.                     // if, stripping the last file name and the backslash give the same length, they are the
  1080.                     // same file/folder
  1081.                     if ((PathFindFileName(pdth->szDestPath) - pdth->szDestPath - 1) ==
  1082.                         lstrlen(pdth->szSrcPath))
  1083.                     {
  1084.                         return OPER_ERROR | DE_DESTSAMETREE;
  1085.                     }
  1086.                     else
  1087.                     {
  1088.                         return OPER_ERROR | DE_DESTSUBTREE;
  1089.                     }
  1090.                 }
  1091.             }
  1092.         }
  1093.     }
  1094.     return 0;
  1095. }
  1096. // this moves to the next node (child, sib, parent) and sets up the
  1097. // directory path info and oper state
  1098. UINT DTGoToNextNode(PDIRTREEHEADER pdth, COPY_STATE *pcs)
  1099. {
  1100.     UINT oper = OPER_ENTERDIR; // the default
  1101.     int iError;
  1102.     
  1103.     if (!pdth->pdtnCurrent)
  1104.     {
  1105.         pdth->pdtnCurrent = pdth->pdtn;
  1106.         
  1107.         if (pdth->pdtnCurrent)
  1108.         {
  1109.             if (pdth->pdtnCurrent->fDummy)
  1110.             {
  1111.                 // if this is just a placeholder... go on to the next one
  1112.                 return DTGoToNextNode(pdth, pcs);
  1113.             }
  1114.             
  1115.             if (!DTInitializePaths(pdth, pcs))
  1116.             {
  1117.                 return OPER_ERROR | DE_INVALIDFILES;
  1118.             }
  1119.         }
  1120.         else
  1121.         {
  1122.             // Our tree is completely empty.
  1123.             
  1124.             // REVIEW: What do we do here?  If pdtnCurrent is still NULL then our list is completely empty.
  1125.             // Is that a bug or what?  My hunch is that we should return an error code here, most likely
  1126.             // OPER_ERROR | DE_INVALIDFILES.  If we do nothing here then we will fail silently.
  1127.             return OPER_ERROR | DE_INVALIDFILES;
  1128.         }
  1129.     }
  1130.     else
  1131.     {
  1132.         UINT iError;
  1133.         BOOL fFreeLastNode = TRUE;
  1134.         PDIRTREENODE pdtnLastCurrent = pdth->pdtnCurrent;
  1135.         
  1136.         if (iError = DTEnumChildren(pdth, pcs, FALSE, DTF_FILES_AND_FOLDERS))
  1137.             return iError;
  1138.         
  1139.         if (pdth->pdtnCurrent->pdtnChild)
  1140.         {
  1141.             fFreeLastNode = FALSE;
  1142.             pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnChild;
  1143.             
  1144.             // if the long name is too long, try the short name
  1145.             if ((!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName) &&
  1146.                 !PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szShortName)) ||
  1147.                 (!PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName) &&
  1148.                 !PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szShortName)))
  1149.                 return OPER_ERROR | DE_INVALIDFILES;
  1150.             
  1151.         }
  1152.         else if (pdth->oper == OPER_ENTERDIR)
  1153.         {
  1154.             // if the last operation was an enterdir and it has no children
  1155.             // (because it failed the above test
  1156.             // then we should do a leave dir on it now
  1157.             oper = OPER_LEAVEDIR;
  1158.             fFreeLastNode = FALSE;
  1159.             
  1160.         }
  1161.         else if (pdth->pdtnCurrent->pdtnNext)
  1162.         {
  1163.             pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnNext;
  1164.             
  1165.             if (!pdth->pdtnCurrent->pdtnParent)
  1166.             {
  1167.                 // if this was the top, we need to build the next path info
  1168.                 // from scratch
  1169.                 
  1170.                 if (pdth->pdtnCurrent->fNewRoot)
  1171.                 {
  1172.                     if (pdth->pdtnCurrent->fConnectedElement)
  1173.                     {
  1174.                         // Since this is a new root in a Connected list, the pFrom and pTo are
  1175.                         // stored in the node itself. This is needed because Connected elements may
  1176.                         // not exist for every item in the source list and we do not want to create dummy
  1177.                         // nodes for each one of them. So, pFrom and pTo are stored for every NewRoot of
  1178.                         // connected elements and we use these here.
  1179.                         pdth->pFrom = pdth->pdtnCurrent->ConnectedInfo.pFromConnected;
  1180.                         pdth->pTo = pdth->pdtnCurrent->ConnectedInfo.pToConnected;
  1181.                     }
  1182.                     else
  1183.                     {
  1184.                         // go to the next path pair
  1185.                         pdth->pFrom += lstrlen(pdth->pFrom) + 1;
  1186.                         if (pdth->pTo)
  1187.                         {
  1188.                             if (pdth->fMultiDest)
  1189.                             {
  1190.                                 pdth->pTo += lstrlen(pdth->pTo) + 1;
  1191.                             }
  1192.                         }
  1193.                     }
  1194.                 }
  1195.                 
  1196.                 if (pdth->pdtnCurrent->fDummy)
  1197.                 {
  1198.                     // if this is just a placeholder... go on to the next one
  1199.                     if (fFreeLastNode)
  1200.                     {
  1201.                         DTFreeNode(pdth, pdtnLastCurrent);
  1202.                     }
  1203.                     return DTGoToNextNode(pdth, pcs);
  1204.                 }
  1205.                 
  1206.                 DTInitializePaths(pdth, pcs);
  1207.             }
  1208.             else
  1209.             {
  1210.                 
  1211.                 PathRemoveFileSpec(pdth->szSrcPath);
  1212.                 PathRemoveFileSpec(pdth->szDestPath);
  1213.                 
  1214.                 if (!PathAppend(pdth->szSrcPath, pdth->pdtnCurrent->szName) ||
  1215.                     !PathAppend(pdth->szDestPath, pdth->pdtnCurrent->szName))
  1216.                     return OPER_ERROR | DE_INVALIDFILES;
  1217.             }
  1218.         }
  1219.         else
  1220.         {
  1221.             oper = OPER_LEAVEDIR;
  1222.             PathRemoveFileSpec(pdth->szSrcPath);
  1223.             PathRemoveFileSpec(pdth->szDestPath);
  1224.             pdth->pdtnCurrent = pdth->pdtnCurrent->pdtnParent;
  1225.         }
  1226.         
  1227.         if (fFreeLastNode)
  1228.         {
  1229.             DTFreeNode(pdth, pdtnLastCurrent);
  1230.         }
  1231.     }
  1232.     
  1233.     if (!pdth->pdtnCurrent)
  1234.     {
  1235.         // no more!  we're done!
  1236.         return 0;
  1237.     }
  1238.     
  1239.     DebugDumpPDTN(pdth->pdtnCurrent, TEXT("PDTNCurrent"));
  1240.     
  1241.     if (oper == OPER_ENTERDIR)
  1242.     {
  1243.         if (pcs->lpfo->wFunc == FO_RENAME || !DTNIsDirectory(pdth->pdtnCurrent))
  1244.         {
  1245.             oper = OPER_DOFILE;
  1246.         }
  1247.     }
  1248.     
  1249.     if (DTNIsRootNode(pdth->pdtnCurrent))
  1250.     {
  1251.         // we need to diskcheck the source and target because this might
  1252.         // be the first time we've seen this drive
  1253.         if (!DTDiskCheck(pdth, pcs, pdth->szSrcPath) ||
  1254.             !DTDiskCheck(pdth, pcs, pdth->szDestPath))
  1255.         {
  1256.             return 0;
  1257.         }
  1258.     }
  1259.     
  1260.     iError = DTValidatePathNames(pdth, oper, pcs);
  1261.     if (iError)
  1262.     {
  1263.         if (iError & OPER_ERROR)
  1264.         {
  1265.             //For connected nodes, ignore the error and silently abort the node!
  1266.             if (DTNIsConnected(pdth->pdtnCurrent))
  1267.             {
  1268.                 DTAbortCurrentNode(pdth);
  1269.                 return DTGoToNextNode(pdth, pcs);
  1270.             }
  1271.             else
  1272.                 return iError;
  1273.         }
  1274.         else
  1275.         {
  1276.             switch (iError) 
  1277.             {
  1278.             case IDNO:
  1279.                 DTAbortCurrentNode(pdth);
  1280.                 pcs->lpfo->fAnyOperationsAborted = TRUE;
  1281.                 return DTGoToNextNode(pdth, pcs);
  1282.                 
  1283.             case IDCANCEL:
  1284.                 // User cancelled the operation
  1285.                 pcs->bAbort = TRUE;
  1286.                 return 0;
  1287.             }
  1288.         }
  1289.     }
  1290.     
  1291.     pdth->oper = oper;
  1292.     return oper;
  1293. }
  1294. int  CopyMoveRetry(COPY_STATE *pcs, LPCTSTR pszDest, int error, DWORD dwFileSize);
  1295. void CopyError(LPCOPY_STATE, LPCTSTR, LPCTSTR, int, UINT, int);
  1296. void SetProgressTime(COPY_STATE *pcs);
  1297. void SetProgressText(COPY_STATE *pcs, LPCTSTR pszFrom, LPCTSTR pszTo);
  1298. void FOUndo_AddInfo(LPUNDOATOM lpua, LPTSTR lpszSrc, LPTSTR lpszDest, DWORD dwAttributes);
  1299. void CALLBACK FOUndo_Release(LPUNDOATOM lpua);
  1300. void FOUndo_FileReallyDeleted(LPTSTR lpszFile);
  1301. void AddRenamePairToHDSA(LPCTSTR pszOldPath, LPCTSTR pszNewPath, HDSA* phdsaRenamePairs);
  1302. BOOL_PTR CALLBACK FOFProgressDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam);
  1303. typedef struct {
  1304.     LPTSTR lpszName;
  1305.     DWORD dwAttributes;
  1306. } FOUNDO_DELETEDFILEINFO, *LPFOUNDO_DELETEDFILEINFO;
  1307. typedef struct {
  1308.     HDPA hdpa;
  1309.     HDSA hdsa;
  1310. } FOUNDODATA, *LPFOUNDODATA;
  1311. DWORD CALLBACK FOUIThreadProc(COPY_STATE *pcs)
  1312. {
  1313.     DWORD dwShowTime;
  1314.     HWND hwnd;
  1315.     int iShowTimeLeft;
  1316.     
  1317.     DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- Begin"));
  1318.     
  1319.     Sleep(SHOW_PROGRESS_TIMEOUT);
  1320.     
  1321.     if (pcs->fDone)
  1322.     {
  1323.         DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- End . Done before we started"));
  1324.         return 0;
  1325.     }
  1326.     
  1327.     hwnd = CreateDialogParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_MOVECOPYPROGRESS),
  1328.         pcs->lpfo->hwnd, FOFProgressDlgProc, (LPARAM)pcs);
  1329.     
  1330.     // crit section to sync with main thread termination
  1331.     ENTERCRITICAL;
  1332.     
  1333.     if (!pcs->fDone)
  1334.     {
  1335.         pcs->hwndProgress = hwnd;
  1336.     }
  1337.     LEAVECRITICAL;
  1338.     
  1339.     if (pcs->hwndProgress)
  1340.     {
  1341.         MSG msg;
  1342.         
  1343.         dwShowTime = GetTickCount();
  1344.         while(!pcs->fDone && GetMessage(&msg, NULL, 0, 0)) 
  1345.         {
  1346.             if (!pcs->fDone && !IsDialogMessage(pcs->hwndProgress, &msg)) 
  1347.             {
  1348.                 TranslateMessage(&msg);
  1349.                 DispatchMessage(&msg);
  1350.             }
  1351.         }
  1352.         
  1353.         // if we've put it up, we need to keep it up for at least some minimal amount of time
  1354.         iShowTimeLeft = MINSHOWTIME - (GetTickCount() - dwShowTime);
  1355.         if (iShowTimeLeft > 0) 
  1356.         {
  1357.             DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- doing an extra sleep"));
  1358.             Sleep(iShowTimeLeft);
  1359.         }
  1360.     }
  1361.     
  1362.     // Keep us from doing this while other thread processing...
  1363.     ENTERCRITICAL;
  1364.     pcs->hwndProgress = NULL;
  1365.     LEAVECRITICAL;
  1366.     
  1367.     DestroyWindow(hwnd);
  1368.     
  1369.     DebugMsg(TF_DEBUGCOPY, TEXT("FOUIThreadProc -- End . Completed"));
  1370.     return 0;
  1371. }
  1372. // this queries the progress dialog for a cancel and yields.
  1373. // it also will show the progress dialog if a certain amount of time has passed
  1374. //
  1375. // returns:
  1376. //    TRUE      cacnel was pressed, abort the operation
  1377. //    FALSE     continue
  1378. BOOL FOQueryAbort(COPY_STATE *pcs)
  1379. {
  1380.     if (!pcs->bAbort && pcs->hwndProgress) 
  1381.     {
  1382.         if (pcs->hwndProgress != pcs->hwndDlgParent) 
  1383.         {
  1384.             // do this here rather than on the FOUIThreadProc so that we don't have
  1385.             // synchronization problems with this thread popping up a dialog on
  1386.             // hwndDlgParent then the progress dialog coming up afterwards on top.
  1387.             pcs->hwndDlgParent = pcs->hwndProgress;
  1388.             ShowWindow(pcs->hwndProgress, SW_SHOW);
  1389.             SetForegroundWindow(pcs->hwndProgress);
  1390.             SetFocus(GetDlgItem(pcs->hwndProgress, IDCANCEL));
  1391.             
  1392.             SetProgressText(pcs, pcs->dth.szSrcPath,
  1393.                 pcs->lpfo->wFunc == FO_DELETE ? NULL : pcs->dth.szDestPath);
  1394.         } 
  1395.         else 
  1396.         {
  1397.             MSG msg;
  1398.             
  1399.             // win95 handled messages in here.
  1400.             // we need to do the same in order to flush the input queue as well as
  1401.             // for backwards compatability.
  1402.             
  1403.             // we need to flush the input queue now because hwndProgress is
  1404.             // on a different thread... which means it has attached thread inputs
  1405.             // inorder to unlock the attached threads, we need to remove some
  1406.             // sort of message until there's none left... any type of message..
  1407.             while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  1408.             {
  1409.                 if (!IsDialogMessage(pcs->hwndProgress, &msg)) 
  1410.                 {
  1411.                     TranslateMessage(&msg);
  1412.                     DispatchMessage(&msg);
  1413.                 }
  1414.             }
  1415.         }
  1416.         
  1417.         if (pcs->dth.dtAll.fChanged || pcs->dth.dtDone.fChanged ) 
  1418.         {
  1419.             if (!pcs->dth.fChangePosted) 
  1420.             {
  1421.                 // set the flag first because with async threads
  1422.                 // the progress window could handle it and clear the
  1423.                 // bit before we set it.. then we'd lose further messages
  1424.                 // thinking that one was still pending
  1425.                 pcs->dth.fChangePosted = TRUE;
  1426.                 if (!PostMessage(pcs->hwndProgress, PDM_UPDATE, 0, 0))
  1427.                     pcs->dth.fChangePosted = FALSE;
  1428.             }
  1429.         }
  1430.     }
  1431.     
  1432.     return pcs->bAbort;
  1433. }
  1434. typedef struct _confdlg_data {
  1435.     LPCTSTR pFileDest;
  1436.     LPCTSTR pFileSource;
  1437.     LPCTSTR pStreamNames;
  1438.     const WIN32_FIND_DATA *pfdDest;
  1439.     const WIN32_FIND_DATA *pfdSource;
  1440.     
  1441.     BOOL bShowCancel;           // allow cancel out of this operation
  1442.     BOOL bShowDates;            // use date/size info in message
  1443.     UINT uDeleteWarning;        // warn that the delete's not going to the wastebasket
  1444.     BOOL bFireIcon;
  1445.     BOOL bShrinkDialog;         // should we move the buttons up to the text?
  1446.     int  nSourceFiles;          // if != 1 used to build "n files" string
  1447.     int idText;                 // if != 0 use to override string in dlg template
  1448.     CONFIRM_FLAG fConfirm;      // we will confirm things set here
  1449.     CONFIRM_FLAG fYesMask;      // these bits are cleared in fConfirm on "yes"
  1450.                                 // Only use fYesMask for things that should be confirmed once per operation
  1451.     CONFIRM_FLAG fYesToAllMask; // these bits are cleared in fConfirm on "yes to all"
  1452.     //COPY_STATE *pcs;
  1453.     CONFIRM_DATA *pcd;
  1454.     void (*InitConfirmDlg)(HWND hDlg, struct _confdlg_data *pcd);  // routine to initialize dialog
  1455.     BOOL bARPWarning; 
  1456. } CONFDLG_DATA;
  1457. BOOL BuildDateLine(LPTSTR pszDateLine, const WIN32_FIND_DATA *pFind, LPCTSTR pFileName)
  1458. {
  1459.     TCHAR szTemplate[64];
  1460.     TCHAR szNum[32], szTmp[64];
  1461.     WIN32_FIND_DATA fd;
  1462.     ULARGE_INTEGER liFileSize;
  1463.     
  1464.     if (!pFind) 
  1465.     {
  1466.         HANDLE hfind = FindFirstFile(pFileName, &fd);
  1467.         ASSERT(hfind != INVALID_HANDLE_VALUE);
  1468.         FindClose(hfind);
  1469.         pFind = &fd;
  1470.     }
  1471.     
  1472.     liFileSize.LowPart  = pFind->nFileSizeLow;
  1473.     liFileSize.HighPart = pFind->nFileSizeHigh;
  1474.     
  1475.     // There are cases where the date is 0, this is especially true when the 
  1476.     // source is from a file contents...
  1477.     if (pFind->ftLastWriteTime.dwLowDateTime || pFind->ftLastWriteTime.dwHighDateTime)
  1478.     {
  1479.         DWORD dwFlags = FDTF_LONGDATE | FDTF_RELATIVE | FDTF_LONGTIME;
  1480.         
  1481.         SHFormatDateTime(&pFind->ftLastWriteTime, &dwFlags, szTmp, SIZECHARS(szTmp));
  1482.         
  1483.         LoadString(HINST_THISDLL, IDS_DATESIZELINE, szTemplate, ARRAYSIZE(szTemplate));
  1484.         wsprintf(pszDateLine, szTemplate, StrFormatByteSize64(liFileSize.QuadPart, szNum, ARRAYSIZE(szNum)),
  1485.             szTmp);
  1486.     }
  1487.     else
  1488.     {
  1489.         // Simpy output the number to the string
  1490.         StrFormatByteSize64(liFileSize.QuadPart, pszDateLine, 64);
  1491.         if (liFileSize.QuadPart == 0)
  1492.             return FALSE;
  1493.     }
  1494.     return TRUE;    // valid data in the strings
  1495. }
  1496. // hide the cancel button and move "Yes" and "No" over to the right positions.
  1497. //
  1498. // "Yes" is IDYES
  1499. // "No"  is IDNO
  1500. //
  1501. #define HideYesToAllAndCancel(hdlg) HideConfirmButtons(hdlg, IDCANCEL)
  1502. #define HideYesToAllAndNo(hdlg) HideConfirmButtons(hdlg, IDNO)
  1503. void HideConfirmButtons(HWND hdlg, int idHide)
  1504. {
  1505.     HWND hwndCancel = GetDlgItem(hdlg, IDCANCEL);
  1506.     HWND hwndYesToAll = GetDlgItem(hdlg, IDD_YESTOALL);
  1507.     if (hwndCancel) {
  1508.         RECT rcCancel;
  1509.         HWND hwndNo;
  1510.         GetWindowRect(hwndCancel, &rcCancel);
  1511.         
  1512.         hwndNo = GetDlgItem(hdlg, IDNO);
  1513.         if (hwndNo) {
  1514.             RECT rcNo;
  1515.             HWND hwndYes;
  1516.             
  1517.             GetWindowRect(hwndNo, &rcNo);
  1518.             
  1519.             MapWindowRect(NULL, hdlg, &rcCancel);
  1520.             
  1521.             SetWindowPos(hwndNo, NULL, rcCancel.left, rcCancel.top,
  1522.                 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
  1523.             
  1524.             hwndYes = GetDlgItem(hdlg, IDYES);
  1525.             if (hwndYes) {
  1526.                 MapWindowRect(NULL, hdlg, &rcNo);
  1527.                 
  1528.                 SetWindowPos(hwndYes, NULL, rcNo.left, rcNo.top,
  1529.                     0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
  1530.             }
  1531.         }
  1532.         
  1533.         // Although the function is called "Hide", we actually destroy
  1534.         // the windows, because keyboard accelerators for hidden windows
  1535.         // are still active!
  1536.         if (hwndYesToAll)
  1537.             DestroyWindow(hwndYesToAll);
  1538.         DestroyWindow( GetDlgItem(hdlg, idHide));
  1539.     }
  1540. }
  1541. int MoveDlgItem(HWND hDlg, UINT id, int y)
  1542. {
  1543.     RECT rc;
  1544.     HWND hwnd = GetDlgItem(hDlg, id);
  1545.     if (hwnd) {
  1546.         GetWindowRect(hwnd, &rc);
  1547.         MapWindowRect(NULL, hDlg, &rc);
  1548.         SetWindowPos(hwnd, NULL, rc.left, y, 0,0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  1549.         return rc.top - y; // return how much it moved
  1550.     }
  1551.     return 0;
  1552. }
  1553. void ShrinkDialog(HWND hDlg, UINT idText)
  1554. {
  1555.     RECT rc;
  1556.     int y;
  1557.     HWND hwnd;
  1558.     hwnd = GetDlgItem(hDlg, idText);
  1559.     ASSERT(hwnd);
  1560.     GetWindowRect(hwnd, &rc);
  1561.     MapWindowRect(NULL, hDlg, &rc);
  1562.     y = rc.bottom + 12;
  1563.     
  1564.     // move all the buttons
  1565.     MoveDlgItem(hDlg, IDNO, y);
  1566.     MoveDlgItem(hDlg, IDCANCEL, y);
  1567.     MoveDlgItem(hDlg, IDD_YESTOALL, y);
  1568.     y = MoveDlgItem(hDlg, IDYES, y);
  1569.     
  1570.     // now resize the entire dialog
  1571.     GetWindowRect(hDlg, &rc);
  1572.     SetWindowPos(hDlg, NULL, 0, 0, rc.right - rc.left, rc.bottom - y - rc.top, SWP_NOMOVE | SWP_NOZORDER |SWP_NOACTIVATE);
  1573. }
  1574. void InitConfirmDlg(HWND hDlg, CONFDLG_DATA *pcd)
  1575. {
  1576.     TCHAR szMessage[255];
  1577.     TCHAR szDeleteWarning[80];
  1578.     TCHAR szSrc[32];
  1579.     TCHAR szFriendlyName[MAX_PATH];
  1580.     SHFILEINFO  sfi;
  1581.     SHFILEINFO sfiDest;
  1582.     LPTSTR pszFileDest = NULL;
  1583.     LPTSTR pszMsg, pszSource;
  1584.     int i;
  1585.     int cxWidth;
  1586.     RECT rc;
  1587.     BOOL bIsARPWarning = pcd->bARPWarning;
  1588.     ASSERT((bIsARPWarning && (pcd->nSourceFiles == 1)) || (!bIsARPWarning));
  1589.     
  1590.     // get the size of the text boxes
  1591.     GetWindowRect(GetDlgItem(hDlg, pcd->idText), &rc);
  1592.     cxWidth = rc.right - rc.left;
  1593.     if (!bIsARPWarning && !pcd->bShowCancel)
  1594.         HideYesToAllAndCancel(hDlg);
  1595.     
  1596.     switch (pcd->nSourceFiles) 
  1597.     {
  1598.     case -1:
  1599.         LoadString(HINST_THISDLL, IDS_SELECTEDFILES, szSrc, ARRAYSIZE(szSrc));
  1600.         pszSource = szSrc;
  1601.         break;
  1602.         
  1603.     case 1:
  1604.         if (bIsARPWarning)
  1605.         {
  1606.             TCHAR szTarget[MAX_PATH];
  1607.             DWORD cchFriendlyName = ARRAYSIZE(szFriendlyName);
  1608.             HRESULT hres = GetPathFromLinkFile(pcd->pFileSource, szTarget, ARRAYSIZE(szTarget));
  1609.             if (S_OK == hres)
  1610.             {
  1611.                 if (SUCCEEDED(AssocQueryString(ASSOCF_VERIFY | ASSOCF_OPEN_BYEXENAME, ASSOCSTR_FRIENDLYAPPNAME,
  1612.                     szTarget, NULL, szFriendlyName, &cchFriendlyName)))
  1613.                 {
  1614.                     pszSource = szFriendlyName;
  1615.                 }
  1616.                 else
  1617.                 {
  1618.                     pszSource = PathFindFileName(szTarget);
  1619.                 }
  1620.             }
  1621.             else if (S_FALSE == hres)
  1622.             {
  1623.                 TCHAR szProductCode[MAX_PATH];
  1624.                 szProductCode[0] = TEXT('');
  1625.                 if ((ERROR_SUCCESS == MsiDecomposeDescriptor(szTarget, szProductCode, NULL, NULL, NULL)) && 
  1626.                     (ERROR_SUCCESS == MsiGetProductInfo(szProductCode, INSTALLPROPERTY_PRODUCTNAME, szFriendlyName, &cchFriendlyName)))
  1627.                 {
  1628.                     pszSource = szFriendlyName;
  1629.                 }
  1630.                 else
  1631.                     goto UNKNOWNAPP;
  1632.                 
  1633.             }
  1634.             else
  1635.             {
  1636. UNKNOWNAPP:
  1637.                 LoadString(HINST_THISDLL, IDS_UNKNOWNAPPLICATION, szSrc, ARRAYSIZE(szSrc));
  1638.                 pszSource = szSrc;
  1639.             }
  1640.             Static_SetIcon(GetDlgItem(hDlg, IDD_ARPINFORMATION), 
  1641.                            LoadIcon(NULL, MAKEINTRESOURCE(IDI_INFORMATION)));
  1642.         }
  1643.         else
  1644.         {
  1645.             SHGetFileInfo(pcd->pFileSource,
  1646.                           (pcd->fConfirm==CONFIRM_DELETE_FOLDER || pcd->fConfirm==CONFIRM_WONT_RECYCLE_FOLDER)? FILE_ATTRIBUTE_DIRECTORY : 0,
  1647.                           &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
  1648.             pszSource = sfi.szDisplayName;
  1649.             PathCompactPath(NULL, pszSource, cxWidth);
  1650.         }
  1651.         break;
  1652.     default:
  1653.         pszSource = AddCommas(pcd->nSourceFiles, szSrc);
  1654.         break;
  1655.     }
  1656.     
  1657.     // if we're supposed to show the date info, grab the icons and format the date string
  1658.     if (pcd->bShowDates) 
  1659.     {
  1660.         SHFILEINFO  sfi2;
  1661.         TCHAR szDateSrc[64], szDateDest[64];
  1662.         
  1663.         BuildDateLine(szDateSrc, pcd->pfdSource, pcd->pFileSource);
  1664.         SetDlgItemText(hDlg, IDD_FILEINFO_NEW,  szDateSrc);
  1665.         
  1666.         BuildDateLine(szDateDest, pcd->pfdDest, pcd->pFileDest);
  1667.         SetDlgItemText(hDlg, IDD_FILEINFO_OLD,  szDateDest);
  1668.         
  1669.         SHGetFileInfo(pcd->pFileDest, pcd->pfdDest ? pcd->pfdDest->dwFileAttributes : 0, &sfi2, SIZEOF(sfi2),
  1670.             pcd->pfdDest ? (SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_LARGEICON) : (SHGFI_ICON|SHGFI_LARGEICON));
  1671.         ReplaceDlgIcon(hDlg, IDD_ICON_OLD, sfi2.hIcon);
  1672.         
  1673.         SHGetFileInfo(pcd->pFileSource, pcd->pfdSource ? pcd->pfdSource->dwFileAttributes : 0, &sfi2, SIZEOF(sfi2),
  1674.             pcd->pfdSource ? (SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_LARGEICON) : (SHGFI_ICON|SHGFI_LARGEICON));
  1675.         ReplaceDlgIcon(hDlg, IDD_ICON_NEW, sfi2.hIcon);
  1676.     }
  1677.     if (!bIsARPWarning)
  1678.     {
  1679.         // there are multiple controls:
  1680.         // IDD_TEXT contains regular text (normal file/folder)
  1681.         // IDD_TEXT1 - IDD_TEXT4 contain optional secondary text
  1682.         for (i = IDD_TEXT; i <= IDD_TEXT4; i++) 
  1683.         {
  1684.             if (i == pcd->idText) 
  1685.             {
  1686.                 szMessage[0] = 0;
  1687.                 GetDlgItemText(hDlg, i, szMessage, ARRAYSIZE(szMessage));
  1688.             } 
  1689.             else 
  1690.             {
  1691.                 HWND hwndCtl = GetDlgItem(hDlg, i);
  1692.                 if (hwndCtl)
  1693.                     ShowWindow(hwndCtl, SW_HIDE);
  1694.             }
  1695.         }
  1696.     }
  1697.     else
  1698.         GetDlgItemText(hDlg, IDD_ARPWARNINGTEXT, szMessage, ARRAYSIZE(szMessage));
  1699.     
  1700.     // REVIEW Is there some better way?  The code above always hides
  1701.     // this control, and I don't see a way around this
  1702.     
  1703.     if (pcd->pStreamNames) 
  1704.     {
  1705.         SetDlgItemText(hDlg, IDD_TEXT1, pcd->pStreamNames);
  1706.         ShowWindow(GetDlgItem(hDlg, IDD_TEXT1), SW_SHOW);
  1707.     }
  1708.     
  1709.     if (pcd->bShrinkDialog)
  1710.         ShrinkDialog(hDlg, pcd->idText);
  1711.     
  1712.     if (pcd->pFileDest) 
  1713.     {
  1714.         SHGetFileInfo(pcd->pFileDest, 0,
  1715.             &sfiDest, SIZEOF(sfiDest), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES);
  1716.         pszFileDest = sfiDest.szDisplayName;
  1717.         PathCompactPath(NULL, pszFileDest, cxWidth);
  1718.     }
  1719.     
  1720.     if (pcd->uDeleteWarning) 
  1721.     {
  1722.         LPITEMIDLIST pidl;
  1723.         if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_BITBUCKET, &pidl)))
  1724.         {
  1725.             SHFILEINFO fi;
  1726.             if (SHGetFileInfo((LPCTSTR)pidl, 0, &fi, sizeof(fi), SHGFI_PIDL | SHGFI_ICON |SHGFI_LARGEICON))
  1727.             {
  1728.                 ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, fi.hIcon);
  1729.             }
  1730.             ILFree(pidl);
  1731.         }
  1732.         LoadString(HINST_THISDLL, pcd->uDeleteWarning, szDeleteWarning, ARRAYSIZE(szDeleteWarning));
  1733.     } 
  1734.     else
  1735.         szDeleteWarning[0] = 0;
  1736.     
  1737.     if (pcd->bFireIcon) 
  1738.     {
  1739.         ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, LoadImage(HINST_THISDLL, MAKEINTRESOURCE(IDI_NUKEFILE), IMAGE_ICON, 0, 0, LR_LOADMAP3DCOLORS));
  1740.     }
  1741.     
  1742.     pszMsg = ShellConstructMessageString(HINST_THISDLL, szMessage,
  1743.         pszSource, pszFileDest, szDeleteWarning);
  1744.     
  1745.     if (pszMsg) 
  1746.     {
  1747.         SetDlgItemText(hDlg, pcd->idText, pszMsg);
  1748.         LocalFree(pszMsg);
  1749.     }
  1750.     if (bIsARPWarning)
  1751.     {
  1752.         TCHAR szLinkWindow[MAX_PATH];
  1753.         GetDlgItemText(hDlg, IDD_ARPLINKWINDOW, szLinkWindow, ARRAYSIZE(szLinkWindow));
  1754.         pszMsg = ShellConstructMessageString(HINST_THISDLL, szLinkWindow, pszSource);
  1755.         if (pszMsg)
  1756.         {
  1757.             SetDlgItemText(hDlg, IDD_ARPLINKWINDOW, pszMsg);
  1758.             LocalFree(pszMsg);
  1759.         }
  1760.     }
  1761. }
  1762. BOOL_PTR CALLBACK ConfirmDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  1763. {
  1764.     CONFDLG_DATA *pcd = (CONFDLG_DATA *)GetWindowLongPtr(hDlg, DWLP_USER);
  1765.     
  1766.     switch (wMsg) {
  1767.     case WM_INITDIALOG:
  1768.         SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1769.         pcd = (CONFDLG_DATA *)lParam;
  1770.         pcd->InitConfirmDlg(hDlg, pcd);
  1771.         break;
  1772.         
  1773.     case WM_DESTROY:
  1774.         // Handle case where the allocation of the PCD failed.
  1775.         if (!pcd)
  1776.             break;
  1777.         
  1778.         if (pcd->bShowDates) 
  1779.         {
  1780.             ReplaceDlgIcon(hDlg, IDD_ICON_NEW, NULL);
  1781.             ReplaceDlgIcon(hDlg, IDD_ICON_OLD, NULL);
  1782.         }
  1783.         if (pcd->bARPWarning)
  1784.         {
  1785.             ReplaceDlgIcon(hDlg, IDD_ARPINFORMATION, NULL);
  1786.         }
  1787.         
  1788.         ReplaceDlgIcon(hDlg, IDD_ICON_WASTEBASKET, NULL);
  1789.         break;
  1790.         
  1791.     case WM_COMMAND:
  1792.         if (!pcd)
  1793.             break;
  1794.         
  1795.         switch (GET_WM_COMMAND_ID(wParam, lParam)) 
  1796.         {
  1797.         case IDNO:
  1798.             if (GetKeyState(VK_SHIFT) < 0)      // force NOTOALL
  1799.             {
  1800.                 // I use the fYesToAllMask here.  There used to be a fNoToAllMask but I
  1801.                 // removed it.  When you select "No To All" what you are saying is that
  1802.                 // anything I would be saying yes to all for I am actually saying "no to
  1803.                 // all" for.  I feel that it is confusing and unnecessary to have both.
  1804.                 pcd->pcd->fNoToAll |= pcd->fYesToAllMask;
  1805.             }
  1806.             EndDialog(hDlg, IDNO);
  1807.             break;
  1808.             
  1809.         case IDD_YESTOALL:
  1810.             // pcd is the confirmation data for just this file/folder.  pcd->pcd is the
  1811.             // confirm data for the entire copy operation.  When we get a Yes To All we
  1812.             // remove the coresponding bits from the entire operation.
  1813.             pcd->pcd->fConfirm &= ~pcd->fYesToAllMask;
  1814.             EndDialog(hDlg, IDYES);
  1815.             break;
  1816.             
  1817.         case IDYES:
  1818.             // There are some messages that we only want to tell the use once even if they
  1819.             // select Yes instead of Yes To All.  As such we sometimes remove bits from the
  1820.             // global confirm state even on a simple Yes.  This mask is usually zero.
  1821.             pcd->pcd->fConfirm &= ~pcd->fYesMask;
  1822.             EndDialog(hDlg, IDYES);
  1823.             break;
  1824.             
  1825.         case IDCANCEL:
  1826.             EndDialog(hDlg, IDCANCEL);
  1827.             break;
  1828.         }
  1829.         break;
  1830.     case WM_NOTIFY:
  1831.         switch (((LPNMHDR)lParam)->code)
  1832.         {
  1833.             case NM_RETURN:
  1834.             case NM_CLICK:
  1835.             {
  1836.                 TCHAR szModule[MAX_PATH];
  1837.                 if (GetSystemDirectory(szModule, ARRAYSIZE(szModule)))
  1838.                 {
  1839.                     if (PathAppend(szModule, TEXT("appwiz.cpl")))
  1840.                     {
  1841.                         TCHAR szParam[1 + MAX_PATH + 2 + MAX_CCH_CPLNAME]; // See MakeCPLCommandLine function
  1842.                         TCHAR szAppwiz[64];
  1843.                         LoadString(g_hinst, IDS_APPWIZCPL, szAppwiz, SIZECHARS(szAppwiz));
  1844.                         MakeCPLCommandLine(szModule, szAppwiz, szParam, ARRAYSIZE(szParam));
  1845.                         SHRunControlPanelEx(szParam, NULL, FALSE);
  1846.                     }
  1847.                 }
  1848.                 EndDialog(hDlg, IDNO);
  1849.             }
  1850.             break;
  1851.         }
  1852.         break;
  1853.     default:
  1854.         return FALSE;
  1855.     }
  1856.     return TRUE;
  1857. }
  1858. BOOL IgnoreSysAndRO(LPCTSTR lpszFileName)
  1859. {
  1860.     TCHAR szIniFile[MAX_PATH];
  1861.     
  1862.     //
  1863.     // if there's a desktop.ini with a .ShellClassInfo ConfirmFileOp=0
  1864.     // then we can ignore the SYS and RO attributes of a file
  1865.     //
  1866.     if (lpszFileName && (lstrlen(lpszFileName) + lstrlen(c_szDesktopIni) + 2 < MAX_PATH))
  1867.     {
  1868.         lstrcpy(szIniFile, lpszFileName);
  1869.         PathAppend(szIniFile, TEXT("desktop.ini"));
  1870.         return GetPrivateProfileInt(STRINI_CLASSINFO, TEXT("ConfirmFileOp"), TRUE, szIniFile) == 0;
  1871.     }
  1872.     
  1873.     return FALSE;
  1874. }
  1875. #define FILE_ATTRIBUTE_SUPERHIDDEN (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN) 
  1876. #define IS_SYSTEM_HIDDEN(dw) ((dw & FILE_ATTRIBUTE_SUPERHIDDEN) == FILE_ATTRIBUTE_SUPERHIDDEN) 
  1877. void SetConfirmMaskAndText(CONFDLG_DATA *pcd, DWORD dwFileAttributes, LPCTSTR pszFile)
  1878. {
  1879.     if (IS_SYSTEM_HIDDEN(dwFileAttributes) && !ShowSuperHidden())
  1880.     {
  1881.         dwFileAttributes &= ~FILE_ATTRIBUTE_SUPERHIDDEN;
  1882.     }
  1883.     
  1884.     // We only need to do this if this is a directory as we are going to append on
  1885.     // desktop.ini onto the path and do GetPrivatePfofileInt calls which does not
  1886.     // make sense if we pass in something like C:foo.exe
  1887.     // Probably a minor perf win.
  1888.     if ((dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) &&
  1889.         (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  1890.         (IgnoreSysAndRO(pszFile)))
  1891.     {
  1892.         dwFileAttributes &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY);
  1893.     }
  1894.     
  1895.     if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
  1896.     {
  1897.         pcd->fConfirm = CONFIRM_SYSTEM_FILE;
  1898.         pcd->fYesToAllMask |= CONFIRM_SYSTEM_FILE;
  1899.         pcd->idText = IDD_TEXT2;
  1900.     }
  1901.     else if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  1902.     {
  1903.         pcd->fConfirm = CONFIRM_READONLY_FILE;
  1904.         pcd->fYesToAllMask |= CONFIRM_READONLY_FILE;
  1905.         pcd->idText = IDD_TEXT1;
  1906.     }
  1907.     else if (pszFile && ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) &&
  1908.         PathIsRegisteredProgram(pszFile))
  1909.     {
  1910.         pcd->fConfirm = CONFIRM_PROGRAM_FILE;
  1911.         pcd->fYesToAllMask |= CONFIRM_PROGRAM_FILE;
  1912.         pcd->idText = IDD_TEXT3;
  1913.     }
  1914. }
  1915. void PauseAnimation(COPY_STATE *pcs, BOOL bStop)
  1916. {
  1917.     // only called from within the hwndProgress wndproc so assum it's there
  1918.     if (bStop)
  1919.         Animate_Stop(GetDlgItem(pcs->hwndProgress, IDD_ANIMATE));
  1920.     else
  1921.         Animate_Play(GetDlgItem(pcs->hwndProgress, IDD_ANIMATE), -1, -1, -1);
  1922. }
  1923. // confirm a file operation UI.
  1924. //
  1925. // this routine uses the CONFIRM_DATA in the copy state structure to
  1926. // decide if it needs to put up a dailog to confirm the given file operation.
  1927. //
  1928. // in:
  1929. //    pcs           current copy state (confirm flags, hwnd)
  1930. //    fConfirm      only one bit may be set! (operation to confirm)
  1931. //    pFileSource   source file
  1932. //    pFileDest     optional destination file
  1933. //    pfdSource
  1934. //    pfdDest       find data describing the destination
  1935. //
  1936. // returns:
  1937. //      IDYES
  1938. //      IDNO
  1939. //      IDCANCEL
  1940. //      ERROR_ (DE_) error codes (DE_MEMORY)
  1941. //
  1942. int ConfirmFileOp(HWND hwnd, COPY_STATE *pcs, CONFIRM_DATA *pcd,
  1943.                       int nSourceFiles, int cDepth, CONFIRM_FLAG fConfirm,
  1944.                       LPCTSTR pFileSource, const WIN32_FIND_DATA *pfdSource,
  1945.                       LPCTSTR pFileDest,   const WIN32_FIND_DATA *pfdDest,
  1946.                       LPCTSTR pStreamNames)
  1947. {
  1948.     int dlg;
  1949.     int ret;
  1950.     CONFDLG_DATA cdd;
  1951.     CONFIRM_FLAG fConfirmType;
  1952.     
  1953.     if (pcs)
  1954.         nSourceFiles = pcs->nSourceFiles;
  1955.     
  1956.     cdd.pfdSource = pfdSource;
  1957.     cdd.pfdDest = NULL; // pfdDest // BUGBUG: pfdDest is only partially filed in
  1958.     cdd.pFileSource = pFileSource;
  1959.     cdd.pFileDest = pFileDest;
  1960.     cdd.pcd = pcd;
  1961.     cdd.fConfirm      = fConfirm;       // default, changed below
  1962.     cdd.fYesMask      = 0;
  1963.     cdd.fYesToAllMask = 0;
  1964.     cdd.nSourceFiles = 1;               // default to individual file names in message
  1965.     cdd.idText = IDD_TEXT;              // default string from the dlg template
  1966.     cdd.bShowCancel = ((nSourceFiles != 1) || cDepth);
  1967.     cdd.uDeleteWarning = 0;
  1968.     cdd.bFireIcon = FALSE;
  1969.     cdd.bShowDates = FALSE;
  1970.     cdd.bShrinkDialog = FALSE;
  1971.     cdd.InitConfirmDlg = InitConfirmDlg;
  1972.     cdd.pStreamNames   = NULL;
  1973.     cdd.bARPWarning    = FALSE;
  1974.     
  1975.     fConfirmType = fConfirm & CONFIRM_FLAG_TYPE_MASK;
  1976.     
  1977.     switch (fConfirmType)
  1978.     {
  1979.         case CONFIRM_DELETE_FILE:
  1980.         case CONFIRM_DELETE_FOLDER:
  1981.         {
  1982.             BOOL bIsFolderShortcut = FALSE;
  1983.             cdd.bShrinkDialog = TRUE;
  1984.             // find data for source is in pdfDest
  1985.             if ((nSourceFiles != 1) && (pcd->fConfirm & CONFIRM_MULTIPLE))
  1986.             {
  1987.                 // this is the special CONFIRM_MULTIPLE case (usuall SHIFT+DELETE, or
  1988.                 // SHIFT+DRAG to Recycle Bin). if the user says yes to this, they 
  1989.                 // basically get no more warnings.
  1990.                 cdd.nSourceFiles = nSourceFiles;
  1991.                 if ((fConfirm & CONFIRM_WASTEBASKET_PURGE) ||
  1992.                     (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO)) ||
  1993.                     !BBWillRecycle(cdd.pFileSource, NULL))
  1994.                 {
  1995.                     // have the fire icon and the REALLY delete warning
  1996.                     cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
  1997.                     cdd.bFireIcon = TRUE;
  1998.                     if (pcs)
  1999.                         pcs->fFlags &= ~FOF_ALLOWUNDO;
  2000.                 
  2001.                     if (nSourceFiles == -1)
  2002.                     {
  2003.                         // -1 indicates that there were > MAX_EMPTY_FILES files, so we stoped counting
  2004.                         // them all up for perf. We use the more generic message in this case.
  2005.                         cdd.idText = IDD_TEXT3;
  2006.                     }
  2007.                     else
  2008.                     {
  2009.                         // use the "are you sure you want to nuke XX files?" message
  2010.                         cdd.idText = IDD_TEXT4;
  2011.                     }
  2012.                 }
  2013.                 else
  2014.                 {
  2015.                     // uDeleteWarning must be set for the proper recycle icon to be loaded.
  2016.                     cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
  2017.                 }
  2018.                 if (!pcs || !pcs->fNoConfirmRecycle)
  2019.                 {
  2020.                     ret = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DELETE_MULTIPLE), hwnd, ConfirmDlgProc, (LPARAM)&cdd);
  2021.                 
  2022.                     if (ret != IDYES)
  2023.                         return IDCANCEL;
  2024.                 }
  2025.             
  2026.                 // clear all other possible warnings
  2027.                 pcd->fConfirm &= ~(CONFIRM_MULTIPLE | CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER);
  2028.                 cdd.fConfirm &= ~(CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER);
  2029.                 cdd.nSourceFiles = 1;       // use individual file name
  2030.             }
  2031.     
  2032.             SetConfirmMaskAndText(&cdd, pfdDest->dwFileAttributes, cdd.pFileSource);
  2033.             if ((pfdDest->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && PathIsShortcut(cdd.pFileSource))
  2034.             {
  2035.                 // Its a folder and its a shortcut... must be a FolderShortcut!
  2036.                 bIsFolderShortcut = TRUE;
  2037.                 // since its a folder, we need to clear out all of these warnings
  2038.                 cdd.fYesMask      |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
  2039.                 cdd.fYesToAllMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
  2040.             }
  2041.                 
  2042.             // we want to treat FolderShortcuts as "files" instead of folders. We do this so we don't display dialogs
  2043.             // that say stuff like "do you want to delete this and all of its contents" when to the user, this looks like
  2044.             // an item instead of a folder (eg nethood shortcut).
  2045.             if ((fConfirmType == CONFIRM_DELETE_FILE) || bIsFolderShortcut)
  2046.             {
  2047.                 dlg = DLG_DELETE_FILE;
  2048.                 if ((nSourceFiles == 1) && PathIsShortcutToProgram(cdd.pFileSource))
  2049.                 {
  2050.                     LinkWindow_RegisterClass();
  2051.                     dlg = DLG_DELETE_FILE_ARP;
  2052.                     cdd.idText = IDD_ARPWARNINGTEXT;
  2053.                     cdd.bShrinkDialog = FALSE;
  2054.                     cdd.bARPWarning = TRUE;
  2055.                 }
  2056.           
  2057.                 if ((fConfirm & CONFIRM_WASTEBASKET_PURGE)      ||
  2058.                     (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO))    ||
  2059.                     !BBWillRecycle(cdd.pFileSource, NULL))
  2060.                 {
  2061.                     // we are really nuking it, so show the appropriate icon/dialog
  2062.                     cdd.bFireIcon = TRUE;
  2063.                     if (pcs)
  2064.                     {
  2065.                         pcs->fFlags &= ~FOF_ALLOWUNDO;
  2066.                     }
  2067.                 
  2068.                     cdd.uDeleteWarning = IDS_FILEDELETEWARNING;
  2069.                     if (cdd.idText == IDD_TEXT)
  2070.                     {
  2071.                         cdd.idText = IDD_TEXT4;
  2072.                     }
  2073.                 }
  2074.                 else
  2075.                 {
  2076.                     // we are recycling it
  2077.                     cdd.uDeleteWarning = IDS_FILERECYCLEWARNING;
  2078.                 }
  2079.             
  2080.             }
  2081.             else
  2082.             {
  2083.                 // fConfirmType == CONFIRM_DELETE_FOLDER
  2084.                 if (pcs)
  2085.                 {
  2086.                     // show cancel on NEXT confirm dialog
  2087.                     pcs->nSourceFiles = -1;
  2088.                 }
  2089.             
  2090.                 cdd.fYesMask      |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
  2091.                 cdd.fYesToAllMask |= CONFIRM_DELETE_FILE | CONFIRM_DELETE_FOLDER | CONFIRM_MULTIPLE;
  2092.                 dlg = DLG_DELETE_FOLDER;
  2093.             
  2094.                 if ((fConfirm & CONFIRM_WASTEBASKET_PURGE)      ||
  2095.                     (!pcs || !(pcs->fFlags & FOF_ALLOWUNDO))    ||
  2096.                     !BBWillRecycle(cdd.pFileSource, NULL))
  2097.                 {
  2098.                     // we are really nuking it, so show the appropriate icon/dialog
  2099.                     cdd.bFireIcon = TRUE;
  2100.                     if (pcs)
  2101.                     {
  2102.                         pcs->fFlags &= ~FOF_ALLOWUNDO;
  2103.                     }
  2104.                 
  2105.                     cdd.uDeleteWarning = IDS_FOLDERDELETEWARNING;
  2106.                 }
  2107.                 else
  2108.                 {
  2109.                     // we are recycling it
  2110.                     cdd.uDeleteWarning = IDS_FOLDERRECYCLEWARNING;
  2111.                 }
  2112.             }
  2113.         
  2114.             if (pcs && pcs->fNoConfirmRecycle)
  2115.             {
  2116.                 cdd.fConfirm = 0;
  2117.             }
  2118.         }
  2119.         break;
  2120.         
  2121.         case CONFIRM_WONT_RECYCLE_FILE:
  2122.         case CONFIRM_WONT_RECYCLE_FOLDER:
  2123.             cdd.bShrinkDialog = TRUE;
  2124.             cdd.nSourceFiles = 1;
  2125.             cdd.bFireIcon = TRUE;
  2126.             cdd.idText = IDD_TEXT;
  2127.             cdd.fYesMask = CONFIRM_MULTIPLE;
  2128.             cdd.fConfirm = fConfirmType;
  2129.             cdd.fYesToAllMask = fConfirmType | CONFIRM_MULTIPLE;
  2130.             
  2131.             // set the dialog to be file or folder
  2132.             if (fConfirmType == CONFIRM_WONT_RECYCLE_FOLDER)
  2133.             {
  2134.                 dlg = DLG_WONT_RECYCLE_FOLDER;
  2135.             }
  2136.             else
  2137.             {
  2138.                 dlg = DLG_WONT_RECYCLE_FILE;
  2139.             }
  2140.             break;
  2141.         case CONFIRM_PATH_TOO_LONG:
  2142.             cdd.bShrinkDialog = TRUE;
  2143.             cdd.nSourceFiles = 1;
  2144.             cdd.bFireIcon = TRUE;
  2145.             cdd.idText = IDD_TEXT;
  2146.             cdd.fYesMask = CONFIRM_MULTIPLE;
  2147.             cdd.fConfirm = CONFIRM_PATH_TOO_LONG;
  2148.             cdd.fYesToAllMask = CONFIRM_PATH_TOO_LONG | CONFIRM_MULTIPLE;
  2149.             dlg = DLG_PATH_TOO_LONG;
  2150.             break;
  2151.             
  2152.         case CONFIRM_WONT_RECYCLE_OFFLINE:
  2153.             cdd.bShrinkDialog = TRUE;
  2154.             cdd.nSourceFiles = 1;
  2155.             cdd.bFireIcon = TRUE;
  2156.             cdd.idText = IDD_TEXT;
  2157.             cdd.fYesMask = CONFIRM_MULTIPLE;
  2158.             cdd.fConfirm = fConfirmType;
  2159.             cdd.fYesToAllMask = fConfirmType | CONFIRM_MULTIPLE;
  2160.             dlg = DLG_WONT_RECYCLE_OFFLINE;
  2161.             break;
  2162.             
  2163.         case CONFIRM_STREAMLOSS:
  2164.             cdd.bShrinkDialog = FALSE;
  2165.             cdd.nSourceFiles  = 1;
  2166.             cdd.idText        = IDD_TEXT;
  2167.             cdd.fConfirm      = CONFIRM_STREAMLOSS;
  2168.             cdd.fYesToAllMask = CONFIRM_STREAMLOSS;
  2169.             cdd.pStreamNames  = pStreamNames;
  2170.             dlg = DLG_STREAMLOSS_ON_COPY;
  2171.             break;
  2172.         case CONFIRM_FAILED_ENCRYPT:
  2173.             cdd.bShrinkDialog = FALSE;
  2174.             cdd.nSourceFiles = nSourceFiles;
  2175.             cdd.idText = IDD_TEXT;
  2176.             cdd.bShowCancel = TRUE;
  2177.             cdd.fConfirm = CONFIRM_FAILED_ENCRYPT;
  2178.             cdd.fYesToAllMask = CONFIRM_FAILED_ENCRYPT;
  2179.             dlg = DLG_FAILED_ENCRYPT;
  2180.             break;
  2181.         
  2182.         case CONFIRM_REPLACE_FILE:
  2183.             cdd.bShowDates = TRUE;
  2184.             cdd.fYesToAllMask = CONFIRM_REPLACE_FILE;
  2185.             SetConfirmMaskAndText(&cdd, pfdDest->dwFileAttributes, NULL);
  2186.             dlg = DLG_REPLACE_FILE;
  2187.             break;
  2188.         
  2189.         case CONFIRM_REPLACE_FOLDER:
  2190.             cdd.bShowCancel = TRUE;
  2191.             if (pcs) pcs->nSourceFiles = -1;        // show cancel on NEXT confirm dialog
  2192.             // this implies operations on the files
  2193.             cdd.fYesMask = CONFIRM_REPLACE_FILE;
  2194.             cdd.fYesToAllMask = CONFIRM_REPLACE_FILE | CONFIRM_REPLACE_FOLDER;
  2195.             dlg = DLG_REPLACE_FOLDER;
  2196.             break;
  2197.         
  2198.         case CONFIRM_MOVE_FILE:
  2199.             cdd.fYesToAllMask = CONFIRM_MOVE_FILE;
  2200.             SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, NULL);
  2201.             dlg = DLG_MOVE_FILE;
  2202.             break;
  2203.         
  2204.         case CONFIRM_MOVE_FOLDER:
  2205.             cdd.bShowCancel = TRUE;
  2206.             cdd.fYesToAllMask = CONFIRM_MOVE_FOLDER;
  2207.             SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, cdd.pFileSource);
  2208.             dlg = DLG_MOVE_FOLDER;
  2209.             break;
  2210.         
  2211.         case CONFIRM_RENAME_FILE:
  2212.             SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, NULL);
  2213.             dlg = DLG_RENAME_FILE;
  2214.             break;
  2215.         
  2216.         case CONFIRM_RENAME_FOLDER:
  2217.             cdd.bShowCancel = TRUE;
  2218.             if (pcs) pcs->nSourceFiles = -1;        // show cancel on NEXT confirm dialog
  2219.             SetConfirmMaskAndText(&cdd, pfdSource->dwFileAttributes, cdd.pFileSource);
  2220.             dlg = DLG_RENAME_FOLDER;
  2221.             break;
  2222.         
  2223.         default:
  2224.             DebugMsg(DM_WARNING, TEXT("bogus confirm option"));
  2225.             return IDCANCEL;
  2226.     }
  2227.     
  2228.     // Does this operation need to be confirmed?
  2229.     if (pcd->fConfirm & cdd.fConfirm)
  2230.     {
  2231.         // Has the user already said "No To All" for this operation?
  2232.         if ((pcd->fNoToAll & cdd.fConfirm) == cdd.fConfirm)
  2233.         {
  2234.             ret = IDNO;
  2235.         }
  2236.         else
  2237.         {
  2238.             // HACK for multimon, make sure the file operation dialog box comes
  2239.             // up on the correct monitor
  2240.             POINT ptInvoke;
  2241.             HWND hwndPos = NULL;
  2242.             if ((GetNumberOfMonitors() > 1) && GetCursorPos(&ptInvoke))
  2243.             {
  2244.                 HMONITOR hMon = MonitorFromPoint(ptInvoke, MONITOR_DEFAULTTONULL);
  2245.                 if (hMon)
  2246.                 {
  2247.                     hwndPos = _CreateStubWindow(&ptInvoke, hwnd);
  2248.                 }
  2249.             }
  2250.             ret = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(dlg), (hwndPos ? hwndPos : hwnd), ConfirmDlgProc, (LPARAM)&cdd);
  2251.             
  2252.             if (hwndPos)
  2253.                 DestroyWindow(hwndPos);
  2254.             
  2255.             if (ret == -1)
  2256.                 ret = DE_INSMEM;
  2257.         }
  2258.     }
  2259.     else
  2260.     {
  2261.         ret = IDYES;
  2262.     }
  2263.     
  2264.     return ret;
  2265. }
  2266. //
  2267. //  DTNIsParentConnectOrigin()
  2268. //
  2269. //      When a folder ("c:foo files") is moved to a different drive ("a:"), the source and the
  2270. //  destinations have different roots, and therefore the "fRecursive" flag is turned ON by default.
  2271. //  This results in confirmations obtained for the individual files ("c:foo filesaaa.gif") 
  2272. // rather than the folder itself. We need to first find the parent and then save the confirmation 
  2273. // in the connected element of it's parent. This function gets the top-most parent and then
  2274. // checks to see if it is a connect origin and if so returns that parent pointer.
  2275. //
  2276. PDIRTREENODE DTNGetConnectOrigin(PDIRTREENODE pdtn)
  2277. {
  2278.     PDIRTREENODE    pdtnParent = pdtn;
  2279.     
  2280.     //Get the top-level parent of the given node.
  2281.     while(pdtn)
  2282.     {
  2283.         pdtnParent = pdtn;
  2284.         pdtn = pdtn->pdtnParent;
  2285.     }
  2286.     
  2287.     //Now check if the parent is a connect origin.
  2288.     if (pdtnParent && DTNIsConnectOrigin(pdtnParent))
  2289.         return pdtnParent; //If so, return him.
  2290.     else
  2291.         return NULL;
  2292. }
  2293. //
  2294. // CachedConfirmFileOp()
  2295. //
  2296. //    When a file("foo.htm") is moved/copied, we may put up a confirmation dialog in case 
  2297. // of a conflict and the end-user might have responded saying "Yes", "no" etc., When the 
  2298. // corresponding connected element ("foo files") is also moved/copied etc., we should NOT put up
  2299. // a confirmation dialog again. We must simply store the answer to the original confirmation and
  2300. // use it later. 
  2301. //  
  2302. //  What this function does is: if the given node is a connected element, it simply retrieves the
  2303. // confirmation for the original operation and returns.  If the given element is NOT a connected 
  2304. // element, then this function calls the ConfirmFileOp and stores the confirmation result in 
  2305. // it's connected element sothat, it later it can be used by the connected element.
  2306. //
  2307. int CachedConfirmFileOp(HWND hwnd, COPY_STATE *pcs, CONFIRM_DATA *pcd,
  2308.                         int nSourceFiles, int cDepth, CONFIRM_FLAG fConfirm,
  2309.                         LPCTSTR pFileSource, const WIN32_FIND_DATA *pfdSource,
  2310.                         LPCTSTR pFileDest,   const WIN32_FIND_DATA *pfdDest,
  2311.                         LPCTSTR pStreamNames)
  2312.                         
  2313. {
  2314.     int result;
  2315.     
  2316.     //See if this is a connected item.
  2317.     if (DTNIsConnected(pcs->dth.pdtnCurrent))
  2318.     {
  2319.         // Since this is a connected item, the confirmation must already have been obtained from
  2320.         // the user and get it from the cache!
  2321.         result = DTNGetConfirmationResult(pcs->dth.pdtnCurrent);
  2322.     }
  2323.     else
  2324.     {
  2325.         PDIRTREENODE    pdtnConnectOrigin;
  2326.         
  2327.         result = ConfirmFileOp(hwnd, pcs, pcd, nSourceFiles, cDepth, fConfirm, pFileSource, 
  2328.             pfdSource, pFileDest, pfdDest, pStreamNames);
  2329.         
  2330.         //Check if this node has a connection.
  2331.         if (pdtnConnectOrigin = DTNGetConnectOrigin(pcs->dth.pdtnCurrent))
  2332.         {
  2333.             pdtnConnectOrigin->pdtnConnected->ConnectedInfo.dwConfirmation = result;
  2334.             
  2335.             // BUGBUG: Can we check for the result to be IDCANCEL or IDNO and if so make the
  2336.             // connected node a Dummy? Currently this won't work because current code assumes
  2337.             // that dummy nodes do not have children. This connected node might have some children.
  2338.             // if ((result == IDCANCEL) || (result == IDNO))
  2339.             //    pdtnConnectOrigin->pdtnConnected->fDummy = TRUE;
  2340.         }
  2341.         
  2342.     }
  2343.     
  2344.     return result;
  2345. }
  2346. void GuessAShortName(LPCTSTR p, LPTSTR szT)
  2347. {
  2348.     int i, j, fDot, cMax;
  2349.     // BUGBUG: use AnsiNext here?
  2350.     for (i = j = fDot = 0, cMax = 8; *p; p++) {
  2351.         if (*p == TEXT('.')) {
  2352.             // if there was a previous dot, step back to it
  2353.             // this way, we get the last extension
  2354.             if (fDot)
  2355.                 i -= j+1;
  2356.             
  2357.             // set number of chars to 0, put the dot in
  2358.             j = 0;
  2359.             szT[i++] = TEXT('.');
  2360.             
  2361.             // remember we saw a dot and set max 3 chars.
  2362.             fDot = TRUE;
  2363.             cMax = 3;
  2364.         } else if (j < cMax && (PathGetCharType(*p) & GCT_SHORTCHAR)) {
  2365.             // if *p is a lead byte, we move forward one more
  2366.             if (IsDBCSLeadByte(*p)) {
  2367.                 szT[i] = *p++;
  2368.                 if (++j >= cMax)
  2369.                     continue;
  2370.                 ++i;
  2371.             }
  2372.             j++;
  2373.             szT[i++] = *p;
  2374.         }
  2375.     }
  2376.     szT[i] = 0;
  2377. }
  2378. /* GetNameDialog
  2379. *
  2380. *  Runs the dialog box to prompt the user for a new filename when copying
  2381. *  or moving from HPFS to FAT.
  2382. */
  2383. typedef struct {
  2384.     LPTSTR pszDialogFrom;
  2385.     LPTSTR pszDialogTo;
  2386.     BOOL bShowCancel;
  2387. } GETNAME_DATA;
  2388. BOOL_PTR CALLBACK GetNameDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
  2389. {
  2390.     TCHAR szT[14];
  2391.     TCHAR szTo[MAX_PATH];
  2392.     GETNAME_DATA * pgn = (GETNAME_DATA *)GetWindowLongPtr(hDlg, DWLP_USER);
  2393.     
  2394.     switch (wMsg) 
  2395.     {
  2396.     case WM_INITDIALOG:
  2397.         SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  2398.         
  2399.         pgn = (GETNAME_DATA *)lParam;
  2400.         
  2401.         // inform the user of the old name
  2402.         PathSetDlgItemPath(hDlg, IDD_FROM, pgn->pszDialogFrom);
  2403.         
  2404.         // directory the file will go into
  2405.         PathRemoveFileSpec(pgn->pszDialogTo);
  2406.         PathSetDlgItemPath(hDlg, IDD_DIR, pgn->pszDialogTo);
  2407.         
  2408.         // generate a guess for the new name
  2409.         GuessAShortName(PathFindFileName(pgn->pszDialogFrom), szT);
  2410.         
  2411.         lstrcpy(szTo, pgn->pszDialogTo);
  2412.         PathAppend(szTo, szT);
  2413.         // make sure that name is unique
  2414.         PathYetAnotherMakeUniqueName(szTo, szTo, NULL, NULL);
  2415.         SetDlgItemText(hDlg, IDD_TO, PathFindFileName(szTo));
  2416.         SendDlgItemMessage(hDlg, IDD_TO, EM_LIMITTEXT, 13, 0L);
  2417.         
  2418.         SHAutoComplete(GetDlgItem(hDlg, IDD_TO), 0);
  2419.         
  2420.         if (!pgn->bShowCancel)
  2421.             HideYesToAllAndNo(hDlg);
  2422.         break;
  2423.         
  2424.     case WM_COMMAND:
  2425.         switch (GET_WM_COMMAND_ID(wParam, lParam)) 
  2426.         {
  2427.         case IDD_YESTOALL:
  2428.         case IDYES:
  2429.             GetDlgItemText(hDlg, IDD_TO, szT, ARRAYSIZE(szT));
  2430.             PathAppend(pgn->pszDialogTo, szT);
  2431.             PathQualify(pgn->pszDialogTo);
  2432.             // fall through
  2433.         case IDNO:
  2434.         case IDCANCEL:
  2435.             EndDialog(hDlg,GET_WM_COMMAND_ID(wParam, lParam));
  2436.             break;
  2437.             
  2438.         case IDD_TO:
  2439.             {
  2440.                 LPCTSTR p;
  2441.                 GetDlgItemText(hDlg, IDD_TO, szT, ARRAYSIZE(szT));
  2442.                 for (p = szT; *p; p = CharNext(p)) 
  2443.                 {
  2444.                     if (!(PathGetCharType(*p) & GCT_SHORTCHAR))
  2445.                         break;
  2446.                 }
  2447.             
  2448.                 EnableWindow(GetDlgItem(hDlg,IDYES), ((!*p) && (p != szT)));
  2449.             }
  2450.             break;
  2451.             
  2452.         default:
  2453.             return FALSE;
  2454.         }
  2455.         break;
  2456.         
  2457.         default:
  2458.             return FALSE;
  2459.     }
  2460.     
  2461.     return TRUE;
  2462. }
  2463. int GetNameDialog(HWND hwnd, COPY_STATE *pcs, BOOL fMultiple,UINT wOp, LPTSTR pFrom, LPTSTR pTo)
  2464. {
  2465.     int iRet;
  2466.     
  2467.     // if we don't want to confirm this, just mock up a string and return ok
  2468.     if (!(pcs->cd.fConfirm & CONFIRM_LFNTOFAT)) 
  2469.     {
  2470.         TCHAR szTemp[MAX_PATH];
  2471.         GuessAShortName(PathFindFileName(pFrom), szTemp);
  2472.         PathRemoveFileSpec(pTo);
  2473.         PathAppend(pTo, szTemp);
  2474.         // make sure that name is unique
  2475.         PathYetAnotherMakeUniqueName(pTo, pTo, NULL, NULL);
  2476.         iRet = IDYES;
  2477.     } 
  2478.     else 
  2479.     {
  2480.         GETNAME_DATA gn;
  2481.         gn.pszDialogFrom = pFrom;
  2482.         gn.pszDialogTo = pTo;
  2483.         gn.bShowCancel = fMultiple;
  2484.         
  2485.         iRet = (int)DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_LFNTOFAT), hwnd, GetNameDlgProc, (LPARAM)(GETNAME_DATA *)&gn);
  2486.         if (iRet == IDD_YESTOALL)
  2487.             pcs->cd.fConfirm &= ~CONFIRM_LFNTOFAT;
  2488.     }
  2489.     return iRet;
  2490. }
  2491. STDAPI_(void) SHFreeNameMappings(void *hNameMappings)
  2492. {
  2493.     HDSA hdsaRenamePairs = (HDSA)hNameMappings;
  2494.     int i;
  2495.     
  2496.     if (!hdsaRenamePairs)
  2497.         return;
  2498.     
  2499.     i = DSA_GetItemCount(hdsaRenamePairs) - 1;
  2500.     for (; i >= 0; i--)
  2501.     {
  2502.         SHNAMEMAPPING FAR* prp = DSA_GetItemPtr(hdsaRenamePairs, i);
  2503.         
  2504.         Free(prp->pszOldPath);
  2505.         Free(prp->pszNewPath);
  2506.     }
  2507.     
  2508.     DSA_Destroy(hdsaRenamePairs);
  2509. }
  2510. void _ProcessNameMappings(LPTSTR pszTarget, HDSA hdsaRenamePairs)
  2511. {
  2512.     int i;
  2513.     
  2514.     if (!hdsaRenamePairs)
  2515.         return;
  2516.     
  2517.     for (i = DSA_GetItemCount(hdsaRenamePairs) - 1; i >= 0; i--)
  2518.     {