OSDN Git Service

Fix Testing\EditorTest build problem
[winmerge-jp/winmerge-jp.git] / ArchiveSupport / Merge7zInstaller / Installer.cpp
1 /* Installer.cpp: Merge7z plugin installer
2  * Copyright (c) 2005 Jochen Tucht
3  *
4  * License:     This program is free software; you can redistribute it and/or modify
5  *                      it under the terms of the GNU General Public License as published by
6  *                      the Free Software Foundation; either version 2 of the License, or
7  *                      (at your option) any later version.
8  *
9  *                      This program is distributed in the hope that it will be useful,
10  *                      but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *                      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  *                      GNU General Public License for more details.
13  *
14  *                      You should have received a copy of the GNU General Public License
15  *                      along with this program; if not, write to the Free Software
16  *                      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  *
18  * Remarks:     Bundles plugins for all supported versions of 7-Zip in a single
19  *                      installer. Also includes essential components from latest 7-Zip
20  *                      stable release for optional standalone operation.
21  *                      Files to be installed are embedded as resources (see Files.rc2).
22  *                      The resulting exe must be run through UPX (upx.sourceforge.net)
23  *                      to reduce size.
24
25 Please mind 2. a) of the GNU General Public License, and log your changes below.
26
27 DATE:           BY:                                     DESCRIPTION:
28 ==========      ==================      ================================================
29 2005-01-15      Jochen Tucht            Created
30 2005-02-28      Jochen Tucht            Initialize filename in Open dialog to "*.exe"
31 2005-04-26      Jochen Tucht            No default assumption on program directory
32                                                                 Double-click option for in-place extraction
33                                                                 Fix empty path issue with GetFileTitle()
34                                                                 Accept extraction folder on command line
35                                                                 Batch options: /standalone, /select, /commit
36 2005-05-30      Jochen Tucht            Standalone option now based on 7z420
37 2005-06-28      Jochen Tucht            Standalone option now based on 7z423
38 2005-12-04      Jochen Tucht            Standalone option now based on 7z431
39 2005-12-09      Jochen Tucht            Standalone option now based on 7z432
40 2006-06-28      Jochen Neubeck          Standalone option now based on 7z442
41 2007-12-22      Jochen Neubeck          Standalone option now based on 7z457
42 */
43
44 #include <windows.h>
45
46 #define VERSION7Z 4.57
47
48 #pragma intrinsic(memset) // do not depend on CRT
49
50 #define SHARPEN(X) #X
51 #define SHARPEN2(X) SHARPEN(X)
52
53 // Compute dwBuild from revision.txt
54 static const DWORD dwBuild =
55 (
56         sizeof""
57 #       define VERSION(MAJOR,MINOR)
58 #       include "../Merge7z/revision.txt"
59 #       undef VERSION
60 );
61
62 static SYSTEMTIME st = {0,0,0,0,0,0,0,0};
63
64 LPTSTR NTAPI ArgLower(LPTSTR lpCmdLine)
65 {
66         while (*lpCmdLine == VK_SPACE)
67                 ++lpCmdLine;
68         return lpCmdLine;
69 }
70
71 LPTSTR NTAPI ArgUpper(LPTSTR lpCmdLine)
72 {
73         TCHAR cSpace = VK_SPACE;
74         while (*lpCmdLine && *lpCmdLine != cSpace)
75         {
76                 if (*lpCmdLine == '"')
77                 {
78                         cSpace ^= VK_SPACE;
79                 }
80                 ++lpCmdLine;
81         }
82         return lpCmdLine;
83 }
84
85 int NTAPI PathGetTailLength(LPCTSTR path)
86 {
87         //GetFileTitle() returns garbage when passed in empty path...
88         return *path ? GetFileTitle(path, 0, 0) : 0;
89 }
90
91 void InstallFile(HWND hWnd, LPTSTR lpName, LPCTSTR lpType, LPTSTR path, int cchPath)
92 {
93         HMODULE hModule = GetModuleHandle(0);
94         HRSRC hResource = FindResource(hModule, lpName, lpType);
95         DWORD dwSize = SizeofResource(hModule, hResource);
96         LPVOID pResource = LoadResource(hModule, hResource);
97         LPTSTR name = path + cchPath;
98         if (name != lpName)
99         {
100                 lstrcpy(name, lpName);
101         }
102         int cchName = lstrlen(name);
103         int i;
104         for (i = 0 ; i < cchName ; ++i)
105         {
106                 if (name[i] == '/')
107                 {
108                         name[i] = '\0';
109                         CreateDirectory(path, 0);
110                         name[i] = '\\';
111                 }
112         }
113         HANDLE hFile = CreateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
114         if (hFile != INVALID_HANDLE_VALUE)
115         {
116                 DWORD dwNumberOfBytesWritten;
117                 BOOL bSuccess = WriteFile(hFile, pResource, dwSize, &dwNumberOfBytesWritten, 0);
118                 FILETIME ft;
119                 if (SystemTimeToFileTime(&st, &ft))
120                 {
121                         SetFileTime(hFile, &ft, &ft, &ft);
122                 }
123                 CloseHandle(hFile);
124                 if (!bSuccess || dwNumberOfBytesWritten != dwSize)
125                 {
126                         hFile = INVALID_HANDLE_VALUE;
127                 }
128         }
129         if (hFile == INVALID_HANDLE_VALUE)
130         {
131                 LONG error = GetLastError();
132                 name[cchName++] = '\n';
133                 FormatMessage
134                 (
135                         FORMAT_MESSAGE_FROM_SYSTEM,
136                         NULL, error,
137                         MAKELANGID(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT),
138                         name + cchName, MAX_PATH, NULL
139                 );
140                 int response = MessageBox(hWnd, path, 0, MB_ICONSTOP|MB_OKCANCEL);
141                 if (response == IDCANCEL)
142                 {
143                         ExitProcess(1);
144                 }
145         }
146 }
147
148 BOOL CALLBACK fnPopulateList(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam)
149 {
150         TCHAR acName[MAX_PATH];
151         if (ATOM aName = FindAtom(lpName))
152         {
153                 GetAtomName(aName, lpName = acName, sizeof acName);
154         }
155         SendDlgItemMessage((HWND)lParam, 100, LB_ADDSTRING, 0, (LPARAM)lpName);
156         return TRUE;
157 }
158
159 BOOL CALLBACK fnInstallFiles(HMODULE hModule, LPCTSTR lpType, LPTSTR lpName, LONG lParam)
160 {
161         HWND hWnd = (HWND)lParam;
162         TCHAR path[8 * MAX_PATH];
163         int cchPath = GetDlgItemText(hWnd, 203, path, 5 * MAX_PATH);
164         int cchName = PathGetTailLength(path);
165         if (cchName < cchPath)
166         {
167                 cchPath -= cchName;
168         }
169         if (cchPath != 3 || path[1] != ':')
170         {
171                 path[cchPath++] = '\\';
172         }
173         TCHAR acName[MAX_PATH];
174         if (ATOM aName = FindAtom(lpName))
175         {
176                 GetAtomName(aName, lpName = acName, sizeof acName);
177         }
178         InstallFile(hWnd, lpName, lpType, path, cchPath);
179         return TRUE;
180 }
181
182 BOOL CALLBACK DlgMain_InitDialog(HWND hWnd, LPARAM lParam)
183 {
184         char date[] = __DATE__; // Compilation date "MMM DD YYYY"
185
186         TCHAR path[5 * MAX_PATH];
187         wsprintf(path + GetWindowText(hWnd, path, MAX_PATH),
188                 " (dllbuild %04lu, %s)", dwBuild, date);
189         SetWindowText(hWnd, path);
190
191         TCHAR fmt[MAX_PATH];
192         GetDlgItemText(hWnd, 205, fmt, MAX_PATH);
193         wsprintf(path, fmt, SHARPEN2(VERSION7Z));
194         SetDlgItemText(hWnd, 205, path);
195
196         date[6] = '#';
197         st.wYear = FindAtom(&date[6]);
198         date[6] = '\0';
199         date[3] = '#';
200         st.wDay = FindAtom(&date[3]);
201         date[3] = '\0';
202         st.wMonth = 1;
203         const char *month = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
204         while (lstrcmpi(month, date))
205         {
206                 ++st.wMonth;
207                 month += 4;
208         } 
209         st.wHour = 12;
210
211         EnumResourceNames(GetModuleHandle(0), "Merge7z", fnPopulateList, (LONG)hWnd);
212         LONG lCount = SendDlgItemMessage(hWnd, 100, LB_GETCOUNT , 0, 0);
213         SendDlgItemMessage(hWnd, 100, LB_SELITEMRANGEEX, 0, lCount - 1);
214         CheckRadioButton(hWnd, 201, 202, 201);
215         BOOL bCommit = FALSE;
216         BOOL bSelect = FALSE;
217         LPTSTR lpCmdLine = GetCommandLine();
218         LPTSTR lpArgLower = ArgLower(lpCmdLine);
219         LPTSTR lpArgUpper = ArgUpper(lpArgLower);
220         while (*(lpArgLower = ArgLower(lpArgUpper)))
221         {
222                 TCHAR cAhead = *(lpArgUpper = ArgUpper(lpArgLower));
223                 *lpArgUpper = '\0';
224                 if (0 == lstrcmpi(lpArgLower, "/standalone"))
225                 {
226                         CheckRadioButton(hWnd, 201, 202, 202);
227                         SendMessage(hWnd, WM_COMMAND, 202, 0);
228                         CheckDlgButton(hWnd, 205, 1);
229                         SendMessage(hWnd, WM_COMMAND, 205, 0);
230                 }
231                 else if (0 == lstrcmpi(lpArgLower, "/select"))
232                 {
233                         int lower = -1;
234                         int upper = -1;
235                         *lpArgUpper = cAhead;
236                         if (*(lpArgLower = ArgLower(lpArgUpper)))
237                         {
238                                 cAhead = *(lpArgUpper = ArgUpper(lpArgLower));
239                                 *lpArgUpper = '\0';
240                                 lower = SendDlgItemMessage(hWnd, 100, LB_FINDSTRING, -1, (LPARAM)lpArgLower);
241                                 if (lower == -1)
242                                 {
243                                         MessageBox(hWnd, lpArgLower, "No match", MB_ICONSTOP);
244                                 }
245                         }
246                         *lpArgUpper = cAhead;
247                         if (*(lpArgLower = ArgLower(lpArgUpper)))
248                         {
249                                 cAhead = *(lpArgUpper = ArgUpper(lpArgLower));
250                                 *lpArgUpper = '\0';
251                                 int ahead = -1;
252                                 while ((ahead = SendDlgItemMessage(hWnd, 100, LB_FINDSTRING, ahead, (LPARAM)lpArgLower)) > upper)
253                                 {
254                                         upper = ahead;
255                                 }
256                                 if (upper == -1)
257                                 {
258                                         MessageBox(hWnd, lpArgLower, "No match", MB_ICONSTOP);
259                                 }
260                         }
261                         if (lower >= 0 && upper >= 0)
262                         {
263                                 if (!bSelect)
264                                 {
265                                         SendDlgItemMessage(hWnd, 100, LB_SETSEL, 0, -1);
266                                         bSelect = TRUE;
267                                 }
268                                 SendDlgItemMessage(hWnd, 100, LB_SELITEMRANGEEX, lower, upper);
269                         }
270                 }
271                 else if (0 == lstrcmpi(lpArgLower, "/commit"))
272                 {
273                         bCommit = TRUE;
274                 }
275                 /*//just for test
276                 else if (0 == lstrcmpi(lpArgLower, "\"ping pong\""))
277                 {
278                         MessageBox(hWnd, "", lpArgLower, 0);
279                 }*/
280                 else
281                 {
282                         DWORD dwAttributes = GetFileAttributes(lpArgLower);
283                         if (dwAttributes != 0xFFFFFFFF && dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
284                         {
285                                 lstrcpy(path, lpArgLower);
286                                 if (PathGetTailLength(path) > 1)
287                                 {
288                                         lstrcat(path, "\\");
289                                 }
290                                 lstrcat(path, "*.exe");
291                                 CheckRadioButton(hWnd, 201, 202, 202);
292                                 SendMessage(hWnd, WM_COMMAND, 202, 0);
293                                 SetDlgItemText(hWnd, 203, path);
294                         }
295                         else
296                         {
297                                 MessageBox(hWnd, lpArgLower, "Not a directory", MB_ICONSTOP);
298                         }
299                 }
300                 *lpArgUpper = cAhead;
301         }
302         if (bCommit)
303         {
304                 SendMessage(hWnd, WM_COMMAND, IDOK, 0);
305         }
306         return TRUE;
307 }
308
309 BOOL CALLBACK DlgMain_BrowseExe(HWND hWnd)
310 {
311         struct
312         {
313                 OPENFILENAME ofn;
314                 TCHAR buffer[5 * MAX_PATH];
315         } path;
316         ZeroMemory(&path, sizeof path);
317         path.ofn.lStructSize = sizeof path.ofn;
318         path.ofn.hwndOwner = hWnd;
319         path.ofn.lpstrFile = path.buffer;
320         path.ofn.nMaxFile = sizeof path.buffer;
321         path.ofn.lpstrFilter = "*.exe\0*.exe\0";
322         path.ofn.lpstrTitle = "Browse for application ...";
323         path.ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST;
324         int cchPath = GetDlgItemText(hWnd, 203, path.buffer, sizeof path.buffer);
325         int cchName = PathGetTailLength(path.buffer);
326         if (cchName < cchPath)
327         {
328                 lstrcpy(path.buffer + cchPath - cchName, "\\*.exe");
329         }
330         if (GetOpenFileName(&path.ofn))
331         {
332                 SetDlgItemText(hWnd, 203, path.buffer);
333         }
334         return FALSE;
335 }
336
337 BOOL CALLBACK DlgMain_InstallFiles(HWND hWnd)
338 {
339         HCURSOR hCursor = SetCursor(LoadCursor(0, IDC_WAIT));
340         HINSTANCE hModule = GetModuleHandle(0);
341         int count = SendDlgItemMessage(hWnd, 100, LB_GETCOUNT , 0, 0);
342         int index = 0;
343         TCHAR path[8 * MAX_PATH];
344         int cchPath;
345         if (IsDlgButtonChecked(hWnd, 201))
346         {
347                 cchPath = GetSystemDirectory(path, 4 * MAX_PATH);
348         }
349         else
350         {
351                 cchPath = GetDlgItemText(hWnd, 203, path, 5 * MAX_PATH);
352                 int cchName = PathGetTailLength(path);
353                 if (cchName < cchPath)
354                 {
355                         cchPath -= cchName;
356                 }
357         }
358         if (cchPath != 3 || path[1] != ':')
359         {
360                 path[cchPath++] = '\\';
361         }
362         LPTSTR name = path + cchPath;
363         while (index < count)
364         {
365                 if (SendDlgItemMessage(hWnd, 100, LB_GETSEL, index, 0))
366                 {
367                         int cchName = SendDlgItemMessage(hWnd, 100, LB_GETTEXT, index, (LPARAM)name);
368                         InstallFile(hWnd, name, "Merge7z", path, cchPath);
369                 }
370                 ++index;
371         }
372         if (IsDlgButtonChecked(hWnd, 205))
373         {
374                 EnumResourceNames(GetModuleHandle(0), "7-ZIP", fnInstallFiles, (LONG)hWnd);
375         }
376         SetCursor(hCursor);
377         return TRUE;
378 }
379
380 BOOL CALLBACK DlgMain_EnableStandalone(HWND hWnd)
381 {
382         if (IsDlgButtonChecked(hWnd, 205))
383         {
384                 TCHAR buffer[40];
385                 const UINT major = UINT(VERSION7Z);
386                 const UINT minor = UINT(VERSION7Z * 100) % 100;
387                 wsprintf(buffer, "Merge7z%u%02u.dll", major, minor);
388                 int lower = SendDlgItemMessage(hWnd, 100, LB_FINDSTRINGEXACT, -1, (LPARAM)buffer);
389                 wsprintf(buffer, "Merge7z%u%02uU.dll", major, minor);
390                 int upper = SendDlgItemMessage(hWnd, 100, LB_FINDSTRINGEXACT, -1, (LPARAM)buffer);
391                 SendDlgItemMessage(hWnd, 100, LB_SELITEMRANGEEX, lower, upper);
392                 if (GetFocus() == GetDlgItem(hWnd, 205))
393                 {
394                         SendDlgItemMessage(hWnd, 100, LB_SETTOPINDEX, lower, 0);
395                 }
396         }
397         return TRUE;
398 }
399
400 BOOL CALLBACK DlgMain(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
401 {
402         TCHAR path[8 * MAX_PATH];
403         switch (uMsg)
404         {
405         case WM_INITDIALOG:
406                 return DlgMain_InitDialog(hWnd, lParam);
407         case WM_COMMAND:
408                 switch (wParam)
409                 {
410                 case 201:
411                         CheckDlgButton(hWnd, 205, 0);
412                         //fall through
413                 case 202:
414                         EnableWindow(GetDlgItem(hWnd, 203), wParam == 202);
415                         EnableWindow(GetDlgItem(hWnd, 204), wParam == 202);
416                         EnableWindow(GetDlgItem(hWnd, 205), wParam == 202);
417                         SetDlgItemText(hWnd, 203, "");
418                         break;
419                 case MAKELONG(202, BN_DOUBLECLICKED):
420                         GetModuleFileName(0, path, sizeof path);
421                         SetDlgItemText(hWnd, 203, path);
422                         break;
423                 case MAKELONG(203, EN_CHANGE):
424                         EnableWindow(GetDlgItem(hWnd, IDOK), GetWindowTextLength((HWND)lParam) || IsDlgButtonChecked(hWnd, 201));
425                         break;
426                 case 204:
427                         return DlgMain_BrowseExe(hWnd);
428                 case 205:
429                         if (IsDlgButtonChecked(hWnd, 205) && GetKeyState(VK_SHIFT) >= 0)
430                                 SendDlgItemMessage(hWnd, 100, LB_SETSEL, 0, -1);
431                         //fall through
432                 case MAKELONG(100, LBN_SELCHANGE):
433                         return DlgMain_EnableStandalone(hWnd);
434                 case IDOK: if (DlgMain_InstallFiles(hWnd)) case IDCANCEL:
435                         EndDialog(hWnd, wParam);
436                         break;
437                 }
438                 return TRUE;
439         }
440         return FALSE;
441 }
442
443 void WinMainCRTStartup(void)
444 {
445         InitAtomTable(0x3001);
446         HINSTANCE hModule = GetModuleHandle(0);
447         HRSRC hResource = FindResource(hModule, "Files.rc2", RT_RCDATA);
448         char *q = (char *)LoadResource(hModule, hResource);
449         DWORD n = SizeofResource(hModule, hResource);
450         char directive[MAX_PATH];
451         char arguments[MAX_PATH];
452         char *p = 0;
453         char c = '\n';
454         while (n)
455         {
456                 switch (c)
457                 {
458                 case '(':
459                         p = arguments;
460                         break;
461                 case ')':
462                         if (*directive == '\n')
463                                 AddAtom(arguments);
464                         //fall through
465                 case '\n':
466                 case '#':
467                         p = directive;
468                         //fall through
469                 default:
470                         *p++ = c;
471                         *p = '\0';
472                         break;
473                 }
474                 c = *q++;
475                 --n;
476         }
477         DialogBoxParam(hModule, MAKEINTRESOURCE(100), 0, DlgMain, 0);
478         ExitProcess(0);
479 }