-/////////////////////////////////////////////////////////////////////////////
-// WinMerge: an interactive diff/merge utility
-// Copyright (C) 1997-2000 Thingamahoochie Software
-// Author: Dean Grimm
-//
-// This program is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-//
-/////////////////////////////////////////////////////////////////////////////
-
-/* 7zCommon.cpp: Implement 7z related classes and functions
- * Copyright (c) 2003 Jochen Tucht
- *
- * Remarks: Different versions of 7-Zip are interfaced through specific
- * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)
- * WinMerge can either use an installed copy of the 7-Zip software,
- * or fallback to a local set of 7-Zip DLLs, which are to be included
- * in the WinMerge binary distribution.
- *
- * Fallback policies are as follows:
- *
- * 1. Detect 7-Zip version installed (XXX).
- * 2. If there is a Merge7zXXX.dll, be happy to use it
- * 3. Detect 7-Zip version from WinMerge distribution (YYY).
- * 4. If there is a Merge7zYYY.dll, be happy to use it.
- * 5. Sorry, no way.
- *
- * These rules can be customized by setting a registry variable
- * *Merge7z/Enable* of type DWORD to one of the following values:
- *
- * 0 - Entirely disable 7-Zip integration.
- * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.
- * 2 - Always use local 7-Zip.
- *
-
-Please mind 2. a) of the GNU General Public License, and log your changes below.
-
-DATE: BY: DESCRIPTION:
-========== ================== ================================================
-2003-12-09 Jochen Tucht Created
-2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2
-2003-12-16 Jochen Tucht Obtain long path to temporary folder
-2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing
-2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex
-2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue
-2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70
-2004-08-25 Jochen Tucht More explicit error message
-2004-10-17 Jochen Tucht Leave decision whether to recurse into folders
- to enumerator (Mask.Recurse)
-2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02
-2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml
- Set Merge7z UI language if DllBuild_Merge7z >= 9
-2005-01-22 Jochen Tucht Better explain what's present/missing/outdated
-2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from
- .exe if .lang file isn't up to date.
-2005-02-26 Jochen Tucht Add download link to error message
-2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes
-2005-02-27 Jochen Tucht FIX [1152375]
-2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView
-2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only
- temporarily, to sort out new directory compare)
-2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20
- Remove noise from Nagbox
-2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ]
-2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for
- formats to be handled by external command line
- tools. These take precedence over Merge7z
- internal handlers.
-2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to
- build intermediate filenames for multi-step
- compression.
-2005-07-15 Jochen Tucht Remove external command line tool integration
- for now. Rethink about it after 2.4 branch.
-2005-08-20 Jochen Tucht Option to guess archive format by signature
- Map extensions through ExternalArchiveFormat.ini
-2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration
-2007-01-04 Kimmo Varis Convert using COptionsMgr for options.
-2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation...
-2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system
- Change recommended version of 7-Zip to 4.57
-2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long
- ago replaced the various format and codec DLLs)
- Change recommended version of 7-Zip to 4.65
-*/
-
-
-#include "stdafx.h"
-#include "7zCommon.h"
-#include <afxinet.h>
-#include <shlwapi.h>
-#include <paths.h>
-#include "OptionsDef.h"
-#include "OptionsMgr.h"
-#include "Merge.h" // DirDocFilter theApp GetOptionsMgr()
-#include "resource.h"
-#include "DirDoc.h"
-//#include "ExternalArchiveFormat.h"
-#include "version.h"
-#include "Environment.h"
-#include "Merge7zFormatRegister.h"
-
-#ifdef _DEBUG
-#define new DEBUG_NEW
-#undef THIS_FILE
-static char THIS_FILE[] = __FILE__;
-#endif
-
-/**
- * @brief Proxy for Merge7z
- */
-static __declspec(thread) Merge7z::Proxy m_Merge7z =
-{
- { 0, 0, DllBuild_Merge7z, },
- "Merge7z%u%02u"DECORATE_U".dll",
- "Merge7z",
- NULL
-};
-
-std::vector<Merge7z::Format *(*)(const String& path)> Merge7zFormatRegister::optionalFormats;
-
-bool IsArchiveFile(const String& pszFile)
-{
- try {
- Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);
- if (piHandler)
- return TRUE;
- else
- return FALSE;
- }
- catch (CException *e)
- {
- e->Delete();
- return FALSE;
- }
- return FALSE;
-}
-
-/**
- * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:
- * - Check if 7-Zip integration is enabled.
- * - Check for filename extension mappings.
- */
-Merge7z::Format *ArchiveGuessFormat(const String& path)
-{
- if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)
- return NULL;
- if (paths_IsDirectory(path))
- return NULL;
- String path2(path);
- // Map extensions through ExternalArchiveFormat.ini
- static TCHAR null[] = _T("");
- static const TCHAR section[] = _T("extensions");
- String entry = paths_FindExtension(path);
- TCHAR value[20];
- static LPCTSTR filename = NULL;
- if (filename == NULL)
- {
- TCHAR cPath[INTERNET_MAX_PATH_LENGTH];
- DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL,
- INTERNET_MAX_PATH_LENGTH, cPath, NULL);
- filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null;
- }
- if (*filename &&
- GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&
- *value == '.')
- {
- // Remove end-of-line comments (in string returned from GetPrivateProfileString)
- // that is, remove semicolon & whatever follows it
- if (LPTSTR p = StrChr(value, ';'))
- {
- *p = '\0';
- StrTrim(value, _T(" \t"));
- }
- path2 = value;
- }
-
- // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues
- // command line integration portion is not yet applied
- // so following code not yet valid, so temporarily commented out
- // Look for command line tool first
- /*Merge7z::Format *pFormat;
- if (CExternalArchiveFormat::GuessFormat(path, pFormat))
- {
- return pFormat;
- }*/
- // Default to Merge7z*.dll
-
- try
- {
- Merge7z::Format *pFormat = m_Merge7z->GuessFormat(path2.c_str());
- if (!pFormat)
- pFormat = Merge7zFormatRegister::GuessFormat(path2);
- return pFormat;
- }
- catch (...)
- {
- Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2);
- if (pFormat)
- return pFormat;
- throw;
- }
-}
-
-/**
- * @brief Self-initializing raw C character buffer class.
- */
-template<class TYPE, size_t SIZE> struct CRawString
-{
- enum { Size = SIZE };
- TYPE Data[SIZE];
- CRawString()
- {
- Data[0] = 0;
- }
-};
-
-/**
- * @brief Exception class for more explicit error message.
- */
-class C7ZipMismatchException : public CException
-{
-public:
- C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause)
- {
- m_dwVer7zInstalled = dwVer7zInstalled;
- m_dwVer7zLocal = dwVer7zLocal;
- m_pCause = pCause;
- }
- ~C7ZipMismatchException()
- {
- if (m_pCause)
- m_pCause->Delete();
- }
- virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);
-protected:
- DWORD m_dwVer7zInstalled;
- DWORD m_dwVer7zLocal;
- CException *m_pCause;
- BOOL m_bShowAllways;
- static const DWORD m_dwVer7zRecommended;
- static const TCHAR m_strRegistryKey[];
- static const TCHAR m_strDownloadURL[];
- static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
- static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD);
-};
-
-/**
- * @brief Recommended version of 7-Zip.
- */
-const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4);
-
-/**
- * @brief Registry key for C7ZipMismatchException's ReportError() popup.
- */
-const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch");
-
-/**
- * @brief Download URL for C7ZipMismatchException's ReportError() popup.
- */
-const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957");
-
-/**
- * @brief Retrieve build number of given DLL.
- */
-static DWORD NTAPI GetDllBuild(LPCTSTR cPath)
-{
- HMODULE hModule = LoadLibrary(cPath);
- DLLVERSIONINFO dvi;
- dvi.cbSize = sizeof dvi;
- dvi.dwBuildNumber = ~0UL;
- if (hModule)
- {
- dvi.dwBuildNumber = 0UL;
- DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion");
- if (DllGetVersion)
- {
- DllGetVersion(&dvi);
- }
- FreeLibrary(hModule);
- }
- return dvi.dwBuildNumber;
-}
-
-/**
- * @brief Format Merge7z version number and plugin name, and retrieve DllBuild.
- */
-DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion)
-{
- *pcVersion = '\0';
- *pcPluginName = '\0';
- if (dwVersion)
- {
- wsprintf
- (
- pcVersion, _T("%u.%02u"),
- UINT HIWORD(dwVersion),
- UINT LOWORD(dwVersion)
- );
- wsprintf
- (
- pcPluginName,
- sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"),
- UINT HIWORD(dwVersion),
- UINT LOWORD(dwVersion)
- );
- }
- return GetDllBuild(pcPluginName);
-}
-
-/**
- * @brief Populate ListBox with names/revisions of DLLs matching given pattern.
- */
-static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox)
-{
- HDC hDC = GetDC(hWnd);
- HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0);
- int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8;
- HGDIOBJ hObject = SelectObject(hDC, hFont);
- WIN32_FIND_DATA ff;
- HANDLE h = FindFirstFile(cPattern, &ff);
- if (h != INVALID_HANDLE_VALUE)
- {
- do
- {
- PathRemoveFileSpec(cPattern);
- PathAppend(cPattern, ff.cFileName);
- wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern));
- lstrcat(cPattern, ff.cFileName);
- int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0);
- if (cxView < cxText)
- cxView = cxText;
- SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern);
- } while (FindNextFile(h, &ff));
- FindClose(h);
- }
- SelectObject(hDC, hObject);
- ReleaseDC(hWnd, hDC);
- SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0);
-}
-
-/**
- * @brief OwnerDraw states from recent SDK.
- */
-#define ODS_NOACCEL 0x0100
-#define ODS_NOFOCUSRECT 0x0200
-
-/**
- * @brief WM_DRAWITEM notification handlers.
- */
-struct CDrawItemStruct : DRAWITEMSTRUCT
-{
- typedef CDrawItemStruct *From;
- void DrawWebLinkButton();
-};
-
-void CDrawItemStruct::DrawWebLinkButton()
-{
- CRawString<TCHAR,INTERNET_MAX_PATH_LENGTH> cText;
- int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size);
- COLORREF clrText = RGB(0,0,255);
- if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT)
- {
- clrText = RGB(128,0,128);
- }
- RECT rcText = rcItem;
- ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT);
- ::OffsetRect(&rcText, 1, 0);
- rcItem.right = rcText.right + 1;
- rcItem.bottom = rcText.bottom + 1;
- switch (itemAction)
- {
- case ODA_DRAWENTIRE:
- ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0);
- ::SetBkMode(hDC, TRANSPARENT);
- ::SetTextColor(hDC, clrText);
- ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT);
- rcText.top = rcText.bottom - 1;
- ::SetBkColor(hDC, clrText);
- ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);
- if (itemState & ODS_FOCUS)
- {
- case ODA_FOCUS:
- if (!(itemState & ODS_NOFOCUSRECT))
- {
- ::SetTextColor(hDC, 0);
- ::SetBkColor(hDC, RGB(255,255,255));
- ::SetBkMode(hDC, OPAQUE);
- DrawFocusRect(hDC, &rcItem);
- }
- }
- break;
- }
-}
-
-/**
- * @brief Load a cursor from COMCTL32.DLL.
- */
-HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName)
-{
- HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL"));
- return hModule ? LoadCursor(hModule, lpCursorName) : NULL;
-}
-
-/**
- * @brief DLGPROC for C7ZipMismatchException's ReportError() popup.
- */
-INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
-{
- switch (uMsg)
- {
- case WM_INITDIALOG:
- {
- theApp.TranslateDialog(hWnd);
- if (GetDlgItem(hWnd, 9001) == NULL)
- {
- // Dialog template isn't up to date. Give it a second chance.
- EndDialog(hWnd, -1);
- return FALSE;
- }
- C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam;
- CRawString<TCHAR,2600> cText;
- CRawString<TCHAR,80> cPresent, cMissing, cOutdated, cNone, cPlugin;
- if (pThis->m_pCause)
- {
- pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size);
- SetDlgItemText(hWnd, 107, cText.Data);
- }
- else
- {
- GetDlgItemText(hWnd, 107, cText.Data, cText.Size);
- switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))
- {
- case 0:
- lstrcat(cText.Data, theApp.LoadString(IDS_MERGE7Z_ENABLE_0).c_str());
- break;
- case 2:
- lstrcat(cText.Data, theApp.LoadString(IDS_MERGE7Z_ENABLE_2).c_str());
- break;
- }
- SetDlgItemText(hWnd, 107, cText.Data);
- }
- GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size);
- GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size);
- GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size);
- GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size);
- GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size);
- wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z);
- SetDlgItemText(hWnd, 102, cText.Data);
- SetDlgItemText
- (
- hWnd, 109,
- (
- pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled
- || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal
- ) ? cPresent.Data : cMissing.Data
- );
- DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended);
- SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data);
- SetDlgItemText(hWnd, 111, cPlugin.Data);
- SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data :
- dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
- dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled);
- SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data);
- SetDlgItemText(hWnd, 121, cPlugin.Data);
- SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data :
- dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
- dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal);
- SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data);
- SetDlgItemText(hWnd, 131, cPlugin.Data);
- SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data :
- dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
- GetModuleFileName(0, cText.Data, MAX_PATH);
- PathRemoveFileSpec(cText.Data);
- PathAppend(cText.Data, _T("Merge7z*.dll"));
- DlgDirListDLLs(hWnd, cText.Data, 105);
- if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0))
- {
- static const TCHAR cSep[] = _T(";");
- LPTSTR pchPath = new TCHAR[cchPath];
- GetEnvironmentVariable(_T("PATH"), pchPath, cchPath);
- LPTSTR pchItem = pchPath;
- while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep))
- {
- if (cchItem < MAX_PATH)
- {
- CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem);
- cText.Data[cchItem] = 0;
- PathAppend(cText.Data, _T("Merge7z*.dll"));
- DlgDirListDLLs(hWnd, cText.Data, 105);
- }
- pchItem += cchItem;
- }
- delete[] pchPath;
- }
- if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0)
- {
- SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data);
- }
- HICON hIcon = LoadIcon(0, IDI_EXCLAMATION);
- SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
- SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
- if (pThis->m_bShowAllways)
- {
- ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE);
- }
- } return TRUE;
- case WM_DRAWITEM:
- {
- switch (wParam)
- {
- case 108:
- CDrawItemStruct::From(lParam)->DrawWebLinkButton();
- break;
- }
- } return TRUE;
- case WM_SETCURSOR:
- {
- HCURSOR hCursor = 0;
- switch (GetDlgCtrlID((HWND)wParam))
- {
- case 108:
- hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108));
- break;
- }
- if (hCursor)
- {
- SetCursor(hCursor);
- SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1);
- return TRUE;
- }
- } return FALSE;
- case WM_COMMAND:
- {
- switch (wParam)
- {
- case IDOK:
- case IDCANCEL:
- {
- LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0);
- EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain));
- } break;
- case 108:
- {
- HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL);
- if ((UINT)h > 32)
- {
- LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE);
- ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT);
- ::InvalidateRect((HWND)lParam, 0, TRUE);
- }
- else
- {
- MessageBeep(0);
- }
- } break;
- }
- } return TRUE;
- }
- return FALSE;
-}
-
-/**
- * @brief Tell user what went wrong and how she can help.
- */
-int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID)
-{
- UINT_PTR response = -1;
- m_bShowAllways = nMessageID;
- if (!m_bShowAllways)
- {
- // Suppress error message in case 7-Zip is not installed.
- response =
- (
- m_dwVer7zInstalled || m_dwVer7zLocal
- ? (INT_PTR)(int)theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1)
- : IDOK
- );
- }
- if (response == -1)
- {
- HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
- response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);
- if (response == -1)
- {
- response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);
- ASSERT(response != -1);
- }
- if (HIBYTE(response) == 1)
- {
- theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response));
- }
- }
- return response;
-}
-
-/**
- * @brief Check whether archive support is available.
- */
-int NTAPI HasZipSupport()
-{
- static int HasZipSupport = -1;
- if (HasZipSupport == -1)
- {
- try
- {
- m_Merge7z.operator->();
- HasZipSupport = 1;
- }
- catch (CException *e)
- {
- e->Delete();
- HasZipSupport = 0;
- }
- }
- return HasZipSupport;
-}
-
-/**
- * @brief Tell user why archive support is not available.
- */
-void NTAPI Recall7ZipMismatchError()
-{
- try
- {
- m_Merge7z.operator->();
- }
- catch (CException *e)
- {
- e->ReportError(MB_ICONEXCLAMATION, TRUE);
- e->Delete();
- }
-}
-
-/**
- * @brief Delete head of temp path context list, and return its parent context.
- */
-CTempPathContext *CTempPathContext::DeleteHead()
-{
- CTempPathContext *pParent = m_pParent;
- delete this;
- return pParent;
-}
-
-BOOL NTAPI IsMerge7zEnabled()
-{
- return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0);
-}
-
-/**
- * @brief Return installed or local version of 7-Zip.
- */
-DWORD NTAPI VersionOf7z(BOOL bLocal)
-{
- TCHAR path[MAX_PATH];
- if (bLocal)
- {
- GetModuleFileName(0, path, sizeof path/sizeof*path);
- PathRemoveFileSpec(path);
- }
- else
- {
- static const TCHAR szSubKey[] = _T("Software\\7-Zip");
- static const TCHAR szValue[] = _T("Path");
- DWORD type = 0;
- DWORD size = sizeof path;
- SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size);
- }
- PathAppend(path, _T("7z.dll"));
- unsigned versionMS = 0;
- unsigned versionLS = 0;
- CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS);
- return versionMS;
-}
-
-/**
- * @brief Access dll functions through proxy.
- */
-interface Merge7z *Merge7z::Proxy::operator->()
-{
- // As long as the Merge7z*.DLL has not yet been loaded, Merge7z
- // [0] points to the name of the DLL (with placeholders for 7-
- // Zip major and minor version numbers). Once the DLL has been
- // loaded successfully, Merge7z[0] is set to NULL, causing the
- // if to fail on subsequent calls.
-
- if (const char *format = Merge7z[0])
- {
- // Merge7z has not yet been loaded
-
- char name[MAX_PATH];
- DWORD flags = ~0;
- CException *pCause = NULL;
- switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))
- {
- case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip.
- if (DWORD ver = VersionOf7z(FALSE))
- {
- flags = Initialize::Default;
- try
- {
- wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));
- Merge7z[0] = name;
- stub.Load();
- break;
- }
- catch (CException *e)
- {
- Merge7z[0] = format;
- pCause = e;
- }
- }
- case 2: //Always use local 7-Zip.
- if (DWORD ver = VersionOf7z(TRUE))
- {
- flags = Initialize::Default | Initialize::Local7z;
- try
- {
- wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));
- Merge7z[0] = name;
- stub.Load();
- break;
- }
- catch (CException *e)
- {
- Merge7z[0] = format;
- if (pCause) pCause->Delete();
- pCause = e;
- }
- }
- default:
- throw new C7ZipMismatchException
- (
- VersionOf7z(FALSE),
- VersionOf7z(TRUE),
- pCause
- );
- }
- LANGID wLangID = (LANGID)GetThreadLocale();
- flags |= wLangID << 16;
- if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))
- {
- flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;
- }
- if (Merge7z[1])
- ((interface Merge7z *)Merge7z[1])->Initialize(flags);
- }
- return ((interface Merge7z *)Merge7z[1]);
-}
-
-/**
- * @brief Tell Merge7z we are going to enumerate just 1 item.
- */
-UINT SingleItemEnumerator::Open()
-{
- return 1;
-}
-
-/**
- * @brief Pass information about the item to Merge7z.
- */
-Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)
-{
- item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;
- item.Name = Name;
- item.FullPath = FullPath;
- return 0;
-}
-
-/**
- * @brief SingleFileEnumerator constructor.
- */
-SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)
-: FullPath(FullPath)
-, Name(Name)
-{
-}
-
-/**
- * @brief Construct a CDirView::DirItemEnumerator.
- *
- * Argument *nFlags* controls operation as follows:
- * LVNI_ALL: Enumerate all items.
- * LVNI_SELECTED: Enumerate selected items only.
- * Original: Set folder prefix for first iteration to "original"
- * Altered: Set folder prefix for second iteration to "altered"
- * BalanceFolders: Ensure that all nonempty folders on either side have a
- * corresponding folder on the other side, even if it is
- * empty (DirScan doesn't recurse into folders which
- * appear only on one side).
- * DiffsOnly: Enumerate diffs only.
- */
-CDirView::DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)
-: m_pView(pView)
-, m_nFlags(nFlags)
-{
- if (m_nFlags & Original)
- {
- m_rgFolderPrefix.push_back(_T("original"));
- }
- if (m_nFlags & Altered)
- {
- m_rgFolderPrefix.push_back(_T("altered"));
- }
- if (m_nFlags & BalanceFolders)
- {
- // Collect implied folders
- for (UINT i = Open() ; i-- ; )
- {
- const DIFFITEM &di = Next();
- if ((m_nFlags & DiffsOnly) && !m_pView->IsItemNavigableDiff(di))
- {
- continue;
- }
- if (m_bRight)
- {
- // Enumerating items on right side
- if (!di.diffcode.isSideFirstOnly())
- {
- // Item is present on right side, i.e. folder is implied
- m_rgImpliedFoldersRight[di.diffFileInfo[1].path.get()] = PVOID(1);
- }
- }
- else
- {
- // Enumerating items on left side
- if (!di.diffcode.isSideSecondOnly())
- {
- // Item is present on left side, i.e. folder is implied
- m_rgImpliedFoldersLeft[di.diffFileInfo[0].path.get()] = PVOID(1);
- }
- }
- }
- }
-}
-
-/**
- * @brief Initialize enumerator, return number of items to be enumerated.
- */
-UINT CDirView::DirItemEnumerator::Open()
-{
- m_nIndex = -1;
- m_curFolderPrefix = m_rgFolderPrefix.begin();
- m_bRight = (m_nFlags & Right) != 0;
- size_t nrgFolderPrefix = m_rgFolderPrefix.size();
- if (nrgFolderPrefix)
- {
- m_strFolderPrefix = *m_curFolderPrefix++;
- }
- else
- {
- nrgFolderPrefix = 1;
- }
- return
- (
- m_nFlags & LVNI_SELECTED
- ? pView(m_pView)->GetSelectedCount()
- : pView(m_pView)->GetItemCount()
- ) * nrgFolderPrefix;
-}
-
-/**
- * @brief Return next item.
- */
-const DIFFITEM &CDirView::DirItemEnumerator::Next()
-{
- enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};
- while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)
- {
- m_strFolderPrefix = *m_curFolderPrefix++;
- m_bRight = TRUE;
- }
- return m_pView->GetDiffItem(m_nIndex);
-}
-
-/**
- * @brief Pass information about an item to Merge7z.
- *
- * Information is passed through struct Merge7z::DirItemEnumerator::Item.
- * The *mask* member denotes which of the other members contain valid data.
- * If *mask* is zero upon return, which will be the case if Enum() decides to
- * leave the struct untouched, Merge7z will ignore the item.
- * If Enum() allocates temporary storage for string members, it must also
- * allocate an Envelope, providing a Free() method to free the temporary
- * storage, along with the Envelope itself. The Envelope pointer is passed to
- * Merge7z as the return value of the function. It is not meant to be a success
- * indicator, so if no temporary storage is required, it is perfectly alright
- * to return NULL.
- */
-Merge7z::Envelope *CDirView::DirItemEnumerator::Enum(Item &item)
-{
- CDirDoc * pDoc = m_pView->GetDocument();
- const DIFFITEM &di = Next();
-
- if ((m_nFlags & DiffsOnly) && !m_pView->IsItemNavigableDiff(di))
- {
- return 0;
- }
-
- bool isSideFirst = di.diffcode.isSideFirstOnly();
- bool isSideSecond = di.diffcode.isSideSecondOnly();
-
- Envelope *envelope = new Envelope;
-
- const String &sFilename = m_bRight ? di.diffFileInfo[1].filename : di.diffFileInfo[0].filename;
- const String &sSubdir = m_bRight ? di.diffFileInfo[1].path : di.diffFileInfo[0].path;
- envelope->Name = sFilename;
- if (sSubdir.length())
- {
- envelope->Name.insert(0, _T("\\"));
- envelope->Name.insert(0, sSubdir);
- }
- envelope->FullPath = sFilename;
- envelope->FullPath.insert(0, _T("\\"));
- envelope->FullPath.insert(0, m_bRight ?
- di.getFilepath(1, pDoc->GetBasePath(1)) :
- di.getFilepath(0, pDoc->GetBasePath(0)));
-
- UINT32 Recurse = item.Mask.Recurse;
-
- if (m_nFlags & BalanceFolders)
- {
- if (m_bRight)
- {
- // Enumerating items on right side
- if (isSideFirst)
- {
- // Item is missing on right side
- PVOID &implied = m_rgImpliedFoldersRight[di.diffFileInfo[0].path.get()];
- if (!implied)
- {
- // Folder is not implied by some other file, and has
- // not been enumerated so far, so enumerate it now!
- envelope->Name = di.diffFileInfo[0].path;
- envelope->FullPath = di.getFilepath(0, pDoc->GetBasePath(0));
- implied = PVOID(2); // Don't enumerate same folder twice!
- isSideFirst = false;
- Recurse = 0;
- }
- }
- }
- else
- {
- // Enumerating items on left side
- if (isSideSecond)
- {
- // Item is missing on left side
- PVOID &implied = m_rgImpliedFoldersLeft[di.diffFileInfo[1].path.get()];
- if (!implied)
- {
- // Folder is not implied by some other file, and has
- // not been enumerated so far, so enumerate it now!
- envelope->Name = di.diffFileInfo[1].path;
- envelope->FullPath = di.getFilepath(1, pDoc->GetBasePath(1));
- implied = PVOID(2); // Don't enumerate same folder twice!
- isSideSecond = false;
- Recurse = 0;
- }
- }
- }
- }
-
- if (m_bRight ? isSideFirst : isSideSecond)
- {
- return envelope;
- }
-
- if (m_strFolderPrefix.length())
- {
- if (envelope->Name.length())
- envelope->Name.insert(0, _T("\\"));
- envelope->Name.insert(0, m_strFolderPrefix);
- }
-
- item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;
- item.Name = envelope->Name.c_str();
- item.FullPath = envelope->FullPath.c_str();
- return envelope;
-}
-
-/**
- * @brief Apply appropriate handlers from left to right.
- */
-bool CDirView::DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)
-{
- DeleteFile(path);
- Merge7z::Format *piHandler = ArchiveGuessFormat(path);
- if (piHandler)
- {
- HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
- CString pathIntermediate;
- SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));
- String pathPrepend = path;
- pathPrepend.resize(pathPrepend.rfind('\\') + 1);
- pathIntermediate.Insert(0, pathPrepend.c_str());
- bool bDone = MultiStepCompressArchive(pathIntermediate);
- if (bDone)
- {
- piHandler->CompressArchive(hwndOwner, path,
- &SingleItemEnumerator(path, pathIntermediate));
- DeleteFile(pathIntermediate);
- }
- else
- {
- piHandler->CompressArchive(hwndOwner, path, this);
- }
- return true;
- }
- return false;
-}
-
-/**
- * @brief Generate archive from DirView items.
- */
-void CDirView::DirItemEnumerator::CompressArchive(LPCTSTR path)
-{
- String strPath;
- if (path == 0)
- {
- // No path given, so prompt for path!
- static const TCHAR _T_Merge7z[] = _T("Merge7z");
- static const TCHAR _T_FilterIndex[] = _T("FilterIndex");
- // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't
- // offer other formats here!
- static const TCHAR _T_Filter[]
- (
- _T("7z|*.7z|")
- //_T("z|*.z|")
- _T("zip|*.zip|")
- _T("jar (zip)|*.jar|")
- _T("ear (zip)|*.ear|")
- _T("war (zip)|*.war|")
- _T("xpi (zip)|*.xpi|")
- //_T("rar|*.rar|")
- _T("tar|*.tar|")
- _T("tar.z|*.tar.z|")
- _T("tar.gz|*.tar.gz|")
- _T("tar.bz2|*.tar.bz2|")
- //_T("tz|*.tz|")
- _T("tgz|*.tgz|")
- _T("tbz2|*.tbz2|")
- //_T("lzh|*.lzh|")
- //_T("cab|*.cab|")
- //_T("arj|*.arj|")
- //_T("deb|*.deb|")
- //_T("rpm|*.rpm|")
- //_T("cpio|*.cpio|")
- //_T("|")
- );
- String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();
- strFilter.insert(0, _T_Filter);
- strFilter += _T("|");
- CFileDialog dlg
- (
- FALSE,
- 0,
- 0,
- OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,
- strFilter.c_str()
- );
- dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1);
- // Use extension from current filter as default extension:
- if (int i = dlg.m_ofn.nFilterIndex)
- {
- dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;
- while (*dlg.m_ofn.lpstrDefExt && --i)
- {
- dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
- dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
- }
- if (*dlg.m_ofn.lpstrDefExt)
- {
- dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;
- }
- }
- if (dlg.DoModal() == IDOK)
- {
- strPath = dlg.GetPathName();
- path = strPath.c_str();
- AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex);
- }
- }
- if (path && !MultiStepCompressArchive(path))
- {
- LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);
- }
-}
-
-/**
- * @brief Collect files for SHFileOperation
- */
-void CDirView::DirItemEnumerator::CollectFiles(String &strBuffer)
-{
- CDirDoc *pDoc = m_pView->GetDocument();
- const String sLeftRootPath = pDoc->GetBasePath(0);
- const String sRightRootPath = pDoc->GetBasePath(1);
- UINT i;
- int cchBuffer = 0;
- for (i = Open() ; i-- ; )
- {
- const DIFFITEM &di = Next();
- if (m_bRight ? m_pView->IsItemOpenableOnRightWith(di) : m_pView->IsItemOpenableOnLeftWith(di))
- {
- cchBuffer +=
- (
- m_bRight ? di.getFilepath(1, sLeftRootPath) : di.getFilepath(0, sRightRootPath)
- ).length() + (m_bRight ? di.diffFileInfo[1].filename : di.diffFileInfo[0].filename).get().length() + 2;
- }
- }
- strBuffer.resize(cchBuffer + 1);
- LPTSTR pchBuffer = &strBuffer[0];
- for (i = Open() ; i-- ; )
- {
- const DIFFITEM &di = Next();
- if (m_bRight ? m_pView->IsItemOpenableOnRightWith(di) : m_pView->IsItemOpenableOnLeftWith(di))
- {
- pchBuffer += wsprintf
- (
- pchBuffer,
- _T("%s\\%s"),
- m_bRight ? di.getFilepath(1, sLeftRootPath).c_str() : di.getFilepath(0, sRightRootPath).c_str(),
- m_bRight ? di.diffFileInfo[1].filename.get().c_str() : di.diffFileInfo[0].filename.get().c_str()
- ) + 1;
- }
- }
- ASSERT(pchBuffer - &strBuffer[0] == cchBuffer);
-}
-
-DecompressResult DecompressArchive(HWND hWnd, const PathContext& files)
-{
- DecompressResult res(files, NULL, IS_EXISTING_DIR);
- try
- {
- String path;
- USES_CONVERSION;
- // Handle archives using 7-zip
- Merge7z::Format *piHandler;
- if (piHandler = ArchiveGuessFormat(res.files[0].c_str()))
- {
- res.pTempPathContext = new CTempPathContext;
- path = env_GetTempChildPath();
- for (int index = 0; index < res.files.GetSize(); index++)
- res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
- if (res.files.GetSize() == 2 && res.files[0] == res.files[1])
- res.files[1].erase();
- do
- {
- if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str())))
- break;
- if (res.files[0].find(path) == 0)
- {
- VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[0].c_str())), false));
- }
- BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str());
- res.files[0] = OLE2T(pTmp);
- SysFreeString(pTmp);
- res.files[0].insert(0, _T("\\"));
- res.files[0].insert(0, path);
- } while (piHandler = ArchiveGuessFormat(res.files[0].c_str()));
- res.files[0] = path;
- }
- if (!res.files[1].empty() && (piHandler = ArchiveGuessFormat(res.files[1].c_str())))
- {
- if (!res.pTempPathContext)
- {
- res.pTempPathContext = new CTempPathContext;
- for (int index = 0; index < res.files.GetSize(); index++)
- res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
- }
- path = env_GetTempChildPath();
- do
- {
- if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str())))
- break;;
- if (res.files[1].find(path) == 0)
- {
- VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[1].c_str())), false));
- }
- BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());
- res.files[1] = OLE2T(pTmp);
- SysFreeString(pTmp);
- res.files[1].insert(0, _T("\\"));
- res.files[1].insert(0, path);
- } while (piHandler = ArchiveGuessFormat(res.files[1].c_str()));
- res.files[1] = path;
- }
- if (res.files.GetSize() > 2 && (piHandler = ArchiveGuessFormat(res.files[2].c_str())))
- {
- if (!res.pTempPathContext)
- {
- res.pTempPathContext = new CTempPathContext;
- for (int index = 0; index < res.files.GetSize(); index++)
- res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
- }
- path = env_GetTempChildPath();
- do
- {
- if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str())))
- break;;
- if (res.files[2].find(path) == 0)
- {
- VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[2].c_str())), false));
- }
- BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());
- res.files[2] = OLE2T(pTmp);
- SysFreeString(pTmp);
- res.files[2].insert(0, _T("\\"));
- res.files[2].insert(0, path);
- } while (piHandler = ArchiveGuessFormat(res.files[2].c_str()));
- res.files[2] = path;
- }
- if (res.files[1].empty())
- {
- // assume Perry style patch
- res.files[1] = path;
- res.files[0] += _T("\\ORIGINAL");
- res.files[1] += _T("\\ALTERED");
- if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str()))
- {
- // not a Perry style patch: diff with itself...
- res.files[0] = res.files[1] = path;
- }
- else
- {
- res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL");
- res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED");
- }
- }
- }
- catch (CException *e)
- {
- e->ReportError(MB_ICONSTOP);
- e->Delete();
- }
- return res;
-}
-
+/////////////////////////////////////////////////////////////////////////////\r
+// WinMerge: an interactive diff/merge utility\r
+// Copyright (C) 1997-2000 Thingamahoochie Software\r
+// Author: Dean Grimm\r
+//\r
+// This program is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
+//\r
+/////////////////////////////////////////////////////////////////////////////\r
+\r
+/* 7zCommon.cpp: Implement 7z related classes and functions\r
+ * Copyright (c) 2003 Jochen Tucht\r
+ *\r
+ * Remarks: Different versions of 7-Zip are interfaced through specific\r
+ * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)\r
+ * WinMerge can either use an installed copy of the 7-Zip software,\r
+ * or fallback to a local set of 7-Zip DLLs, which are to be included\r
+ * in the WinMerge binary distribution.\r
+ *\r
+ * Fallback policies are as follows:\r
+ *\r
+ * 1. Detect 7-Zip version installed (XXX).\r
+ * 2. If there is a Merge7zXXX.dll, be happy to use it\r
+ * 3. Detect 7-Zip version from WinMerge distribution (YYY).\r
+ * 4. If there is a Merge7zYYY.dll, be happy to use it.\r
+ * 5. Sorry, no way.\r
+ *\r
+ * These rules can be customized by setting a registry variable\r
+ * *Merge7z/Enable* of type DWORD to one of the following values:\r
+ *\r
+ * 0 - Entirely disable 7-Zip integration.\r
+ * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
+ * 2 - Always use local 7-Zip.\r
+ *\r
+\r
+Please mind 2. a) of the GNU General Public License, and log your changes below.\r
+\r
+DATE: BY: DESCRIPTION:\r
+========== ================== ================================================\r
+2003-12-09 Jochen Tucht Created\r
+2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2\r
+2003-12-16 Jochen Tucht Obtain long path to temporary folder\r
+2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing\r
+2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex\r
+2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue\r
+2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70\r
+2004-08-25 Jochen Tucht More explicit error message\r
+2004-10-17 Jochen Tucht Leave decision whether to recurse into folders\r
+ to enumerator (Mask.Recurse)\r
+2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02\r
+2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml\r
+ Set Merge7z UI language if DllBuild_Merge7z >= 9\r
+2005-01-22 Jochen Tucht Better explain what's present/missing/outdated\r
+2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from\r
+ .exe if .lang file isn't up to date.\r
+2005-02-26 Jochen Tucht Add download link to error message\r
+2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes\r
+2005-02-27 Jochen Tucht FIX [1152375]\r
+2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView\r
+2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only\r
+ temporarily, to sort out new directory compare)\r
+2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20\r
+ Remove noise from Nagbox\r
+2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ]\r
+2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for\r
+ formats to be handled by external command line\r
+ tools. These take precedence over Merge7z\r
+ internal handlers.\r
+2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to\r
+ build intermediate filenames for multi-step\r
+ compression.\r
+2005-07-15 Jochen Tucht Remove external command line tool integration\r
+ for now. Rethink about it after 2.4 branch.\r
+2005-08-20 Jochen Tucht Option to guess archive format by signature\r
+ Map extensions through ExternalArchiveFormat.ini\r
+2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration\r
+2007-01-04 Kimmo Varis Convert using COptionsMgr for options.\r
+2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation...\r
+2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system\r
+ Change recommended version of 7-Zip to 4.57\r
+2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long\r
+ ago replaced the various format and codec DLLs)\r
+ Change recommended version of 7-Zip to 4.65\r
+*/\r
+\r
- // ID line follows -- this is updated by SVN\r
- // $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $\r
+\r
+#include "stdafx.h"\r
+#include "7zCommon.h"\r
+#include <afxinet.h>\r
+#include <shlwapi.h>\r
+#include "OptionsDef.h"\r
+#include "OptionsMgr.h"\r
+#include "Merge.h" // DirDocFilter theApp GetOptionsMgr()\r
+#include "resource.h"\r
+#include "DirView.h"\r
+#include "DirDoc.h"\r
+#include "DirActions.h"\r
+//#include "ExternalArchiveFormat.h"\r
+#include "version.h"\r
+#include "paths.h"\r
+#include "Environment.h"\r
+#include "Merge7zFormatRegister.h"\r
+\r
+#ifdef _DEBUG\r
+#define new DEBUG_NEW\r
+#undef THIS_FILE\r
+static char THIS_FILE[] = __FILE__;\r
+#endif\r
+\r
+/**\r
+ * @brief Proxy for Merge7z\r
+ */\r
+static __declspec(thread) Merge7z::Proxy m_Merge7z =\r
+{\r
+ { 0, 0, DllBuild_Merge7z, },\r
+ "Merge7z%u%02u"DECORATE_U".dll",\r
+ "Merge7z",\r
+ NULL\r
+};\r
+\r
+std::vector<Merge7z::Format *(*)(const String& path)> Merge7zFormatRegister::optionalFormats;\r
+\r
+/**\r
+ * @brief assign BSTR to String, and return BSTR for optional SysFreeString()\r
+ */\r
+inline BSTR Assign(CString &dst, BSTR src)\r
+{\r
+ dst = src;\r
+ return src;\r
+}\r
+\r
+bool IsArchiveFile(const String& pszFile)\r
+{\r
+ try {\r
+ Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);\r
+ if (piHandler)\r
+ return TRUE;\r
+ else\r
+ return FALSE;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->Delete();\r
+ return FALSE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:\r
+ * - Check if 7-Zip integration is enabled.\r
+ * - Check for filename extension mappings.\r
+ */\r
+Merge7z::Format *ArchiveGuessFormat(const String& path)\r
+{\r
+ if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)\r
+ return NULL;\r
+ if (paths_IsDirectory(path))\r
+ return NULL;\r
+ String path2(path);\r
+ // Map extensions through ExternalArchiveFormat.ini\r
+ static TCHAR null[] = _T("");\r
+ static const TCHAR section[] = _T("extensions");\r
+ String entry = paths_FindExtension(path);\r
+ TCHAR value[20];\r
+ static LPCTSTR filename = NULL;\r
+ if (filename == NULL)\r
+ {\r
+ TCHAR cPath[INTERNET_MAX_PATH_LENGTH];\r
+ DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL,\r
+ INTERNET_MAX_PATH_LENGTH, cPath, NULL);\r
+ filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null;\r
+ }\r
+ if (*filename &&\r
+ GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&\r
+ *value == '.')\r
+ {\r
+ // Remove end-of-line comments (in string returned from GetPrivateProfileString)\r
+ // that is, remove semicolon & whatever follows it\r
+ if (LPTSTR p = StrChr(value, ';'))\r
+ {\r
+ *p = '\0';\r
+ StrTrim(value, _T(" \t"));\r
+ }\r
+ path2 = value;\r
+ }\r
+\r
+ // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues\r
+ // command line integration portion is not yet applied\r
+ // so following code not yet valid, so temporarily commented out\r
+ // Look for command line tool first\r
+ /*Merge7z::Format *pFormat;\r
+ if (CExternalArchiveFormat::GuessFormat(path, pFormat))\r
+ {\r
+ return pFormat;\r
+ }*/\r
+ // Default to Merge7z*.dll\r
+\r
+ try\r
+ {\r
+ Merge7z::Format *pFormat = m_Merge7z->GuessFormat(path2.c_str());\r
+ if (!pFormat)\r
+ pFormat = Merge7zFormatRegister::GuessFormat(path2);\r
+ return pFormat;\r
+ }\r
+ catch (...)\r
+ {\r
+ Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2);\r
+ if (pFormat)\r
+ return pFormat;\r
+ throw;\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Self-initializing raw C character buffer class.\r
+ */\r
+template<class TYPE, size_t SIZE> struct CRawString\r
+{\r
+ enum { Size = SIZE };\r
+ TYPE Data[SIZE];\r
+ CRawString()\r
+ {\r
+ Data[0] = 0;\r
+ }\r
+};\r
+\r
+/**\r
+ * @brief Exception class for more explicit error message.\r
+ */\r
+class C7ZipMismatchException : public CException\r
+{\r
+public:\r
+ C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause)\r
+ {\r
+ m_dwVer7zInstalled = dwVer7zInstalled;\r
+ m_dwVer7zLocal = dwVer7zLocal;\r
+ m_pCause = pCause;\r
+ }\r
+ ~C7ZipMismatchException()\r
+ {\r
+ if (m_pCause)\r
+ m_pCause->Delete();\r
+ }\r
+ virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);\r
+protected:\r
+ DWORD m_dwVer7zInstalled;\r
+ DWORD m_dwVer7zLocal;\r
+ CException *m_pCause;\r
+ BOOL m_bShowAllways;\r
+ static const DWORD m_dwVer7zRecommended;\r
+ static const TCHAR m_strRegistryKey[];\r
+ static const TCHAR m_strDownloadURL[];\r
+ static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);\r
+ static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD);\r
+};\r
+\r
+/**\r
+ * @brief Recommended version of 7-Zip.\r
+ */\r
+const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4);\r
+\r
+/**\r
+ * @brief Registry key for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch");\r
+\r
+/**\r
+ * @brief Download URL for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957");\r
+\r
+/**\r
+ * @brief Retrieve build number of given DLL.\r
+ */\r
+static DWORD NTAPI GetDllBuild(LPCTSTR cPath)\r
+{\r
+ HMODULE hModule = LoadLibrary(cPath);\r
+ DLLVERSIONINFO dvi;\r
+ dvi.cbSize = sizeof dvi;\r
+ dvi.dwBuildNumber = ~0UL;\r
+ if (hModule)\r
+ {\r
+ dvi.dwBuildNumber = 0UL;\r
+ DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion");\r
+ if (DllGetVersion)\r
+ {\r
+ DllGetVersion(&dvi);\r
+ }\r
+ FreeLibrary(hModule);\r
+ }\r
+ return dvi.dwBuildNumber;\r
+}\r
+\r
+/**\r
+ * @brief Format Merge7z version number and plugin name, and retrieve DllBuild.\r
+ */\r
+DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion)\r
+{\r
+ *pcVersion = '\0';\r
+ *pcPluginName = '\0';\r
+ if (dwVersion)\r
+ {\r
+ wsprintf\r
+ (\r
+ pcVersion, _T("%u.%02u"),\r
+ UINT HIWORD(dwVersion),\r
+ UINT LOWORD(dwVersion)\r
+ );\r
+ wsprintf\r
+ (\r
+ pcPluginName,\r
+ sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"),\r
+ UINT HIWORD(dwVersion),\r
+ UINT LOWORD(dwVersion)\r
+ );\r
+ }\r
+ return GetDllBuild(pcPluginName);\r
+}\r
+\r
+/**\r
+ * @brief Populate ListBox with names/revisions of DLLs matching given pattern.\r
+ */\r
+static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox)\r
+{\r
+ HDC hDC = GetDC(hWnd);\r
+ HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0);\r
+ int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8;\r
+ HGDIOBJ hObject = SelectObject(hDC, hFont);\r
+ WIN32_FIND_DATA ff;\r
+ HANDLE h = FindFirstFile(cPattern, &ff);\r
+ if (h != INVALID_HANDLE_VALUE)\r
+ {\r
+ do\r
+ {\r
+ PathRemoveFileSpec(cPattern);\r
+ PathAppend(cPattern, ff.cFileName);\r
+ wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern));\r
+ lstrcat(cPattern, ff.cFileName);\r
+ int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0);\r
+ if (cxView < cxText)\r
+ cxView = cxText;\r
+ SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern);\r
+ } while (FindNextFile(h, &ff));\r
+ FindClose(h);\r
+ }\r
+ SelectObject(hDC, hObject);\r
+ ReleaseDC(hWnd, hDC);\r
+ SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0);\r
+}\r
+\r
+/**\r
+ * @brief OwnerDraw states from recent SDK.\r
+ */\r
+#define ODS_NOACCEL 0x0100\r
+#define ODS_NOFOCUSRECT 0x0200\r
+\r
+/**\r
+ * @brief WM_DRAWITEM notification handlers.\r
+ */\r
+struct CDrawItemStruct : DRAWITEMSTRUCT\r
+{\r
+ typedef CDrawItemStruct *From;\r
+ void DrawWebLinkButton();\r
+};\r
+\r
+void CDrawItemStruct::DrawWebLinkButton()\r
+{\r
+ CRawString<TCHAR,INTERNET_MAX_PATH_LENGTH> cText;\r
+ int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size);\r
+ COLORREF clrText = RGB(0,0,255);\r
+ if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT)\r
+ {\r
+ clrText = RGB(128,0,128);\r
+ }\r
+ RECT rcText = rcItem;\r
+ ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT);\r
+ ::OffsetRect(&rcText, 1, 0);\r
+ rcItem.right = rcText.right + 1;\r
+ rcItem.bottom = rcText.bottom + 1;\r
+ switch (itemAction)\r
+ {\r
+ case ODA_DRAWENTIRE:\r
+ ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0);\r
+ ::SetBkMode(hDC, TRANSPARENT);\r
+ ::SetTextColor(hDC, clrText);\r
+ ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT);\r
+ rcText.top = rcText.bottom - 1;\r
+ ::SetBkColor(hDC, clrText);\r
+ ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);\r
+ if (itemState & ODS_FOCUS)\r
+ {\r
+ case ODA_FOCUS:\r
+ if (!(itemState & ODS_NOFOCUSRECT))\r
+ {\r
+ ::SetTextColor(hDC, 0);\r
+ ::SetBkColor(hDC, RGB(255,255,255));\r
+ ::SetBkMode(hDC, OPAQUE);\r
+ DrawFocusRect(hDC, &rcItem);\r
+ }\r
+ }\r
+ break;\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Load a cursor from COMCTL32.DLL.\r
+ */\r
+HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName)\r
+{\r
+ HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL"));\r
+ return hModule ? LoadCursor(hModule, lpCursorName) : NULL;\r
+}\r
+\r
+/**\r
+ * @brief DLGPROC for C7ZipMismatchException's ReportError() popup.\r
+ */\r
+INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)\r
+{\r
+ switch (uMsg)\r
+ {\r
+ case WM_INITDIALOG:\r
+ {\r
+ theApp.TranslateDialog(hWnd);\r
+ if (GetDlgItem(hWnd, 9001) == NULL)\r
+ {\r
+ // Dialog template isn't up to date. Give it a second chance.\r
+ EndDialog(hWnd, -1);\r
+ return FALSE;\r
+ }\r
+ C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam;\r
+ CRawString<TCHAR,2600> cText;\r
+ CRawString<TCHAR,80> cPresent, cMissing, cOutdated, cNone, cPlugin;\r
+ if (pThis->m_pCause)\r
+ {\r
+ pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size);\r
+ SetDlgItemText(hWnd, 107, cText.Data);\r
+ }\r
+ else\r
+ {\r
+ GetDlgItemText(hWnd, 107, cText.Data, cText.Size);\r
+ switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
+ {\r
+ case 0:\r
+ lstrcat(cText.Data, _("\nNote: 7-Zip integration is disabled in WinMerge settings.").c_str());\r
+ break;\r
+ case 2:\r
+ lstrcat(cText.Data, _("\nNote: 7-Zip integration is restricted to standalone operation in WinMerge settings.").c_str());\r
+ break;\r
+ }\r
+ SetDlgItemText(hWnd, 107, cText.Data);\r
+ }\r
+ GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size);\r
+ GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size);\r
+ GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size);\r
+ GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size);\r
+ GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size);\r
+ wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z);\r
+ SetDlgItemText(hWnd, 102, cText.Data);\r
+ SetDlgItemText\r
+ (\r
+ hWnd, 109,\r
+ (\r
+ pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled\r
+ || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal\r
+ ) ? cPresent.Data : cMissing.Data\r
+ );\r
+ DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended);\r
+ SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 111, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled);\r
+ SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 121, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal);\r
+ SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data);\r
+ SetDlgItemText(hWnd, 131, cPlugin.Data);\r
+ SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data :\r
+ dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);\r
+ GetModuleFileName(0, cText.Data, MAX_PATH);\r
+ PathRemoveFileSpec(cText.Data);\r
+ PathAppend(cText.Data, _T("Merge7z*.dll"));\r
+ DlgDirListDLLs(hWnd, cText.Data, 105);\r
+ if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0))\r
+ {\r
+ static const TCHAR cSep[] = _T(";");\r
+ LPTSTR pchPath = new TCHAR[cchPath];\r
+ GetEnvironmentVariable(_T("PATH"), pchPath, cchPath);\r
+ LPTSTR pchItem = pchPath;\r
+ while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep))\r
+ {\r
+ if (cchItem < MAX_PATH)\r
+ {\r
+ CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem);\r
+ cText.Data[cchItem] = 0;\r
+ PathAppend(cText.Data, _T("Merge7z*.dll"));\r
+ DlgDirListDLLs(hWnd, cText.Data, 105);\r
+ }\r
+ pchItem += cchItem;\r
+ }\r
+ delete[] pchPath;\r
+ }\r
+ if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0)\r
+ {\r
+ SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data);\r
+ }\r
+ HICON hIcon = LoadIcon(0, IDI_EXCLAMATION);\r
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);\r
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);\r
+ if (pThis->m_bShowAllways)\r
+ {\r
+ ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE);\r
+ }\r
+ } return TRUE;\r
+ case WM_DRAWITEM:\r
+ {\r
+ switch (wParam)\r
+ {\r
+ case 108:\r
+ CDrawItemStruct::From(lParam)->DrawWebLinkButton();\r
+ break;\r
+ }\r
+ } return TRUE;\r
+ case WM_SETCURSOR:\r
+ {\r
+ HCURSOR hCursor = 0;\r
+ switch (GetDlgCtrlID((HWND)wParam))\r
+ {\r
+ case 108:\r
+ hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108));\r
+ break;\r
+ }\r
+ if (hCursor)\r
+ {\r
+ SetCursor(hCursor);\r
+ SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1);\r
+ return TRUE;\r
+ }\r
+ } return FALSE;\r
+ case WM_COMMAND:\r
+ {\r
+ switch (wParam)\r
+ {\r
+ case IDOK:\r
+ case IDCANCEL:\r
+ {\r
+ LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0);\r
+ EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain));\r
+ } break;\r
+ case 108:\r
+ {\r
+ HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL);\r
+ if ((UINT)h > 32)\r
+ {\r
+ LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE);\r
+ ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT);\r
+ ::InvalidateRect((HWND)lParam, 0, TRUE);\r
+ }\r
+ else\r
+ {\r
+ MessageBeep(0);\r
+ }\r
+ } break;\r
+ }\r
+ } return TRUE;\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ * @brief Tell user what went wrong and how she can help.\r
+ */\r
+int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID)\r
+{\r
+ UINT_PTR response = -1;\r
+ m_bShowAllways = nMessageID;\r
+ if (!m_bShowAllways)\r
+ {\r
+ // Suppress error message in case 7-Zip is not installed.\r
+ response =\r
+ (\r
+ m_dwVer7zInstalled || m_dwVer7zLocal\r
+ ? (INT_PTR)(int)theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1)\r
+ : IDOK\r
+ );\r
+ }\r
+ if (response == -1)\r
+ {\r
+ HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
+ response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
+ if (response == -1)\r
+ {\r
+ response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);\r
+ ASSERT(response != -1);\r
+ }\r
+ if (HIBYTE(response) == 1)\r
+ {\r
+ theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response));\r
+ }\r
+ }\r
+ return response;\r
+}\r
+\r
+/**\r
+ * @brief Check whether archive support is available.\r
+ */\r
+int NTAPI HasZipSupport()\r
+{\r
+ static int HasZipSupport = -1;\r
+ if (HasZipSupport == -1)\r
+ {\r
+ try\r
+ {\r
+ m_Merge7z.operator->();\r
+ HasZipSupport = 1;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->Delete();\r
+ HasZipSupport = 0;\r
+ }\r
+ }\r
+ return HasZipSupport;\r
+}\r
+\r
+/**\r
+ * @brief Tell user why archive support is not available.\r
+ */\r
+void NTAPI Recall7ZipMismatchError()\r
+{\r
+ try\r
+ {\r
+ m_Merge7z.operator->();\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->ReportError(MB_ICONEXCLAMATION, TRUE);\r
+ e->Delete();\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Delete head of temp path context list, and return its parent context.\r
+ */\r
+CTempPathContext *CTempPathContext::DeleteHead()\r
+{\r
+ CTempPathContext *pParent = m_pParent;\r
+ delete this;\r
+ return pParent;\r
+}\r
+\r
+BOOL NTAPI IsMerge7zEnabled()\r
+{\r
+ return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0);\r
+}\r
+\r
+/**\r
+ * @brief Return installed or local version of 7-Zip.\r
+ */\r
+DWORD NTAPI VersionOf7z(BOOL bLocal)\r
+{\r
+ TCHAR path[MAX_PATH];\r
+ if (bLocal)\r
+ {\r
+ GetModuleFileName(0, path, sizeof path/sizeof*path);\r
+ PathRemoveFileSpec(path);\r
+ }\r
+ else\r
+ {\r
+ static const TCHAR szSubKey[] = _T("Software\\7-Zip");\r
+ static const TCHAR szValue[] = _T("Path");\r
+ DWORD type = 0;\r
+ DWORD size = sizeof path;\r
+ SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size);\r
+ }\r
+ PathAppend(path, _T("7z.dll"));\r
+ unsigned versionMS = 0;\r
+ unsigned versionLS = 0;\r
+ CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS);\r
+ return versionMS;\r
+}\r
+\r
+/**\r
+ * @brief Access dll functions through proxy.\r
+ */\r
+interface Merge7z *Merge7z::Proxy::operator->()\r
+{\r
+ // As long as the Merge7z*.DLL has not yet been loaded, Merge7z\r
+ // [0] points to the name of the DLL (with placeholders for 7-\r
+ // Zip major and minor version numbers). Once the DLL has been\r
+ // loaded successfully, Merge7z[0] is set to NULL, causing the\r
+ // if to fail on subsequent calls.\r
+\r
+ if (const char *format = Merge7z[0])\r
+ {\r
+ // Merge7z has not yet been loaded\r
+\r
+ char name[MAX_PATH];\r
+ DWORD flags = ~0;\r
+ CException *pCause = NULL;\r
+ switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
+ {\r
+ case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip.\r
+ if (DWORD ver = VersionOf7z(FALSE))\r
+ {\r
+ flags = Initialize::Default;\r
+ try\r
+ {\r
+ wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
+ Merge7z[0] = name;\r
+ stub.Load();\r
+ break;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ Merge7z[0] = format;\r
+ pCause = e;\r
+ }\r
+ }\r
+ case 2: //Always use local 7-Zip.\r
+ if (DWORD ver = VersionOf7z(TRUE))\r
+ {\r
+ flags = Initialize::Default | Initialize::Local7z;\r
+ try\r
+ {\r
+ wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
+ Merge7z[0] = name;\r
+ stub.Load();\r
+ break;\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ Merge7z[0] = format;\r
+ if (pCause) pCause->Delete();\r
+ pCause = e;\r
+ }\r
+ }\r
+ default:\r
+ throw new C7ZipMismatchException\r
+ (\r
+ VersionOf7z(FALSE),\r
+ VersionOf7z(TRUE),\r
+ pCause\r
+ );\r
+ }\r
+ LANGID wLangID = (LANGID)GetThreadLocale();\r
+ flags |= wLangID << 16;\r
+ if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))\r
+ {\r
+ flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;\r
+ }\r
+ if (Merge7z[1])\r
+ ((interface Merge7z *)Merge7z[1])->Initialize(flags);\r
+ }\r
+ return ((interface Merge7z *)Merge7z[1]);\r
+}\r
+\r
+/**\r
+ * @brief Tell Merge7z we are going to enumerate just 1 item.\r
+ */\r
+UINT SingleItemEnumerator::Open()\r
+{\r
+ return 1;\r
+}\r
+\r
+/**\r
+ * @brief Pass information about the item to Merge7z.\r
+ */\r
+Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)\r
+{\r
+ item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;\r
+ item.Name = Name;\r
+ item.FullPath = FullPath;\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * @brief SingleFileEnumerator constructor.\r
+ */\r
+SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)\r
+: FullPath(FullPath)\r
+, Name(Name)\r
+{\r
+}\r
+\r
+/**\r
+ * @brief Construct a DirItemEnumerator.\r
+ *\r
+ * Argument *nFlags* controls operation as follows:\r
+ * LVNI_ALL: Enumerate all items.\r
+ * LVNI_SELECTED: Enumerate selected items only.\r
+ * Original: Set folder prefix for first iteration to "original"\r
+ * Altered: Set folder prefix for second iteration to "altered"\r
+ * BalanceFolders: Ensure that all nonempty folders on either side have a\r
+ * corresponding folder on the other side, even if it is\r
+ * empty (DirScan doesn't recurse into folders which\r
+ * appear only on one side).\r
+ * DiffsOnly: Enumerate diffs only.\r
+ */\r
+DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)\r
+: m_pView(pView)\r
+, m_nFlags(nFlags)\r
+{\r
+ if (m_nFlags & Original)\r
+ {\r
+ m_rgFolderPrefix.push_back(_T("original"));\r
+ }\r
+ if (m_nFlags & Altered)\r
+ {\r
+ m_rgFolderPrefix.push_back(_T("altered"));\r
+ }\r
+ if (m_nFlags & BalanceFolders)\r
+ {\r
+ const CDiffContext& ctxt = pView->GetDiffContext();\r
+ // Collect implied folders\r
+ for (UINT i = Open() ; i-- ; )\r
+ {\r
+ const DIFFITEM &di = Next();\r
+ if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
+ {\r
+ continue;\r
+ }\r
+ // Enumerating items\r
+ if (di.diffcode.exists(m_index))\r
+ {\r
+ // Item is present on right side, i.e. folder is implied\r
+ m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ * @brief Initialize enumerator, return number of items to be enumerated.\r
+ */\r
+UINT DirItemEnumerator::Open()\r
+{\r
+ m_nIndex = -1;\r
+ m_curFolderPrefix = m_rgFolderPrefix.begin();\r
+ m_index = (m_nFlags & Right) != 0 ? 1 : 0;\r
+ size_t nrgFolderPrefix = m_rgFolderPrefix.size();\r
+ if (nrgFolderPrefix)\r
+ {\r
+ m_strFolderPrefix = *m_curFolderPrefix++;\r
+ }\r
+ else\r
+ {\r
+ nrgFolderPrefix = 1;\r
+ }\r
+ return\r
+ (\r
+ m_nFlags & LVNI_SELECTED\r
+ ? pView(m_pView)->GetSelectedCount()\r
+ : pView(m_pView)->GetItemCount()\r
+ ) * nrgFolderPrefix;\r
+}\r
+\r
+/**\r
+ * @brief Return next item.\r
+ */\r
+const DIFFITEM &DirItemEnumerator::Next()\r
+{\r
+ enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};\r
+ while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)\r
+ {\r
+ m_strFolderPrefix = *m_curFolderPrefix++;\r
+ m_index = 1;\r
+ }\r
+ return m_pView->GetDiffItem(m_nIndex);\r
+}\r
+\r
+/**\r
+ * @brief Pass information about an item to Merge7z.\r
+ *\r
+ * Information is passed through struct Merge7z::DirItemEnumerator::Item.\r
+ * The *mask* member denotes which of the other members contain valid data.\r
+ * If *mask* is zero upon return, which will be the case if Enum() decides to\r
+ * leave the struct untouched, Merge7z will ignore the item.\r
+ * If Enum() allocates temporary storage for string members, it must also\r
+ * allocate an Envelope, providing a Free() method to free the temporary\r
+ * storage, along with the Envelope itself. The Envelope pointer is passed to\r
+ * Merge7z as the return value of the function. It is not meant to be a success\r
+ * indicator, so if no temporary storage is required, it is perfectly alright\r
+ * to return NULL.\r
+ */\r
+Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)\r
+{\r
+ const CDiffContext& ctxt = m_pView->GetDiffContext();\r
+ const DIFFITEM &di = Next();\r
+\r
+ if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))\r
+ {\r
+ return 0;\r
+ }\r
+\r
+ bool isSideOnly = !di.diffcode.exists(m_index);\r
+\r
+ Envelope *envelope = new Envelope;\r
+\r
+ const String &sFilename = di.diffFileInfo[m_index].filename;\r
+ const String &sSubdir = di.diffFileInfo[m_index].path;\r
+ if (sSubdir.length())\r
+ envelope->Name = paths_ConcatPath(sSubdir, sFilename);\r
+ else\r
+ envelope->Name = sFilename;\r
+ envelope->FullPath = paths_ConcatPath(\r
+ di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),\r
+ sFilename);\r
+\r
+ UINT32 Recurse = item.Mask.Recurse;\r
+\r
+ if (m_nFlags & BalanceFolders)\r
+ {\r
+ // Enumerating items on right side\r
+ if (isSideOnly)\r
+ {\r
+ // Item is missing on right side\r
+ PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];\r
+ if (!implied)\r
+ {\r
+ // Folder is not implied by some other file, and has\r
+ // not been enumerated so far, so enumerate it now!\r
+ envelope->Name = di.diffFileInfo[1-m_index].path;\r
+ envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));\r
+ implied = PVOID(2); // Don't enumerate same folder twice!\r
+ isSideOnly = false;\r
+ Recurse = 0;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (isSideOnly)\r
+ {\r
+ return envelope;\r
+ }\r
+\r
+ if (m_strFolderPrefix.length())\r
+ {\r
+ if (envelope->Name.length())\r
+ envelope->Name.insert(0, _T("\\"));\r
+ envelope->Name.insert(0, m_strFolderPrefix);\r
+ }\r
+\r
+ item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;\r
+ item.Name = envelope->Name.c_str();\r
+ item.FullPath = envelope->FullPath.c_str();\r
+ return envelope;\r
+}\r
+\r
+/**\r
+ * @brief Apply appropriate handlers from left to right.\r
+ */\r
+bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)\r
+{\r
+ DeleteFile(path);\r
+ Merge7z::Format *piHandler = ArchiveGuessFormat(path);\r
+ if (piHandler)\r
+ {\r
+ HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();\r
+ CString pathIntermediate;\r
+ SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));\r
+ String pathPrepend = path;\r
+ pathPrepend.resize(pathPrepend.rfind('\\') + 1);\r
+ pathIntermediate.Insert(0, pathPrepend.c_str());\r
+ bool bDone = MultiStepCompressArchive(pathIntermediate);\r
+ if (bDone)\r
+ {\r
+ piHandler->CompressArchive(hwndOwner, path,\r
+ &SingleItemEnumerator(path, pathIntermediate));\r
+ DeleteFile(pathIntermediate);\r
+ }\r
+ else\r
+ {\r
+ piHandler->CompressArchive(hwndOwner, path, this);\r
+ }\r
+ return true;\r
+ }\r
+ return false;\r
+}\r
+\r
+/**\r
+ * @brief Generate archive from DirView items.\r
+ */\r
+void DirItemEnumerator::CompressArchive(LPCTSTR path)\r
+{\r
+ String strPath;\r
+ if (path == 0)\r
+ {\r
+ // No path given, so prompt for path!\r
+ static const TCHAR _T_Merge7z[] = _T("Merge7z");\r
+ static const TCHAR _T_FilterIndex[] = _T("FilterIndex");\r
+ // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't\r
+ // offer other formats here!\r
+ static const TCHAR _T_Filter[]\r
+ (\r
+ _T("7z|*.7z|")\r
+ //_T("z|*.z|")\r
+ _T("zip|*.zip|")\r
+ _T("jar (zip)|*.jar|")\r
+ _T("ear (zip)|*.ear|")\r
+ _T("war (zip)|*.war|")\r
+ _T("xpi (zip)|*.xpi|")\r
+ //_T("rar|*.rar|")\r
+ _T("tar|*.tar|")\r
+ _T("tar.z|*.tar.z|")\r
+ _T("tar.gz|*.tar.gz|")\r
+ _T("tar.bz2|*.tar.bz2|")\r
+ //_T("tz|*.tz|")\r
+ _T("tgz|*.tgz|")\r
+ _T("tbz2|*.tbz2|")\r
+ //_T("lzh|*.lzh|")\r
+ //_T("cab|*.cab|")\r
+ //_T("arj|*.arj|")\r
+ //_T("deb|*.deb|")\r
+ //_T("rpm|*.rpm|")\r
+ //_T("cpio|*.cpio|")\r
+ //_T("|")\r
+ );\r
+ String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();\r
+ strFilter.insert(0, _T_Filter);\r
+ strFilter += _T("|");\r
+ CFileDialog dlg\r
+ (\r
+ FALSE,\r
+ 0,\r
+ 0,\r
+ OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,\r
+ strFilter.c_str()\r
+ );\r
+ dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1);\r
+ // Use extension from current filter as default extension:\r
+ if (int i = dlg.m_ofn.nFilterIndex)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;\r
+ while (*dlg.m_ofn.lpstrDefExt && --i)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;\r
+ }\r
+ if (*dlg.m_ofn.lpstrDefExt)\r
+ {\r
+ dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;\r
+ }\r
+ }\r
+ if (dlg.DoModal() == IDOK)\r
+ {\r
+ strPath = dlg.GetPathName();\r
+ path = strPath.c_str();\r
+ AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex);\r
+ }\r
+ }\r
+ if (path && !MultiStepCompressArchive(path))\r
+ {\r
+ LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);\r
+ }\r
+}\r
+\r
+\r
+DecompressResult DecompressArchive(HWND hWnd, const PathContext& files)\r
+{\r
+ DecompressResult res(files, NULL, IS_EXISTING_DIR);\r
+ try\r
+ {\r
+ String path;\r
+ USES_CONVERSION;\r
+ // Handle archives using 7-zip\r
+ Merge7z::Format *piHandler;\r
+ if (piHandler = ArchiveGuessFormat(res.files[0].c_str()))\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ path = env_GetTempChildPath();\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ if (res.files.GetSize() == 2 && res.files[0] == res.files[1])\r
+ res.files[1].erase();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str())))\r
+ break;\r
+ if (res.files[0].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[0].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str());\r
+ res.files[0] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[0].insert(0, _T("\\"));\r
+ res.files[0].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[0].c_str()));\r
+ res.files[0] = path;\r
+ }\r
+ if (!res.files[1].empty() && (piHandler = ArchiveGuessFormat(res.files[1].c_str())))\r
+ {\r
+ if (!res.pTempPathContext)\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ }\r
+ path = env_GetTempChildPath();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str())))\r
+ break;;\r
+ if (res.files[1].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[1].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());\r
+ res.files[1] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[1].insert(0, _T("\\"));\r
+ res.files[1].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[1].c_str()));\r
+ res.files[1] = path;\r
+ }\r
+ if (res.files.GetSize() > 2 && (piHandler = ArchiveGuessFormat(res.files[2].c_str())))\r
+ {\r
+ if (!res.pTempPathContext)\r
+ {\r
+ res.pTempPathContext = new CTempPathContext;\r
+ for (int index = 0; index < res.files.GetSize(); index++)\r
+ res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];\r
+ }\r
+ path = env_GetTempChildPath();\r
+ do\r
+ {\r
+ if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str())))\r
+ break;;\r
+ if (res.files[2].find(path) == 0)\r
+ {\r
+ VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(string_format(_T("DeleteFile(%s) failed"), res.files[2].c_str())), false));\r
+ }\r
+ BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());\r
+ res.files[2] = OLE2T(pTmp);\r
+ SysFreeString(pTmp);\r
+ res.files[2].insert(0, _T("\\"));\r
+ res.files[2].insert(0, path);\r
+ } while (piHandler = ArchiveGuessFormat(res.files[2].c_str()));\r
+ res.files[2] = path;\r
+ }\r
+ if (res.files[1].empty())\r
+ {\r
+ // assume Perry style patch\r
+ res.files[1] = path;\r
+ res.files[0] += _T("\\ORIGINAL");\r
+ res.files[1] += _T("\\ALTERED");\r
+ if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str()))\r
+ {\r
+ // not a Perry style patch: diff with itself...\r
+ res.files[0] = res.files[1] = path;\r
+ }\r
+ else\r
+ {\r
+ res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL");\r
+ res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED");\r
+ }\r
+ }\r
+ }\r
+ catch (CException *e)\r
+ {\r
+ e->ReportError(MB_ICONSTOP);\r
+ e->Delete();\r
+ }\r
+ return res;\r
+}\r
+\r
+\r