-/////////////////////////////////////////////////////////////////////////////
-// 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"
-#include "paths.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\\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 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 Delete head of temp path context list, and return its parent context.
- */
-CTempPathContext *CTempPathContext::DeleteHead()
-{
- CTempPathContext *pParent = m_pParent;
- delete this;
- return pParent;
-}
-
-/**
- * @brief Return installed or local version of 7-Zip.
- */
-DWORD NTAPI VersionOf7z()
-{
- String path = paths_ConcatPath(env_GetProgPath(), _T("Merge7z\\7z.dll"));
- unsigned versionMS = 0;
- unsigned versionLS = 0;
- CVersionInfo(path.c_str()).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
-
- if (!GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))
- throw new CResourceException();
- if (DWORD ver = VersionOf7z())
- {
- char name[MAX_PATH];
- wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));
- Merge7z[0] = name;
- stub.Load();
- }
- else
- {
- throw new CResourceException();
- }
- LANGID wLangID = (LANGID)GetThreadLocale();
- DWORD flags = Initialize::Default | Initialize::Local7z | (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
- static_cast<UINT>((
- 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;
- if (sSubdir.length())
- envelope->Name = paths_ConcatPath(sSubdir, sFilename);
- else
- envelope->Name = sFilename;
- envelope->FullPath = paths_ConcatPath(m_bRight ?
- di.getFilepath(1, pDoc->GetBasePath(1)) : di.getFilepath(0, pDoc->GetBasePath(0)),
- sFilename);
-
- 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;
- size_t 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->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
+\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\\Merge7z%u%02u"DECORATE_U".dll",\r
++ "Merge7z\\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 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 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
+/**\r
+ * @brief Return installed or local version of 7-Zip.\r
+ */\r
+DWORD NTAPI VersionOf7z()\r
+{\r
+ String path = paths_ConcatPath(env_GetProgPath(), _T("Merge7z\\7z.dll"));\r
+ unsigned versionMS = 0;\r
+ unsigned versionLS = 0;\r
+ CVersionInfo(path.c_str()).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
+ if (!GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))\r
+ throw new CResourceException();\r
+ if (DWORD ver = VersionOf7z())\r
+ {\r
+ char name[MAX_PATH];\r
+ wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));\r
+ Merge7z[0] = name;\r
+ stub.Load();\r
+ }\r
+ else\r
+ {\r
+ throw new CResourceException();\r
+ }\r
+ LANGID wLangID = (LANGID)GetThreadLocale();\r
+ DWORD flags = Initialize::Default | Initialize::Local7z | (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
+ static_cast<UINT>((\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->Delete();\r
+ }\r
+ return res;\r
+}\r
+\r
+\r