OSDN Git Service

Merge with stable
authorsdottaka <sdottaka@users.sourceforge.net>
Sun, 2 Aug 2015 09:10:51 +0000 (18:10 +0900)
committersdottaka <sdottaka@users.sourceforge.net>
Sun, 2 Aug 2015 09:10:51 +0000 (18:10 +0900)
1  2 
Src/7zCommon.cpp
Src/ImgMergeFrm.cpp
Src/OptionsDef.h
Src/OptionsInit.cpp

 -/////////////////////////////////////////////////////////////////////////////
 -//    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
Simple merge
Simple merge
Simple merge