1 /////////////////////////////////////////////////////////////////////////////
\r
2 // WinMerge: an interactive diff/merge utility
\r
3 // Copyright (C) 1997-2000 Thingamahoochie Software
\r
4 // Author: Dean Grimm
\r
5 // SPDX-License-Identifier: GPL-2.0-or-later
\r
7 /////////////////////////////////////////////////////////////////////////////
\r
9 /* 7zCommon.cpp: Implement 7z related classes and functions
\r
10 * Copyright (c) 2003 Jochen Tucht
\r
12 * Remarks: Different versions of 7-Zip are interfaced through specific
\r
13 * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)
\r
14 * WinMerge can either use an installed copy of the 7-Zip software,
\r
15 * or fallback to a local set of 7-Zip DLLs, which are to be included
\r
16 * in the WinMerge binary distribution.
\r
18 * Fallback policies are as follows:
\r
20 * 1. Detect 7-Zip version installed (XXX).
\r
21 * 2. If there is a Merge7zXXX.dll, be happy to use it
\r
22 * 3. Detect 7-Zip version from WinMerge distribution (YYY).
\r
23 * 4. If there is a Merge7zYYY.dll, be happy to use it.
\r
26 * These rules can be customized by setting a registry variable
\r
27 * *Merge7z/Enable* of type DWORD to one of the following values:
\r
29 * 0 - Entirely disable 7-Zip integration.
\r
30 * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.
\r
31 * 2 - Always use local 7-Zip.
\r
34 Please mind 2. a) of the GNU General Public License, and log your changes below.
\r
36 DATE: BY: DESCRIPTION:
\r
37 ========== ================== ================================================
\r
38 2003-12-09 Jochen Tucht Created
\r
39 2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2
\r
40 2003-12-16 Jochen Tucht Obtain long path to temporary folder
\r
41 2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing
\r
42 2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex
\r
43 2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue
\r
44 2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70
\r
45 2004-08-25 Jochen Tucht More explicit error message
\r
46 2004-10-17 Jochen Tucht Leave decision whether to recurse into folders
\r
47 to enumerator (Mask.Recurse)
\r
48 2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02
\r
49 2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml
\r
50 Set Merge7z UI language if DllBuild_Merge7z >= 9
\r
51 2005-01-22 Jochen Tucht Better explain what's present/missing/outdated
\r
52 2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from
\r
53 .exe if .lang file isn't up to date.
\r
54 2005-02-26 Jochen Tucht Add download link to error message
\r
55 2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes
\r
56 2005-02-27 Jochen Tucht FIX [1152375]
\r
57 2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView
\r
58 2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only
\r
59 temporarily, to sort out new directory compare)
\r
60 2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20
\r
61 Remove noise from Nagbox
\r
62 2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ]
\r
63 2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for
\r
64 formats to be handled by external command line
\r
65 tools. These take precedence over Merge7z
\r
67 2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to
\r
68 build intermediate filenames for multi-step
\r
70 2005-07-15 Jochen Tucht Remove external command line tool integration
\r
71 for now. Rethink about it after 2.4 branch.
\r
72 2005-08-20 Jochen Tucht Option to guess archive format by signature
\r
73 Map extensions through ExternalArchiveFormat.ini
\r
74 2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration
\r
75 2007-01-04 Kimmo Varis Convert using COptionsMgr for options.
\r
76 2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation...
\r
77 2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system
\r
78 Change recommended version of 7-Zip to 4.57
\r
79 2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long
\r
80 ago replaced the various format and codec DLLs)
\r
81 Change recommended version of 7-Zip to 4.65
\r
86 #include "7zCommon.h"
\r
87 #include <afxinet.h>
\r
88 #include "OptionsDef.h"
\r
89 #include "OptionsMgr.h"
\r
90 #include "DirView.h"
\r
92 #include "DirActions.h"
\r
93 //#include "ExternalArchiveFormat.h"
\r
94 #include "VersionInfo.h"
\r
96 #include "Environment.h"
\r
97 #include "Merge7zFormatRegister.h"
\r
100 #define new DEBUG_NEW
\r
104 * @brief Proxy for Merge7z
\r
106 static __declspec(thread) Merge7z::Proxy m_Merge7z =
\r
108 { 0, 0, DllBuild_Merge7z, },
\r
109 "Merge7z\\Merge7z.dll",
\r
114 std::vector<Merge7z::Format *(*)(const String& path)> Merge7zFormatRegister::optionalFormats;
\r
117 * @brief assign BSTR to String, and return BSTR for optional SysFreeString()
\r
119 inline BSTR Assign(CString &dst, BSTR src)
\r
125 bool IsArchiveFile(const String& pszFile)
\r
128 Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);
\r
129 if (piHandler != nullptr)
\r
134 catch (CException *e)
\r
142 * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:
\r
143 * - Check if 7-Zip integration is enabled.
\r
144 * - Check for filename extension mappings.
\r
146 Merge7z::Format *ArchiveGuessFormat(const String& path)
\r
148 if (!GetOptionsMgr()->GetBool(OPT_ARCHIVE_ENABLE))
\r
150 if (paths::IsDirectory(path))
\r
152 String path2(path);
\r
153 // Map extensions through ExternalArchiveFormat.ini
\r
154 static tchar_t null[] = _T("");
\r
155 static const tchar_t section[] = _T("extensions");
\r
156 String entry = paths::FindExtension(path);
\r
158 static const tchar_t* filename = nullptr;
\r
159 if (filename == nullptr)
\r
161 tchar_t cPath[INTERNET_MAX_PATH_LENGTH];
\r
162 DWORD cchPath = SearchPath(nullptr, _T("ExternalArchiveFormat.ini"), nullptr,
\r
163 INTERNET_MAX_PATH_LENGTH, cPath, nullptr);
\r
164 filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? tc::tcsdup(cPath) : null;
\r
167 GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&
\r
170 // Remove end-of-line comments (in string returned from GetPrivateProfileString)
\r
171 // that is, remove semicolon & whatever follows it
\r
172 if (tchar_t* p = StrChr(value, ';'))
\r
175 StrTrim(value, _T(" \t"));
\r
180 // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues
\r
181 // command line integration portion is not yet applied
\r
182 // so following code not yet valid, so temporarily commented out
\r
183 // Look for command line tool first
\r
184 /*Merge7z::Format *pFormat;
\r
185 if (CExternalArchiveFormat::GuessFormat(path, pFormat))
\r
189 // Default to Merge7z*.dll
\r
193 Merge7z::Format* pFormat = nullptr;
\r
194 if (!paths::IsURL(path2))
\r
195 pFormat = m_Merge7z->GuessFormat(path2.c_str());
\r
196 if (pFormat == nullptr)
\r
197 pFormat = Merge7zFormatRegister::GuessFormat(path2);
\r
202 Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2);
\r
203 if (pFormat != nullptr)
\r
210 * @brief Check whether archive support is available.
\r
212 int NTAPI HasZipSupport()
\r
214 static int HasZipSupport = -1;
\r
215 if (HasZipSupport == -1)
\r
219 m_Merge7z.operator->();
\r
222 catch (CException *e)
\r
228 return HasZipSupport;
\r
232 * @brief Delete head of temp path context list, and return its parent context.
\r
234 CTempPathContext *CTempPathContext::DeleteHead()
\r
236 CTempPathContext *pParent = m_pParent;
\r
241 void CTempPathContext::Swap(int idx1, int idx2)
\r
243 std::swap(m_strDisplayRoot[idx1], m_strDisplayRoot[idx2]);
\r
244 std::swap(m_strRoot[idx1], m_strRoot[idx2]);
\r
245 if (m_pParent != nullptr)
\r
246 m_pParent->Swap(idx1, idx2);
\r
250 * @brief Return installed or local version of 7-Zip.
\r
252 DWORD NTAPI VersionOf7z()
\r
254 String path = paths::ConcatPath(env::GetProgPath(), _T("Merge7z\\7z.dll"));
\r
255 unsigned versionMS = 0;
\r
256 unsigned versionLS = 0;
\r
257 CVersionInfo(path.c_str()).GetFixedFileVersion(versionMS, versionLS);
\r
262 * @brief Access dll functions through proxy.
\r
264 interface Merge7z *Merge7z::Proxy::operator->()
\r
266 // As long as the Merge7z*.DLL has not yet been loaded, Merge7z
\r
267 // [0] points to the name of the DLL (with placeholders for 7-
\r
268 // Zip major and minor version numbers). Once the DLL has been
\r
269 // loaded successfully, Merge7z[0] is set to nullptr, causing the
\r
270 // if to fail on subsequent calls.
\r
272 if (const char *format = Merge7z[0])
\r
274 // Merge7z has not yet been loaded
\r
276 if (!GetOptionsMgr()->GetBool(OPT_ARCHIVE_ENABLE))
\r
277 throw new CResourceException();
\r
278 if (DWORD ver = VersionOf7z())
\r
280 Merge7z[0] = format;
\r
285 throw new CResourceException();
\r
287 LANGID wLangID = (LANGID)GetThreadLocale();
\r
288 DWORD flags = Initialize::Default | Initialize::Local7z | (wLangID << 16);
\r
289 if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))
\r
291 flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;
\r
294 ((interface Merge7z *)Merge7z[1])->Initialize(flags);
\r
296 return ((interface Merge7z *)Merge7z[1]);
\r
300 * @brief Tell Merge7z we are going to enumerate just 1 item.
\r
302 UINT SingleItemEnumerator::Open()
\r
308 * @brief Pass information about the item to Merge7z.
\r
310 Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)
\r
312 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;
\r
314 item.FullPath = FullPath;
\r
319 * @brief SingleFileEnumerator constructor.
\r
321 SingleItemEnumerator::SingleItemEnumerator(const tchar_t* path, const tchar_t* FullPath, const tchar_t* Name)
\r
322 : FullPath(FullPath)
\r
328 * @brief Construct a DirItemEnumerator.
\r
330 * Argument *nFlags* controls operation as follows:
\r
331 * LVNI_ALL: Enumerate all items.
\r
332 * LVNI_SELECTED: Enumerate selected items only.
\r
333 * Original: Set folder prefix for first iteration to "original"
\r
334 * Altered: Set folder prefix for second iteration to "altered"
\r
335 * BalanceFolders: Ensure that all nonempty folders on either side have a
\r
336 * corresponding folder on the other side, even if it is
\r
337 * empty (DirScan doesn't recurse into folders which
\r
338 * appear only on one side).
\r
339 * DiffsOnly: Enumerate diffs only.
\r
341 DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)
\r
345 if (m_nFlags & Original)
\r
347 m_rgFolderPrefix.push_back(_T("original"));
\r
349 if (m_nFlags & Altered)
\r
351 m_rgFolderPrefix.push_back(_T("altered"));
\r
353 if (m_nFlags & BalanceFolders)
\r
355 const CDiffContext& ctxt = pView->GetDiffContext();
\r
356 // Collect implied folders
\r
357 for (UINT i = Open() ; i-- ; )
\r
359 const DIFFITEM &di = Next();
\r
360 if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))
\r
364 // Enumerating items
\r
365 if (di.diffcode.exists(m_index))
\r
367 // Item is present on right side, i.e. folder is implied
\r
368 m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);
\r
375 * @brief Initialize enumerator, return number of items to be enumerated.
\r
377 UINT DirItemEnumerator::Open()
\r
380 m_curFolderPrefix = m_rgFolderPrefix.begin();
\r
381 if (m_pView->GetDocument()->m_nDirs < 3)
\r
382 m_index = (m_nFlags & Right) != 0 ? 1 : 0;
\r
384 m_index = ((m_nFlags & Right) != 0) ? 2 : ((m_nFlags & Middle) != 0 ? 1 : 0);
\r
385 size_t nrgFolderPrefix = m_rgFolderPrefix.size();
\r
386 if (nrgFolderPrefix)
\r
388 m_strFolderPrefix = *m_curFolderPrefix++;
\r
392 nrgFolderPrefix = 1;
\r
395 static_cast<UINT>((
\r
396 (m_nFlags & LVNI_SELECTED)
\r
397 ? pView(m_pView)->GetSelectedCount()
\r
398 : pView(m_pView)->GetItemCount()
\r
399 ) * nrgFolderPrefix);
\r
403 * @brief Return next item.
\r
405 const DIFFITEM &DirItemEnumerator::Next()
\r
407 enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};
\r
408 while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)
\r
410 m_strFolderPrefix = *m_curFolderPrefix++;
\r
413 const auto& di = m_pView->GetDiffItem(m_nIndex);
\r
414 for (const auto* pfdi : m_selectedFolderDiffItems)
\r
416 if (di.IsAncestor(pfdi))
\r
417 return *DIFFITEM::GetEmptyItem();
\r
419 if (di.diffcode.isDirectory())
\r
420 m_selectedFolderDiffItems.push_back(&di);
\r
425 * @brief Pass information about an item to Merge7z.
\r
427 * Information is passed through struct Merge7z::DirItemEnumerator::Item.
\r
428 * The *mask* member denotes which of the other members contain valid data.
\r
429 * If *mask* is zero upon return, which will be the case if Enum() decides to
\r
430 * leave the struct untouched, Merge7z will ignore the item.
\r
431 * If Enum() allocates temporary storage for string members, it must also
\r
432 * allocate an Envelope, providing a Free() method to free the temporary
\r
433 * storage, along with the Envelope itself. The Envelope pointer is passed to
\r
434 * Merge7z as the return value of the function. It is not meant to be a success
\r
435 * indicator, so if no temporary storage is required, it is perfectly alright
\r
436 * to return `nullptr`.
\r
438 Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)
\r
440 const CDiffContext& ctxt = m_pView->GetDiffContext();
\r
441 const DIFFITEM &di = Next();
\r
443 if (di.isEmpty() || ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di)))
\r
448 bool isSideOnly = !di.diffcode.exists(m_index);
\r
450 Envelope *envelope = new Envelope;
\r
452 const String &sFilename = di.diffFileInfo[m_index].filename;
\r
453 const String &sSubdir = di.diffFileInfo[m_index].path;
\r
454 if (sSubdir.length())
\r
455 envelope->Name = paths::ConcatPath(sSubdir, sFilename);
\r
457 envelope->Name = sFilename;
\r
458 envelope->FullPath = paths::ConcatPath(
\r
459 di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),
\r
462 UINT32 Recurse = item.Mask.Recurse;
\r
464 if (m_nFlags & BalanceFolders)
\r
466 // Enumerating items on right side
\r
469 // Item is missing on right side
\r
470 PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];
\r
471 if (implied == nullptr)
\r
473 // Folder is not implied by some other file, and has
\r
474 // not been enumerated so far, so enumerate it now!
\r
475 envelope->Name = di.diffFileInfo[1-m_index].path;
\r
476 envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));
\r
477 implied = PVOID(2); // Don't enumerate same folder twice!
\r
478 isSideOnly = false;
\r
489 if (m_strFolderPrefix.length())
\r
491 if (envelope->Name.length())
\r
492 envelope->Name.insert(0, _T("\\"));
\r
493 envelope->Name.insert(0, m_strFolderPrefix);
\r
496 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;
\r
497 item.Name = envelope->Name.c_str();
\r
498 item.FullPath = envelope->FullPath.c_str();
\r
503 * @brief Apply appropriate handlers from left to right.
\r
505 bool DirItemEnumerator::MultiStepCompressArchive(const tchar_t* path)
\r
508 Merge7z::Format *piHandler = ArchiveGuessFormat(path);
\r
509 if (piHandler != nullptr)
\r
511 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
\r
512 CString pathIntermediate;
\r
513 SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));
\r
514 String pathPrepend = path;
\r
515 pathPrepend.resize(pathPrepend.rfind('\\') + 1);
\r
516 pathIntermediate.Insert(0, pathPrepend.c_str());
\r
517 bool bDone = MultiStepCompressArchive(pathIntermediate);
\r
520 SingleItemEnumerator tmpEnumerator(path, pathIntermediate);
\r
521 piHandler->CompressArchive(hwndOwner, path, &tmpEnumerator);
\r
522 DeleteFile(pathIntermediate);
\r
526 piHandler->CompressArchive(hwndOwner, path, this);
\r
534 * @brief Generate archive from DirView items.
\r
536 void DirItemEnumerator::CompressArchive(const tchar_t* path)
\r
539 if (path == nullptr)
\r
541 // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't
\r
542 // offer other formats here!
\r
543 static const tchar_t _T_Filter[]
\r
548 _T("jar (zip)|*.jar|")
\r
549 _T("ear (zip)|*.ear|")
\r
550 _T("war (zip)|*.war|")
\r
551 _T("xpi (zip)|*.xpi|")
\r
554 _T("tar.z|*.tar.z|")
\r
555 _T("tar.gz|*.tar.gz|")
\r
556 _T("tar.bz2|*.tar.bz2|")
\r
565 //_T("cpio|*.cpio|")
\r
568 String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();
\r
569 strFilter.insert(0, _T_Filter);
\r
570 strFilter += _T("|");
\r
576 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,
\r
579 dlg.m_ofn.nFilterIndex = GetOptionsMgr()->GetInt(OPT_ARCHIVE_FILTER_INDEX);
\r
580 // Use extension from current filter as default extension:
\r
581 if (int i = dlg.m_ofn.nFilterIndex)
\r
583 dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;
\r
584 while (*dlg.m_ofn.lpstrDefExt && --i)
\r
586 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
587 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
589 if (*dlg.m_ofn.lpstrDefExt)
\r
591 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;
\r
594 if (dlg.DoModal() == IDOK)
\r
596 strPath = dlg.GetPathName();
\r
597 path = strPath.c_str();
\r
598 GetOptionsMgr()->SaveOption(OPT_ARCHIVE_FILTER_INDEX, static_cast<int>(dlg.m_ofn.nFilterIndex));
\r
601 if (path && !MultiStepCompressArchive(path))
\r
603 LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);
\r
608 DecompressResult DecompressArchive(HWND hWnd, const PathContext& files)
\r
610 DecompressResult res(files, nullptr, paths::IS_EXISTING_DIR);
\r
615 // Handle archives using 7-zip
\r
616 Merge7z::Format *piHandler;
\r
617 piHandler = ArchiveGuessFormat(res.files[0]);
\r
618 if (piHandler != nullptr)
\r
620 res.pTempPathContext = new CTempPathContext;
\r
621 path = env::GetTempChildPath();
\r
622 for (int index = 0; index < res.files.GetSize(); index++)
\r
623 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
624 if (res.files.GetSize() == 2 && res.files[0] == res.files[1])
\r
625 res.files[1].erase();
\r
628 hr = piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str());
\r
634 if (res.files[0].find(path) == 0)
\r
636 VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[0])), false));
\r
638 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str());
\r
639 res.files[0] = ucr::toTString(pTmp);
\r
640 SysFreeString(pTmp);
\r
641 res.files[0].insert(0, _T("\\"));
\r
642 res.files[0].insert(0, path);
\r
643 piHandler = ArchiveGuessFormat(res.files[0]);
\r
644 } while (piHandler != nullptr);
\r
645 res.files[0] = path;
\r
647 piHandler = res.files[1].empty() ? nullptr
\r
648 : ArchiveGuessFormat(res.files[1]);
\r
649 if (piHandler != nullptr)
\r
651 if (res.pTempPathContext == nullptr)
\r
653 res.pTempPathContext = new CTempPathContext;
\r
654 for (int index = 0; index < res.files.GetSize(); index++)
\r
655 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
657 path = env::GetTempChildPath();
\r
660 hr = piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str());
\r
666 if (res.files[1].find(path) == 0)
\r
668 VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[1])), false));
\r
670 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());
\r
671 res.files[1] = OLE2T(pTmp);
\r
672 SysFreeString(pTmp);
\r
673 res.files[1].insert(0, _T("\\"));
\r
674 res.files[1].insert(0, path);
\r
675 piHandler = ArchiveGuessFormat(res.files[1]);
\r
676 } while (piHandler != nullptr);
\r
677 res.files[1] = path;
\r
679 piHandler = (res.files.GetSize() <= 2) ? nullptr : ArchiveGuessFormat(res.files[2]);
\r
680 if (piHandler != nullptr)
\r
682 if (res.pTempPathContext == nullptr)
\r
684 res.pTempPathContext = new CTempPathContext;
\r
685 for (int index = 0; index < res.files.GetSize(); index++)
\r
686 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
688 path = env::GetTempChildPath();
\r
691 hr = piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str());
\r
697 if (res.files[2].find(path) == 0)
\r
699 VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[2])), false));
\r
701 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[2].c_str());
\r
702 res.files[2] = OLE2T(pTmp);
\r
703 SysFreeString(pTmp);
\r
704 res.files[2].insert(0, _T("\\"));
\r
705 res.files[2].insert(0, path);
\r
706 piHandler = ArchiveGuessFormat(res.files[2]);
\r
707 } while (piHandler != nullptr);
\r
708 res.files[2] = path;
\r
710 if (res.files[1].empty())
\r
712 // assume Perry style patch
\r
713 res.files[1] = path;
\r
714 res.files[0] += _T("\\ORIGINAL");
\r
715 res.files[1] += _T("\\ALTERED");
\r
716 if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str()))
\r
718 // not a Perry style patch: diff with itself...
\r
719 res.files[0] = path;
\r
720 res.files[1] = std::move(path);
\r
724 res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL");
\r
725 res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED");
\r
729 catch (CException *e)
\r