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()->GetInt(OPT_ARCHIVE_ENABLE) == 0)
\r
150 if (paths::IsDirectory(path))
\r
152 String path2(path);
\r
153 // Map extensions through ExternalArchiveFormat.ini
\r
154 static TCHAR null[] = _T("");
\r
155 static const TCHAR section[] = _T("extensions");
\r
156 String entry = paths::FindExtension(path);
\r
158 static LPCTSTR filename = nullptr;
\r
159 if (filename == nullptr)
\r
161 TCHAR 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 ? _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 (LPTSTR 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 = m_Merge7z->GuessFormat(path2.c_str());
\r
194 if (pFormat == nullptr)
\r
195 pFormat = Merge7zFormatRegister::GuessFormat(path2);
\r
200 Merge7z::Format *pFormat = Merge7zFormatRegister::GuessFormat(path2);
\r
201 if (pFormat != nullptr)
\r
208 * @brief Check whether archive support is available.
\r
210 int NTAPI HasZipSupport()
\r
212 static int HasZipSupport = -1;
\r
213 if (HasZipSupport == -1)
\r
217 m_Merge7z.operator->();
\r
220 catch (CException *e)
\r
226 return HasZipSupport;
\r
230 * @brief Delete head of temp path context list, and return its parent context.
\r
232 CTempPathContext *CTempPathContext::DeleteHead()
\r
234 CTempPathContext *pParent = m_pParent;
\r
239 void CTempPathContext::Swap(int idx1, int idx2)
\r
241 std::swap(m_strDisplayRoot[idx1], m_strDisplayRoot[idx2]);
\r
242 std::swap(m_strRoot[idx1], m_strRoot[idx2]);
\r
243 if (m_pParent != nullptr)
\r
244 m_pParent->Swap(idx1, idx2);
\r
248 * @brief Return installed or local version of 7-Zip.
\r
250 DWORD NTAPI VersionOf7z()
\r
252 String path = paths::ConcatPath(env::GetProgPath(), _T("Merge7z\\7z.dll"));
\r
253 unsigned versionMS = 0;
\r
254 unsigned versionLS = 0;
\r
255 CVersionInfo(path.c_str()).GetFixedFileVersion(versionMS, versionLS);
\r
260 * @brief Access dll functions through proxy.
\r
262 interface Merge7z *Merge7z::Proxy::operator->()
\r
264 // As long as the Merge7z*.DLL has not yet been loaded, Merge7z
\r
265 // [0] points to the name of the DLL (with placeholders for 7-
\r
266 // Zip major and minor version numbers). Once the DLL has been
\r
267 // loaded successfully, Merge7z[0] is set to nullptr, causing the
\r
268 // if to fail on subsequent calls.
\r
270 if (const char *format = Merge7z[0])
\r
272 // Merge7z has not yet been loaded
\r
274 if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)
\r
275 throw new CResourceException();
\r
276 if (DWORD ver = VersionOf7z())
\r
278 Merge7z[0] = format;
\r
283 throw new CResourceException();
\r
285 LANGID wLangID = (LANGID)GetThreadLocale();
\r
286 DWORD flags = Initialize::Default | Initialize::Local7z | (wLangID << 16);
\r
287 if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))
\r
289 flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;
\r
292 ((interface Merge7z *)Merge7z[1])->Initialize(flags);
\r
294 return ((interface Merge7z *)Merge7z[1]);
\r
298 * @brief Tell Merge7z we are going to enumerate just 1 item.
\r
300 UINT SingleItemEnumerator::Open()
\r
306 * @brief Pass information about the item to Merge7z.
\r
308 Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)
\r
310 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;
\r
312 item.FullPath = FullPath;
\r
317 * @brief SingleFileEnumerator constructor.
\r
319 SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)
\r
320 : FullPath(FullPath)
\r
326 * @brief Construct a DirItemEnumerator.
\r
328 * Argument *nFlags* controls operation as follows:
\r
329 * LVNI_ALL: Enumerate all items.
\r
330 * LVNI_SELECTED: Enumerate selected items only.
\r
331 * Original: Set folder prefix for first iteration to "original"
\r
332 * Altered: Set folder prefix for second iteration to "altered"
\r
333 * BalanceFolders: Ensure that all nonempty folders on either side have a
\r
334 * corresponding folder on the other side, even if it is
\r
335 * empty (DirScan doesn't recurse into folders which
\r
336 * appear only on one side).
\r
337 * DiffsOnly: Enumerate diffs only.
\r
339 DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)
\r
343 if (m_nFlags & Original)
\r
345 m_rgFolderPrefix.push_back(_T("original"));
\r
347 if (m_nFlags & Altered)
\r
349 m_rgFolderPrefix.push_back(_T("altered"));
\r
351 if (m_nFlags & BalanceFolders)
\r
353 const CDiffContext& ctxt = pView->GetDiffContext();
\r
354 // Collect implied folders
\r
355 for (UINT i = Open() ; i-- ; )
\r
357 const DIFFITEM &di = Next();
\r
358 if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))
\r
362 // Enumerating items
\r
363 if (di.diffcode.exists(m_index))
\r
365 // Item is present on right side, i.e. folder is implied
\r
366 m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);
\r
373 * @brief Initialize enumerator, return number of items to be enumerated.
\r
375 UINT DirItemEnumerator::Open()
\r
378 m_curFolderPrefix = m_rgFolderPrefix.begin();
\r
379 if (m_pView->GetDocument()->m_nDirs < 3)
\r
380 m_index = (m_nFlags & Right) != 0 ? 1 : 0;
\r
382 m_index = ((m_nFlags & Right) != 0) ? 2 : ((m_nFlags & Middle) != 0 ? 1 : 0);
\r
383 size_t nrgFolderPrefix = m_rgFolderPrefix.size();
\r
384 if (nrgFolderPrefix)
\r
386 m_strFolderPrefix = *m_curFolderPrefix++;
\r
390 nrgFolderPrefix = 1;
\r
393 static_cast<UINT>((
\r
394 (m_nFlags & LVNI_SELECTED)
\r
395 ? pView(m_pView)->GetSelectedCount()
\r
396 : pView(m_pView)->GetItemCount()
\r
397 ) * nrgFolderPrefix);
\r
401 * @brief Return next item.
\r
403 const DIFFITEM &DirItemEnumerator::Next()
\r
405 enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};
\r
406 while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)
\r
408 m_strFolderPrefix = *m_curFolderPrefix++;
\r
411 return m_pView->GetDiffItem(m_nIndex);
\r
415 * @brief Pass information about an item to Merge7z.
\r
417 * Information is passed through struct Merge7z::DirItemEnumerator::Item.
\r
418 * The *mask* member denotes which of the other members contain valid data.
\r
419 * If *mask* is zero upon return, which will be the case if Enum() decides to
\r
420 * leave the struct untouched, Merge7z will ignore the item.
\r
421 * If Enum() allocates temporary storage for string members, it must also
\r
422 * allocate an Envelope, providing a Free() method to free the temporary
\r
423 * storage, along with the Envelope itself. The Envelope pointer is passed to
\r
424 * Merge7z as the return value of the function. It is not meant to be a success
\r
425 * indicator, so if no temporary storage is required, it is perfectly alright
\r
426 * to return `nullptr`.
\r
428 Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)
\r
430 const CDiffContext& ctxt = m_pView->GetDiffContext();
\r
431 const DIFFITEM &di = Next();
\r
433 if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))
\r
438 bool isSideOnly = !di.diffcode.exists(m_index);
\r
440 Envelope *envelope = new Envelope;
\r
442 const String &sFilename = di.diffFileInfo[m_index].filename;
\r
443 const String &sSubdir = di.diffFileInfo[m_index].path;
\r
444 if (sSubdir.length())
\r
445 envelope->Name = paths::ConcatPath(sSubdir, sFilename);
\r
447 envelope->Name = sFilename;
\r
448 envelope->FullPath = paths::ConcatPath(
\r
449 di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),
\r
452 UINT32 Recurse = item.Mask.Recurse;
\r
454 if (m_nFlags & BalanceFolders)
\r
456 // Enumerating items on right side
\r
459 // Item is missing on right side
\r
460 PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];
\r
461 if (implied == nullptr)
\r
463 // Folder is not implied by some other file, and has
\r
464 // not been enumerated so far, so enumerate it now!
\r
465 envelope->Name = di.diffFileInfo[1-m_index].path;
\r
466 envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));
\r
467 implied = PVOID(2); // Don't enumerate same folder twice!
\r
468 isSideOnly = false;
\r
479 if (m_strFolderPrefix.length())
\r
481 if (envelope->Name.length())
\r
482 envelope->Name.insert(0, _T("\\"));
\r
483 envelope->Name.insert(0, m_strFolderPrefix);
\r
486 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;
\r
487 item.Name = envelope->Name.c_str();
\r
488 item.FullPath = envelope->FullPath.c_str();
\r
493 * @brief Apply appropriate handlers from left to right.
\r
495 bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)
\r
498 Merge7z::Format *piHandler = ArchiveGuessFormat(path);
\r
499 if (piHandler != nullptr)
\r
501 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
\r
502 CString pathIntermediate;
\r
503 SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));
\r
504 String pathPrepend = path;
\r
505 pathPrepend.resize(pathPrepend.rfind('\\') + 1);
\r
506 pathIntermediate.Insert(0, pathPrepend.c_str());
\r
507 bool bDone = MultiStepCompressArchive(pathIntermediate);
\r
510 SingleItemEnumerator tmpEnumerator(path, pathIntermediate);
\r
511 piHandler->CompressArchive(hwndOwner, path, &tmpEnumerator);
\r
512 DeleteFile(pathIntermediate);
\r
516 piHandler->CompressArchive(hwndOwner, path, this);
\r
524 * @brief Generate archive from DirView items.
\r
526 void DirItemEnumerator::CompressArchive(LPCTSTR path)
\r
529 if (path == nullptr)
\r
531 // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't
\r
532 // offer other formats here!
\r
533 static const TCHAR _T_Filter[]
\r
538 _T("jar (zip)|*.jar|")
\r
539 _T("ear (zip)|*.ear|")
\r
540 _T("war (zip)|*.war|")
\r
541 _T("xpi (zip)|*.xpi|")
\r
544 _T("tar.z|*.tar.z|")
\r
545 _T("tar.gz|*.tar.gz|")
\r
546 _T("tar.bz2|*.tar.bz2|")
\r
555 //_T("cpio|*.cpio|")
\r
558 String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();
\r
559 strFilter.insert(0, _T_Filter);
\r
560 strFilter += _T("|");
\r
566 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,
\r
569 dlg.m_ofn.nFilterIndex = GetOptionsMgr()->GetInt(OPT_ARCHIVE_FILTER_INDEX);
\r
570 // Use extension from current filter as default extension:
\r
571 if (int i = dlg.m_ofn.nFilterIndex)
\r
573 dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;
\r
574 while (*dlg.m_ofn.lpstrDefExt && --i)
\r
576 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
577 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
579 if (*dlg.m_ofn.lpstrDefExt)
\r
581 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;
\r
584 if (dlg.DoModal() == IDOK)
\r
586 strPath = dlg.GetPathName();
\r
587 path = strPath.c_str();
\r
588 GetOptionsMgr()->SaveOption(OPT_ARCHIVE_FILTER_INDEX, static_cast<int>(dlg.m_ofn.nFilterIndex));
\r
591 if (path && !MultiStepCompressArchive(path))
\r
593 LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);
\r
598 DecompressResult DecompressArchive(HWND hWnd, const PathContext& files)
\r
600 DecompressResult res(files, nullptr, paths::IS_EXISTING_DIR);
\r
605 // Handle archives using 7-zip
\r
606 Merge7z::Format *piHandler;
\r
607 piHandler = ArchiveGuessFormat(res.files[0]);
\r
608 if (piHandler != nullptr)
\r
610 res.pTempPathContext = new CTempPathContext;
\r
611 path = env::GetTempChildPath();
\r
612 for (int index = 0; index < res.files.GetSize(); index++)
\r
613 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
614 if (res.files.GetSize() == 2 && res.files[0] == res.files[1])
\r
615 res.files[1].erase();
\r
618 if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[0].c_str(), path.c_str())))
\r
620 if (res.files[0].find(path) == 0)
\r
622 VERIFY(::DeleteFile(res.files[0].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[0])), false));
\r
624 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[0].c_str());
\r
625 res.files[0] = OLE2T(pTmp);
\r
626 SysFreeString(pTmp);
\r
627 res.files[0].insert(0, _T("\\"));
\r
628 res.files[0].insert(0, path);
\r
629 piHandler = ArchiveGuessFormat(res.files[0]);
\r
630 } while (piHandler != nullptr);
\r
631 res.files[0] = path;
\r
633 piHandler = res.files[1].empty() ? nullptr
\r
634 : ArchiveGuessFormat(res.files[1]);
\r
635 if (piHandler != nullptr)
\r
637 if (res.pTempPathContext == nullptr)
\r
639 res.pTempPathContext = new CTempPathContext;
\r
640 for (int index = 0; index < res.files.GetSize(); index++)
\r
641 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
643 path = env::GetTempChildPath();
\r
646 if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[1].c_str(), path.c_str())))
\r
648 if (res.files[1].find(path) == 0)
\r
650 VERIFY(::DeleteFile(res.files[1].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[1])), false));
\r
652 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[1].c_str());
\r
653 res.files[1] = OLE2T(pTmp);
\r
654 SysFreeString(pTmp);
\r
655 res.files[1].insert(0, _T("\\"));
\r
656 res.files[1].insert(0, path);
\r
657 piHandler = ArchiveGuessFormat(res.files[1]);
\r
658 } while (piHandler != nullptr);
\r
659 res.files[1] = path;
\r
661 piHandler = (res.files.GetSize() <= 2) ? nullptr : ArchiveGuessFormat(res.files[2]);
\r
662 if (piHandler != nullptr)
\r
664 if (res.pTempPathContext == nullptr)
\r
666 res.pTempPathContext = new CTempPathContext;
\r
667 for (int index = 0; index < res.files.GetSize(); index++)
\r
668 res.pTempPathContext->m_strDisplayRoot[index] = res.files[index];
\r
670 path = env::GetTempChildPath();
\r
673 if (FAILED(piHandler->DeCompressArchive(hWnd, res.files[2].c_str(), path.c_str())))
\r
675 if (res.files[2].find(path) == 0)
\r
677 VERIFY(::DeleteFile(res.files[2].c_str()) || (LogErrorString(strutils::format(_T("DeleteFile(%s) failed"), res.files[2])), false));
\r
679 BSTR pTmp = piHandler->GetDefaultName(hWnd, res.files[2].c_str());
\r
680 res.files[2] = OLE2T(pTmp);
\r
681 SysFreeString(pTmp);
\r
682 res.files[2].insert(0, _T("\\"));
\r
683 res.files[2].insert(0, path);
\r
684 piHandler = ArchiveGuessFormat(res.files[2]);
\r
685 } while (piHandler != nullptr);
\r
686 res.files[2] = path;
\r
688 if (res.files[1].empty())
\r
690 // assume Perry style patch
\r
691 res.files[1] = path;
\r
692 res.files[0] += _T("\\ORIGINAL");
\r
693 res.files[1] += _T("\\ALTERED");
\r
694 if (!PathFileExists(res.files[0].c_str()) || !PathFileExists(res.files[1].c_str()))
\r
696 // not a Perry style patch: diff with itself...
\r
697 res.files[0] = res.files[1] = path;
\r
701 res.pTempPathContext->m_strDisplayRoot[0] += _T("\\ORIGINAL");
\r
702 res.pTempPathContext->m_strDisplayRoot[1] += _T("\\ALTERED");
\r
706 catch (CException *e)
\r