1 /////////////////////////////////////////////////////////////////////////////
\r
2 // WinMerge: an interactive diff/merge utility
\r
3 // Copyright (C) 1997-2000 Thingamahoochie Software
\r
4 // Author: Dean Grimm
\r
6 // This program is free software; you can redistribute it and/or modify
\r
7 // it under the terms of the GNU General Public License as published by
\r
8 // the Free Software Foundation; either version 2 of the License, or
\r
9 // (at your option) any later version.
\r
11 // This program is distributed in the hope that it will be useful,
\r
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 // GNU General Public License for more details.
\r
16 // You should have received a copy of the GNU General Public License
\r
17 // along with this program; if not, write to the Free Software
\r
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\r
20 /////////////////////////////////////////////////////////////////////////////
\r
22 /* 7zCommon.cpp: Implement 7z related classes and functions
\r
23 * Copyright (c) 2003 Jochen Tucht
\r
25 * Remarks: Different versions of 7-Zip are interfaced through specific
\r
26 * versions of Merge7z (Merge7z311.dll, Merge7z312.dll, etc.)
\r
27 * WinMerge can either use an installed copy of the 7-Zip software,
\r
28 * or fallback to a local set of 7-Zip DLLs, which are to be included
\r
29 * in the WinMerge binary distribution.
\r
31 * Fallback policies are as follows:
\r
33 * 1. Detect 7-Zip version installed (XXX).
\r
34 * 2. If there is a Merge7zXXX.dll, be happy to use it
\r
35 * 3. Detect 7-Zip version from WinMerge distribution (YYY).
\r
36 * 4. If there is a Merge7zYYY.dll, be happy to use it.
\r
39 * These rules can be customized by setting a registry variable
\r
40 * *Merge7z/Enable* of type DWORD to one of the following values:
\r
42 * 0 - Entirely disable 7-Zip integration.
\r
43 * 1 - Use installed 7-Zip if present. Otherwise, use local 7-Zip.
\r
44 * 2 - Always use local 7-Zip.
\r
47 Please mind 2. a) of the GNU General Public License, and log your changes below.
\r
49 DATE: BY: DESCRIPTION:
\r
50 ========== ================== ================================================
\r
51 2003-12-09 Jochen Tucht Created
\r
52 2003-12-16 Jochen Tucht Properly generate .tar.gz and .tar.bz2
\r
53 2003-12-16 Jochen Tucht Obtain long path to temporary folder
\r
54 2004-01-20 Jochen Tucht Complain only once if Merge7z*.dll is missing
\r
55 2004-01-25 Jochen Tucht Fix bad default for OPENFILENAME::nFilterIndex
\r
56 2004-03-15 Jochen Tucht Fix Visual Studio 2003 build issue
\r
57 2004-04-13 Jochen Tucht Avoid StrNCat to get away with shlwapi 4.70
\r
58 2004-08-25 Jochen Tucht More explicit error message
\r
59 2004-10-17 Jochen Tucht Leave decision whether to recurse into folders
\r
60 to enumerator (Mask.Recurse)
\r
61 2004-11-03 Jochen Tucht FIX [1048997] as proposed by Kimmo 2004-11-02
\r
62 2005-01-15 Jochen Tucht Read 7-Zip version from 7zip_pad.xml
\r
63 Set Merge7z UI language if DllBuild_Merge7z >= 9
\r
64 2005-01-22 Jochen Tucht Better explain what's present/missing/outdated
\r
65 2005-02-05 Jochen Tucht Fall back to IDD_MERGE7ZMISMATCH template from
\r
66 .exe if .lang file isn't up to date.
\r
67 2005-02-26 Jochen Tucht Add download link to error message
\r
68 2005-02-26 Jochen Tucht Use WinAPI to obtain ISO language/region codes
\r
69 2005-02-27 Jochen Tucht FIX [1152375]
\r
70 2005-04-24 Kimmo Varis Don't use DiffContext exported from DirView
\r
71 2005-06-08 Kimmo Varis Use DIFFITEM, not reference to it (hopefully only
\r
72 temporarily, to sort out new directory compare)
\r
73 2005-06-22 Jochen Tucht Change recommended version of 7-Zip to 4.20
\r
74 Remove noise from Nagbox
\r
75 2005-07-03 Jochen Tucht DIFFITEM has changed due to RFE [ 1205516 ]
\r
76 2005-07-04 Jochen Tucht New global ArchiveGuessFormat() checks for
\r
77 formats to be handled by external command line
\r
78 tools. These take precedence over Merge7z
\r
80 2005-07-05 Jochen Tucht Move to Merge7z::Format::GetDefaultName() to
\r
81 build intermediate filenames for multi-step
\r
83 2005-07-15 Jochen Tucht Remove external command line tool integration
\r
84 for now. Rethink about it after 2.4 branch.
\r
85 2005-08-20 Jochen Tucht Option to guess archive format by signature
\r
86 Map extensions through ExternalArchiveFormat.ini
\r
87 2005-08-23 Jochen Tucht Option to entirely disable 7-Zip integration
\r
88 2007-01-04 Kimmo Varis Convert using COptionsMgr for options.
\r
89 2007-06-16 Jochen Neubeck FIX [1723263] "Zip --> Both" operation...
\r
90 2007-12-22 Jochen Neubeck Fix Merge7z UI lang for new translation system
\r
91 Change recommended version of 7-Zip to 4.57
\r
92 2010-05-16 Jochen Neubeck Read 7-Zip version from 7z.dll (which has long
\r
93 ago replaced the various format and codec DLLs)
\r
94 Change recommended version of 7-Zip to 4.65
\r
97 // ID line follows -- this is updated by SVN
\r
98 // $Id: 7zCommon.cpp 7169 2010-05-16 14:44:19Z jtuc $
\r
100 #include "stdafx.h"
\r
101 #include "7zCommon.h"
\r
102 #include <afxinet.h>
\r
103 #include <shlwapi.h>
\r
104 #include "OptionsDef.h"
\r
105 #include "OptionsMgr.h"
\r
106 #include "Merge.h" // DirDocFilter theApp GetOptionsMgr()
\r
107 #include "resource.h"
\r
108 #include "DirView.h"
\r
109 #include "DirDoc.h"
\r
110 #include "DirActions.h"
\r
111 //#include "ExternalArchiveFormat.h"
\r
112 #include "version.h"
\r
114 #include "Environment.h"
\r
117 #define new DEBUG_NEW
\r
119 static char THIS_FILE[] = __FILE__;
\r
123 * @brief Proxy for Merge7z
\r
125 static __declspec(thread) Merge7z::Proxy m_Merge7z =
\r
127 { 0, 0, DllBuild_Merge7z, },
\r
128 "Merge7z%u%02u"DECORATE_U".dll",
\r
134 * @brief assign BSTR to String, and return BSTR for optional SysFreeString()
\r
136 inline BSTR Assign(CString &dst, BSTR src)
\r
142 bool IsArchiveFile(const String& pszFile)
\r
145 Merge7z::Format *piHandler = ArchiveGuessFormat(pszFile);
\r
151 catch (CException *e)
\r
160 * @brief Wrap Merge7z::GuessFormat() to allow for some customizing:
\r
161 * - Check if 7-Zip integration is enabled.
\r
162 * - Check for filename extension mappings.
\r
164 Merge7z::Format *ArchiveGuessFormat(const String& path)
\r
166 if (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE) == 0)
\r
168 if (paths_IsDirectory(path))
\r
170 String path2(path);
\r
171 // Map extensions through ExternalArchiveFormat.ini
\r
172 static TCHAR null[] = _T("");
\r
173 static const TCHAR section[] = _T("extensions");
\r
174 String entry = paths_FindExtension(path);
\r
176 static LPCTSTR filename = NULL;
\r
177 if (filename == NULL)
\r
179 TCHAR cPath[INTERNET_MAX_PATH_LENGTH];
\r
180 DWORD cchPath = SearchPath(NULL, _T("ExternalArchiveFormat.ini"), NULL,
\r
181 INTERNET_MAX_PATH_LENGTH, cPath, NULL);
\r
182 filename = cchPath && cchPath < INTERNET_MAX_PATH_LENGTH ? StrDup(cPath) : null;
\r
185 GetPrivateProfileString(section, entry.c_str(), null, value, 20, filename) &&
\r
188 // Remove end-of-line comments (in string returned from GetPrivateProfileString)
\r
189 // that is, remove semicolon & whatever follows it
\r
190 if (LPTSTR p = StrChr(value, ';'))
\r
193 StrTrim(value, _T(" \t"));
\r
198 // PATCH [ 1229867 ] RFE [ 1205516 ], RFE [ 887948 ], and other issues
\r
199 // command line integration portion is not yet applied
\r
200 // so following code not yet valid, so temporarily commented out
\r
201 // Look for command line tool first
\r
202 /*Merge7z::Format *pFormat;
\r
203 if (CExternalArchiveFormat::GuessFormat(path, pFormat))
\r
207 // Default to Merge7z*.dll
\r
209 return m_Merge7z->GuessFormat(path2.c_str());
\r
213 * @brief Self-initializing raw C character buffer class.
\r
215 template<class TYPE, size_t SIZE> struct CRawString
\r
217 enum { Size = SIZE };
\r
226 * @brief Exception class for more explicit error message.
\r
228 class C7ZipMismatchException : public CException
\r
231 C7ZipMismatchException(DWORD dwVer7zInstalled, DWORD dwVer7zLocal, CException *pCause)
\r
233 m_dwVer7zInstalled = dwVer7zInstalled;
\r
234 m_dwVer7zLocal = dwVer7zLocal;
\r
237 ~C7ZipMismatchException()
\r
240 m_pCause->Delete();
\r
242 virtual int ReportError(UINT nType = MB_OK, UINT nMessageID = 0);
\r
244 DWORD m_dwVer7zInstalled;
\r
245 DWORD m_dwVer7zLocal;
\r
246 CException *m_pCause;
\r
247 BOOL m_bShowAllways;
\r
248 static const DWORD m_dwVer7zRecommended;
\r
249 static const TCHAR m_strRegistryKey[];
\r
250 static const TCHAR m_strDownloadURL[];
\r
251 static INT_PTR CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
\r
252 static DWORD FormatVersion(LPTSTR, LPTSTR, DWORD);
\r
256 * @brief Recommended version of 7-Zip.
\r
258 const DWORD C7ZipMismatchException::m_dwVer7zRecommended = DWORD MAKELONG(65,4);
\r
261 * @brief Registry key for C7ZipMismatchException's ReportError() popup.
\r
263 const TCHAR C7ZipMismatchException::m_strRegistryKey[] = _T("7ZipMismatch");
\r
266 * @brief Download URL for C7ZipMismatchException's ReportError() popup.
\r
268 const TCHAR C7ZipMismatchException::m_strDownloadURL[] = _T("https://sourceforge.net/project/showfiles.php?group_id=13216&package_id=143957");
\r
271 * @brief Retrieve build number of given DLL.
\r
273 static DWORD NTAPI GetDllBuild(LPCTSTR cPath)
\r
275 HMODULE hModule = LoadLibrary(cPath);
\r
276 DLLVERSIONINFO dvi;
\r
277 dvi.cbSize = sizeof dvi;
\r
278 dvi.dwBuildNumber = ~0UL;
\r
281 dvi.dwBuildNumber = 0UL;
\r
282 DLLGETVERSIONPROC DllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hModule, "DllGetVersion");
\r
285 DllGetVersion(&dvi);
\r
287 FreeLibrary(hModule);
\r
289 return dvi.dwBuildNumber;
\r
293 * @brief Format Merge7z version number and plugin name, and retrieve DllBuild.
\r
295 DWORD C7ZipMismatchException::FormatVersion(LPTSTR pcVersion, LPTSTR pcPluginName, DWORD dwVersion)
\r
298 *pcPluginName = '\0';
\r
303 pcVersion, _T("%u.%02u"),
\r
304 UINT HIWORD(dwVersion),
\r
305 UINT LOWORD(dwVersion)
\r
310 sizeof(TCHAR) == 1 ? _T("Merge7z%u%02u.dll") : _T("Merge7z%u%02uU.dll"),
\r
311 UINT HIWORD(dwVersion),
\r
312 UINT LOWORD(dwVersion)
\r
315 return GetDllBuild(pcPluginName);
\r
319 * @brief Populate ListBox with names/revisions of DLLs matching given pattern.
\r
321 static void NTAPI DlgDirListDLLs(HWND hWnd, LPTSTR cPattern, int nIDListBox)
\r
323 HDC hDC = GetDC(hWnd);
\r
324 HFONT hFont = (HFONT)SendDlgItemMessage(hWnd, nIDListBox, WM_GETFONT, 0, 0);
\r
325 int cxView = (int)SendDlgItemMessage(hWnd, nIDListBox, LB_GETHORIZONTALEXTENT, 0, 0) - 8;
\r
326 HGDIOBJ hObject = SelectObject(hDC, hFont);
\r
327 WIN32_FIND_DATA ff;
\r
328 HANDLE h = FindFirstFile(cPattern, &ff);
\r
329 if (h != INVALID_HANDLE_VALUE)
\r
333 PathRemoveFileSpec(cPattern);
\r
334 PathAppend(cPattern, ff.cFileName);
\r
335 wsprintf(ff.cFileName, _T(" (dllbuild %04u)"), GetDllBuild(cPattern));
\r
336 lstrcat(cPattern, ff.cFileName);
\r
337 int cxText = (int)(WORD)GetTabbedTextExtent(hDC, cPattern, lstrlen(cPattern), 0, 0);
\r
338 if (cxView < cxText)
\r
340 SendDlgItemMessage(hWnd, nIDListBox, LB_ADDSTRING, 0, (LPARAM)cPattern);
\r
341 } while (FindNextFile(h, &ff));
\r
344 SelectObject(hDC, hObject);
\r
345 ReleaseDC(hWnd, hDC);
\r
346 SendDlgItemMessage(hWnd, nIDListBox, LB_SETHORIZONTALEXTENT, cxView + 8, 0);
\r
350 * @brief OwnerDraw states from recent SDK.
\r
352 #define ODS_NOACCEL 0x0100
\r
353 #define ODS_NOFOCUSRECT 0x0200
\r
356 * @brief WM_DRAWITEM notification handlers.
\r
358 struct CDrawItemStruct : DRAWITEMSTRUCT
\r
360 typedef CDrawItemStruct *From;
\r
361 void DrawWebLinkButton();
\r
364 void CDrawItemStruct::DrawWebLinkButton()
\r
366 CRawString<TCHAR,INTERNET_MAX_PATH_LENGTH> cText;
\r
367 int cchText = ::GetWindowText(hwndItem, cText.Data, cText.Size);
\r
368 COLORREF clrText = RGB(0,0,255);
\r
369 if (::GetWindowLong(hwndItem, GWL_STYLE) & BS_LEFTTEXT)
\r
371 clrText = RGB(128,0,128);
\r
373 RECT rcText = rcItem;
\r
374 ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT|DT_CALCRECT);
\r
375 ::OffsetRect(&rcText, 1, 0);
\r
376 rcItem.right = rcText.right + 1;
\r
377 rcItem.bottom = rcText.bottom + 1;
\r
378 switch (itemAction)
\r
380 case ODA_DRAWENTIRE:
\r
381 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcItem, 0, 0, 0);
\r
382 ::SetBkMode(hDC, TRANSPARENT);
\r
383 ::SetTextColor(hDC, clrText);
\r
384 ::DrawText(hDC, cText.Data, cchText, &rcText, DT_LEFT);
\r
385 rcText.top = rcText.bottom - 1;
\r
386 ::SetBkColor(hDC, clrText);
\r
387 ::ExtTextOut(hDC, 0, 0, ETO_OPAQUE, &rcText, 0, 0, 0);
\r
388 if (itemState & ODS_FOCUS)
\r
391 if (!(itemState & ODS_NOFOCUSRECT))
\r
393 ::SetTextColor(hDC, 0);
\r
394 ::SetBkColor(hDC, RGB(255,255,255));
\r
395 ::SetBkMode(hDC, OPAQUE);
\r
396 DrawFocusRect(hDC, &rcItem);
\r
404 * @brief Load a cursor from COMCTL32.DLL.
\r
406 HCURSOR NTAPI CommCtrl_LoadCursor(LPCTSTR lpCursorName)
\r
408 HMODULE hModule = GetModuleHandle(_T("COMCTL32.DLL"));
\r
409 return hModule ? LoadCursor(hModule, lpCursorName) : NULL;
\r
413 * @brief DLGPROC for C7ZipMismatchException's ReportError() popup.
\r
415 INT_PTR CALLBACK C7ZipMismatchException::DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
\r
419 case WM_INITDIALOG:
\r
421 theApp.TranslateDialog(hWnd);
\r
422 if (GetDlgItem(hWnd, 9001) == NULL)
\r
424 // Dialog template isn't up to date. Give it a second chance.
\r
425 EndDialog(hWnd, -1);
\r
428 C7ZipMismatchException *pThis = (C7ZipMismatchException *)lParam;
\r
429 CRawString<TCHAR,2600> cText;
\r
430 CRawString<TCHAR,80> cPresent, cMissing, cOutdated, cNone, cPlugin;
\r
431 if (pThis->m_pCause)
\r
433 pThis->m_pCause->GetErrorMessage(cText.Data, cText.Size);
\r
434 SetDlgItemText(hWnd, 107, cText.Data);
\r
438 GetDlgItemText(hWnd, 107, cText.Data, cText.Size);
\r
439 switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))
\r
442 lstrcat(cText.Data, _("\nNote: 7-Zip integration is disabled in WinMerge settings.").c_str());
\r
445 lstrcat(cText.Data, _("\nNote: 7-Zip integration is restricted to standalone operation in WinMerge settings.").c_str());
\r
448 SetDlgItemText(hWnd, 107, cText.Data);
\r
450 GetDlgItemText(hWnd, 112, cPresent.Data, cPresent.Size);
\r
451 GetDlgItemText(hWnd, 122, cMissing.Data, cMissing.Size);
\r
452 GetDlgItemText(hWnd, 132, cOutdated.Data, cOutdated.Size);
\r
453 GetDlgItemText(hWnd, 120, cNone.Data, cNone.Size);
\r
454 GetDlgItemText(hWnd, 102, cPlugin.Data, cPlugin.Size);
\r
455 wsprintf(cText.Data, cPlugin.Data, DllBuild_Merge7z);
\r
456 SetDlgItemText(hWnd, 102, cText.Data);
\r
461 pThis->m_dwVer7zRecommended == pThis->m_dwVer7zInstalled
\r
462 || pThis->m_dwVer7zRecommended == pThis->m_dwVer7zLocal
\r
463 ) ? cPresent.Data : cMissing.Data
\r
465 DWORD dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zRecommended);
\r
466 SetDlgItemText(hWnd, 110, *cText.Data ? cText.Data : cNone.Data);
\r
467 SetDlgItemText(hWnd, 111, cPlugin.Data);
\r
468 SetDlgItemText(hWnd, 112, *cPlugin.Data == '\0' ? cPlugin.Data :
\r
469 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
\r
470 dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zInstalled);
\r
471 SetDlgItemText(hWnd, 120, *cText.Data ? cText.Data : cNone.Data);
\r
472 SetDlgItemText(hWnd, 121, cPlugin.Data);
\r
473 SetDlgItemText(hWnd, 122, *cPlugin.Data == '\0' ? cPlugin.Data :
\r
474 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
\r
475 dwDllBuild = FormatVersion(cText.Data, cPlugin.Data, pThis->m_dwVer7zLocal);
\r
476 SetDlgItemText(hWnd, 130, *cText.Data ? cText.Data : cNone.Data);
\r
477 SetDlgItemText(hWnd, 131, cPlugin.Data);
\r
478 SetDlgItemText(hWnd, 132, *cPlugin.Data == '\0' ? cPlugin.Data :
\r
479 dwDllBuild == ~0 ? cMissing.Data : dwDllBuild < DllBuild_Merge7z ? cOutdated.Data : cPresent.Data);
\r
480 GetModuleFileName(0, cText.Data, MAX_PATH);
\r
481 PathRemoveFileSpec(cText.Data);
\r
482 PathAppend(cText.Data, _T("Merge7z*.dll"));
\r
483 DlgDirListDLLs(hWnd, cText.Data, 105);
\r
484 if (DWORD cchPath = GetEnvironmentVariable(_T("path"), 0, 0))
\r
486 static const TCHAR cSep[] = _T(";");
\r
487 LPTSTR pchPath = new TCHAR[cchPath];
\r
488 GetEnvironmentVariable(_T("PATH"), pchPath, cchPath);
\r
489 LPTSTR pchItem = pchPath;
\r
490 while (int cchItem = StrCSpn(pchItem += StrSpn(pchItem, cSep), cSep))
\r
492 if (cchItem < MAX_PATH)
\r
494 CopyMemory(cText.Data, pchItem, cchItem*sizeof*pchItem);
\r
495 cText.Data[cchItem] = 0;
\r
496 PathAppend(cText.Data, _T("Merge7z*.dll"));
\r
497 DlgDirListDLLs(hWnd, cText.Data, 105);
\r
499 pchItem += cchItem;
\r
503 if (SendDlgItemMessage(hWnd, 105, LB_GETCOUNT, 0, 0) == 0)
\r
505 SendDlgItemMessage(hWnd, 105, LB_ADDSTRING, 0, (LPARAM) cNone.Data);
\r
507 HICON hIcon = LoadIcon(0, IDI_EXCLAMATION);
\r
508 SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
\r
509 SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM) hIcon);
\r
510 if (pThis->m_bShowAllways)
\r
512 ShowWindow(GetDlgItem(hWnd, 106), SW_HIDE);
\r
520 CDrawItemStruct::From(lParam)->DrawWebLinkButton();
\r
526 HCURSOR hCursor = 0;
\r
527 switch (GetDlgCtrlID((HWND)wParam))
\r
530 hCursor = CommCtrl_LoadCursor(MAKEINTRESOURCE(108));
\r
535 SetCursor(hCursor);
\r
536 SetWindowLongPtr(hWnd, DWLP_MSGRESULT, 1);
\r
547 LRESULT nDontShowAgain = SendDlgItemMessage(hWnd, 106, BM_GETCHECK, 0, 0);
\r
548 EndDialog(hWnd, MAKEWORD(IDOK, nDontShowAgain));
\r
552 HINSTANCE h = ShellExecute(hWnd, _T("open"), m_strDownloadURL, 0, 0, SW_SHOWNORMAL);
\r
555 LONG lStyle = ::GetWindowLong((HWND)lParam, GWL_STYLE);
\r
556 ::SetWindowLong((HWND)lParam, GWL_STYLE, lStyle|BS_LEFTTEXT);
\r
557 ::InvalidateRect((HWND)lParam, 0, TRUE);
\r
571 * @brief Tell user what went wrong and how she can help.
\r
573 int C7ZipMismatchException::ReportError(UINT nType, UINT nMessageID)
\r
575 UINT_PTR response = -1;
\r
576 m_bShowAllways = nMessageID;
\r
577 if (!m_bShowAllways)
\r
579 // Suppress error message in case 7-Zip is not installed.
\r
582 m_dwVer7zInstalled || m_dwVer7zLocal
\r
583 ? theApp.GetProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, -1)
\r
587 if (response == -1)
\r
589 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
\r
590 response = DialogBoxParam(AfxGetResourceHandle(), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);
\r
591 if (response == -1)
\r
593 response = DialogBoxParam(GetModuleHandle(0), MAKEINTRESOURCE(IDD_MERGE7ZMISMATCH), hwndOwner, DlgProc, (LPARAM)this);
\r
594 ASSERT(response != -1);
\r
596 if (HIBYTE(response) == 1)
\r
598 theApp.WriteProfileInt(REGISTRY_SECTION_MESSAGEBOX, m_strRegistryKey, response = int LOBYTE(response));
\r
605 * @brief Check whether archive support is available.
\r
607 int NTAPI HasZipSupport()
\r
609 static int HasZipSupport = -1;
\r
610 if (HasZipSupport == -1)
\r
614 m_Merge7z.operator->();
\r
617 catch (CException *e)
\r
623 return HasZipSupport;
\r
627 * @brief Tell user why archive support is not available.
\r
629 void NTAPI Recall7ZipMismatchError()
\r
633 m_Merge7z.operator->();
\r
635 catch (CException *e)
\r
637 e->ReportError(MB_ICONEXCLAMATION, TRUE);
\r
643 * @brief Delete head of temp path context list, and return its parent context.
\r
645 CTempPathContext *CTempPathContext::DeleteHead()
\r
647 CTempPathContext *pParent = m_pParent;
\r
652 BOOL NTAPI IsMerge7zEnabled()
\r
654 return AfxGetApp()->GetProfileInt(_T("Merge7z"), _T("Enable"), 0);
\r
658 * @brief Return installed or local version of 7-Zip.
\r
660 DWORD NTAPI VersionOf7z(BOOL bLocal)
\r
662 TCHAR path[MAX_PATH];
\r
665 GetModuleFileName(0, path, sizeof path/sizeof*path);
\r
666 PathRemoveFileSpec(path);
\r
670 static const TCHAR szSubKey[] = _T("Software\\7-Zip");
\r
671 static const TCHAR szValue[] = _T("Path");
\r
673 DWORD size = sizeof path;
\r
674 SHGetValue(HKEY_LOCAL_MACHINE, szSubKey, szValue, &type, path, &size);
\r
676 PathAppend(path, _T("7z.dll"));
\r
677 unsigned versionMS = 0;
\r
678 unsigned versionLS = 0;
\r
679 CVersionInfo(path).GetFixedFileVersion(versionMS, versionLS);
\r
684 * @brief Access dll functions through proxy.
\r
686 interface Merge7z *Merge7z::Proxy::operator->()
\r
688 // As long as the Merge7z*.DLL has not yet been loaded, Merge7z
\r
689 // [0] points to the name of the DLL (with placeholders for 7-
\r
690 // Zip major and minor version numbers). Once the DLL has been
\r
691 // loaded successfully, Merge7z[0] is set to NULL, causing the
\r
692 // if to fail on subsequent calls.
\r
694 if (const char *format = Merge7z[0])
\r
696 // Merge7z has not yet been loaded
\r
698 char name[MAX_PATH];
\r
700 CException *pCause = NULL;
\r
701 switch (GetOptionsMgr()->GetInt(OPT_ARCHIVE_ENABLE))
\r
703 case 1: //Use installed 7-Zip if present. Otherwise, use local 7-Zip.
\r
704 if (DWORD ver = VersionOf7z(FALSE))
\r
706 flags = Initialize::Default;
\r
709 wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));
\r
714 catch (CException *e)
\r
716 Merge7z[0] = format;
\r
720 case 2: //Always use local 7-Zip.
\r
721 if (DWORD ver = VersionOf7z(TRUE))
\r
723 flags = Initialize::Default | Initialize::Local7z;
\r
726 wsprintfA(name, format, UINT HIWORD(ver), UINT LOWORD(ver));
\r
731 catch (CException *e)
\r
733 Merge7z[0] = format;
\r
734 if (pCause) pCause->Delete();
\r
739 throw new C7ZipMismatchException
\r
741 VersionOf7z(FALSE),
\r
746 LANGID wLangID = (LANGID)GetThreadLocale();
\r
747 flags |= wLangID << 16;
\r
748 if (GetOptionsMgr()->GetBool(OPT_ARCHIVE_PROBETYPE))
\r
750 flags |= Initialize::GuessFormatBySignature | Initialize::GuessFormatByExtension;
\r
753 ((interface Merge7z *)Merge7z[1])->Initialize(flags);
\r
755 return ((interface Merge7z *)Merge7z[1]);
\r
759 * @brief Tell Merge7z we are going to enumerate just 1 item.
\r
761 UINT SingleItemEnumerator::Open()
\r
767 * @brief Pass information about the item to Merge7z.
\r
769 Merge7z::Envelope *SingleItemEnumerator::Enum(Item &item)
\r
771 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.Recurse;
\r
773 item.FullPath = FullPath;
\r
778 * @brief SingleFileEnumerator constructor.
\r
780 SingleItemEnumerator::SingleItemEnumerator(LPCTSTR path, LPCTSTR FullPath, LPCTSTR Name)
\r
781 : FullPath(FullPath)
\r
787 * @brief Construct a DirItemEnumerator.
\r
789 * Argument *nFlags* controls operation as follows:
\r
790 * LVNI_ALL: Enumerate all items.
\r
791 * LVNI_SELECTED: Enumerate selected items only.
\r
792 * Original: Set folder prefix for first iteration to "original"
\r
793 * Altered: Set folder prefix for second iteration to "altered"
\r
794 * BalanceFolders: Ensure that all nonempty folders on either side have a
\r
795 * corresponding folder on the other side, even if it is
\r
796 * empty (DirScan doesn't recurse into folders which
\r
797 * appear only on one side).
\r
798 * DiffsOnly: Enumerate diffs only.
\r
800 DirItemEnumerator::DirItemEnumerator(CDirView *pView, int nFlags)
\r
804 if (m_nFlags & Original)
\r
806 m_rgFolderPrefix.push_back(_T("original"));
\r
808 if (m_nFlags & Altered)
\r
810 m_rgFolderPrefix.push_back(_T("altered"));
\r
812 if (m_nFlags & BalanceFolders)
\r
814 const CDiffContext& ctxt = pView->GetDiffContext();
\r
815 // Collect implied folders
\r
816 for (UINT i = Open() ; i-- ; )
\r
818 const DIFFITEM &di = Next();
\r
819 if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))
\r
823 // Enumerating items
\r
824 if (di.diffcode.isExists(m_index))
\r
826 // Item is present on right side, i.e. folder is implied
\r
827 m_rgImpliedFolders[m_index][di.diffFileInfo[m_index].path.get()] = PVOID(1);
\r
834 * @brief Initialize enumerator, return number of items to be enumerated.
\r
836 UINT DirItemEnumerator::Open()
\r
839 m_curFolderPrefix = m_rgFolderPrefix.begin();
\r
840 m_index = (m_nFlags & Right) != 0 ? 1 : 0;
\r
841 size_t nrgFolderPrefix = m_rgFolderPrefix.size();
\r
842 if (nrgFolderPrefix)
\r
844 m_strFolderPrefix = *m_curFolderPrefix++;
\r
848 nrgFolderPrefix = 1;
\r
852 m_nFlags & LVNI_SELECTED
\r
853 ? pView(m_pView)->GetSelectedCount()
\r
854 : pView(m_pView)->GetItemCount()
\r
855 ) * nrgFolderPrefix;
\r
859 * @brief Return next item.
\r
861 const DIFFITEM &DirItemEnumerator::Next()
\r
863 enum {nMask = LVNI_FOCUSED|LVNI_SELECTED|LVNI_CUT|LVNI_DROPHILITED};
\r
864 while ((m_nIndex = pView(m_pView)->GetNextItem(m_nIndex, m_nFlags & nMask)) == -1)
\r
866 m_strFolderPrefix = *m_curFolderPrefix++;
\r
869 return m_pView->GetDiffItem(m_nIndex);
\r
873 * @brief Pass information about an item to Merge7z.
\r
875 * Information is passed through struct Merge7z::DirItemEnumerator::Item.
\r
876 * The *mask* member denotes which of the other members contain valid data.
\r
877 * If *mask* is zero upon return, which will be the case if Enum() decides to
\r
878 * leave the struct untouched, Merge7z will ignore the item.
\r
879 * If Enum() allocates temporary storage for string members, it must also
\r
880 * allocate an Envelope, providing a Free() method to free the temporary
\r
881 * storage, along with the Envelope itself. The Envelope pointer is passed to
\r
882 * Merge7z as the return value of the function. It is not meant to be a success
\r
883 * indicator, so if no temporary storage is required, it is perfectly alright
\r
886 Merge7z::Envelope *DirItemEnumerator::Enum(Item &item)
\r
888 const CDiffContext& ctxt = m_pView->GetDiffContext();
\r
889 const DIFFITEM &di = Next();
\r
891 if ((m_nFlags & DiffsOnly) && !IsItemNavigableDiff(ctxt, di))
\r
896 bool isSideOnly = !di.diffcode.isExists(m_index);
\r
898 Envelope *envelope = new Envelope;
\r
900 const String &sFilename = di.diffFileInfo[m_index].filename;
\r
901 const String &sSubdir = di.diffFileInfo[m_index].path;
\r
902 if (sSubdir.length())
\r
903 envelope->Name = paths_ConcatPath(sSubdir, sFilename);
\r
905 envelope->Name = sFilename;
\r
906 envelope->FullPath = paths_ConcatPath(
\r
907 di.getFilepath(m_index, ctxt.GetNormalizedPath(m_index)),
\r
910 UINT32 Recurse = item.Mask.Recurse;
\r
912 if (m_nFlags & BalanceFolders)
\r
914 // Enumerating items on right side
\r
917 // Item is missing on right side
\r
918 PVOID &implied = m_rgImpliedFolders[m_index][di.diffFileInfo[1-m_index].path.get()];
\r
921 // Folder is not implied by some other file, and has
\r
922 // not been enumerated so far, so enumerate it now!
\r
923 envelope->Name = di.diffFileInfo[1-m_index].path;
\r
924 envelope->FullPath = di.getFilepath(1-m_index, ctxt.GetNormalizedPath(1-m_index));
\r
925 implied = PVOID(2); // Don't enumerate same folder twice!
\r
926 isSideOnly = false;
\r
937 if (m_strFolderPrefix.length())
\r
939 if (envelope->Name.length())
\r
940 envelope->Name.insert(0, _T("\\"));
\r
941 envelope->Name.insert(0, m_strFolderPrefix);
\r
944 item.Mask.Item = item.Mask.Name|item.Mask.FullPath|item.Mask.CheckIfPresent|Recurse;
\r
945 item.Name = envelope->Name.c_str();
\r
946 item.FullPath = envelope->FullPath.c_str();
\r
951 * @brief Apply appropriate handlers from left to right.
\r
953 bool DirItemEnumerator::MultiStepCompressArchive(LPCTSTR path)
\r
956 Merge7z::Format *piHandler = ArchiveGuessFormat(path);
\r
959 HWND hwndOwner = CWnd::GetSafeOwner()->GetSafeHwnd();
\r
960 CString pathIntermediate;
\r
961 SysFreeString(Assign(pathIntermediate, piHandler->GetDefaultName(hwndOwner, path)));
\r
962 String pathPrepend = path;
\r
963 pathPrepend.resize(pathPrepend.rfind('\\') + 1);
\r
964 pathIntermediate.Insert(0, pathPrepend.c_str());
\r
965 bool bDone = MultiStepCompressArchive(pathIntermediate);
\r
968 piHandler->CompressArchive(hwndOwner, path,
\r
969 &SingleItemEnumerator(path, pathIntermediate));
\r
970 DeleteFile(pathIntermediate);
\r
974 piHandler->CompressArchive(hwndOwner, path, this);
\r
982 * @brief Generate archive from DirView items.
\r
984 void DirItemEnumerator::CompressArchive(LPCTSTR path)
\r
989 // No path given, so prompt for path!
\r
990 static const TCHAR _T_Merge7z[] = _T("Merge7z");
\r
991 static const TCHAR _T_FilterIndex[] = _T("FilterIndex");
\r
992 // 7z311 can only write 7z, zip, and tar(.gz|.bz2) archives, so don't
\r
993 // offer other formats here!
\r
994 static const TCHAR _T_Filter[]
\r
999 _T("jar (zip)|*.jar|")
\r
1000 _T("ear (zip)|*.ear|")
\r
1001 _T("war (zip)|*.war|")
\r
1002 _T("xpi (zip)|*.xpi|")
\r
1003 //_T("rar|*.rar|")
\r
1005 _T("tar.z|*.tar.z|")
\r
1006 _T("tar.gz|*.tar.gz|")
\r
1007 _T("tar.bz2|*.tar.bz2|")
\r
1010 _T("tbz2|*.tbz2|")
\r
1011 //_T("lzh|*.lzh|")
\r
1012 //_T("cab|*.cab|")
\r
1013 //_T("arj|*.arj|")
\r
1014 //_T("deb|*.deb|")
\r
1015 //_T("rpm|*.rpm|")
\r
1016 //_T("cpio|*.cpio|")
\r
1019 String strFilter; // = CExternalArchiveFormat::GetOpenFileFilterString();
\r
1020 strFilter.insert(0, _T_Filter);
\r
1021 strFilter += _T("|");
\r
1027 OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN,
\r
1030 dlg.m_ofn.nFilterIndex = AfxGetApp()->GetProfileInt(_T_Merge7z, _T_FilterIndex, 1);
\r
1031 // Use extension from current filter as default extension:
\r
1032 if (int i = dlg.m_ofn.nFilterIndex)
\r
1034 dlg.m_ofn.lpstrDefExt = dlg.m_ofn.lpstrFilter;
\r
1035 while (*dlg.m_ofn.lpstrDefExt && --i)
\r
1037 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
1038 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 1;
\r
1040 if (*dlg.m_ofn.lpstrDefExt)
\r
1042 dlg.m_ofn.lpstrDefExt += lstrlen(dlg.m_ofn.lpstrDefExt) + 3;
\r
1045 if (dlg.DoModal() == IDOK)
\r
1047 strPath = dlg.GetPathName();
\r
1048 path = strPath.c_str();
\r
1049 AfxGetApp()->WriteProfileInt(_T_Merge7z, _T_FilterIndex, dlg.m_ofn.nFilterIndex);
\r
1052 if (path && !MultiStepCompressArchive(path))
\r
1054 LangMessageBox(IDS_UNKNOWN_ARCHIVE_FORMAT, MB_ICONEXCLAMATION);
\r