1c2c66affSColin Finck /*
2c2c66affSColin Finck * Extract - Wine-compatible program for extract *.cab files.
3c2c66affSColin Finck *
4c2c66affSColin Finck * Copyright 2007 Etersoft (Lyutin Anatoly)
5c2c66affSColin Finck * Copyright 2009 Ilya Shpigor
6c2c66affSColin Finck *
7c2c66affSColin Finck * This library is free software; you can redistribute it and/or
8c2c66affSColin Finck * modify it under the terms of the GNU Lesser General Public
9c2c66affSColin Finck * License as published by the Free Software Foundation; either
10c2c66affSColin Finck * version 2.1 of the License, or (at your option) any later version.
11c2c66affSColin Finck *
12c2c66affSColin Finck * This library is distributed in the hope that it will be useful,
13c2c66affSColin Finck * but WITHOUT ANY WARRANTY; without even the implied warranty of
14c2c66affSColin Finck * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15c2c66affSColin Finck * Lesser General Public License for more details.
16c2c66affSColin Finck *
17c2c66affSColin Finck * You should have received a copy of the GNU Lesser General Public
18c2c66affSColin Finck * License along with this library; if not, write to the Free Software
19c2c66affSColin Finck * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20c2c66affSColin Finck */
21c2c66affSColin Finck
22c2c66affSColin Finck #include <windows.h>
23c2c66affSColin Finck #include <shellapi.h>
24c2c66affSColin Finck #include <setupapi.h>
25c2c66affSColin Finck #include <shlwapi.h>
26c2c66affSColin Finck #include <shlobj.h>
278ac66229SAmine Khaldi
288ac66229SAmine Khaldi #include "wine/unicode.h"
298ac66229SAmine Khaldi #include "wine/debug.h"
30c2c66affSColin Finck
31c2c66affSColin Finck WINE_DEFAULT_DEBUG_CHANNEL(extrac32);
32c2c66affSColin Finck
33c2c66affSColin Finck static BOOL force_mode;
34c2c66affSColin Finck static BOOL show_content;
35c2c66affSColin Finck
create_target_directory(LPWSTR Target)36c2c66affSColin Finck static void create_target_directory(LPWSTR Target)
37c2c66affSColin Finck {
38c2c66affSColin Finck WCHAR dir[MAX_PATH];
39c2c66affSColin Finck int res;
40c2c66affSColin Finck
41c2c66affSColin Finck strcpyW(dir, Target);
42c2c66affSColin Finck *PathFindFileNameW(dir) = 0; /* Truncate file name */
43c2c66affSColin Finck if(!PathIsDirectoryW(dir))
44c2c66affSColin Finck {
45c2c66affSColin Finck res = SHCreateDirectoryExW(NULL, dir, NULL);
46c2c66affSColin Finck if(res != ERROR_SUCCESS && res != ERROR_ALREADY_EXISTS)
47c2c66affSColin Finck WINE_ERR("Can't create directory: %s\n", wine_dbgstr_w(dir));
48c2c66affSColin Finck }
49c2c66affSColin Finck }
50c2c66affSColin Finck
ExtCabCallback(PVOID Context,UINT Notification,UINT_PTR Param1,UINT_PTR Param2)51c2c66affSColin Finck static UINT WINAPI ExtCabCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
52c2c66affSColin Finck {
53c2c66affSColin Finck FILE_IN_CABINET_INFO_W *pInfo;
54c2c66affSColin Finck FILEPATHS_W *pFilePaths;
55c2c66affSColin Finck
56c2c66affSColin Finck switch(Notification)
57c2c66affSColin Finck {
58c2c66affSColin Finck case SPFILENOTIFY_FILEINCABINET:
59c2c66affSColin Finck pInfo = (FILE_IN_CABINET_INFO_W*)Param1;
60c2c66affSColin Finck if(show_content)
61c2c66affSColin Finck {
62c2c66affSColin Finck FILETIME ft;
63c2c66affSColin Finck SYSTEMTIME st;
64c2c66affSColin Finck CHAR date[12], time[12], buf[2 * MAX_PATH];
65c2c66affSColin Finck int count;
66c2c66affSColin Finck DWORD dummy;
67c2c66affSColin Finck
68c2c66affSColin Finck /* DosDate and DosTime already represented at local time */
69c2c66affSColin Finck DosDateTimeToFileTime(pInfo->DosDate, pInfo->DosTime, &ft);
70c2c66affSColin Finck FileTimeToSystemTime(&ft, &st);
71c2c66affSColin Finck GetDateFormatA(0, 0, &st, "MM'-'dd'-'yyyy", date, sizeof date);
72c2c66affSColin Finck GetTimeFormatA(0, 0, &st, "HH':'mm':'ss", time, sizeof time);
73c2c66affSColin Finck count = wsprintfA(buf, "%s %s %c%c%c%c %15u %S\n", date, time,
74c2c66affSColin Finck pInfo->DosAttribs & FILE_ATTRIBUTE_ARCHIVE ? 'A' : '-',
75c2c66affSColin Finck pInfo->DosAttribs & FILE_ATTRIBUTE_HIDDEN ? 'H' : '-',
76c2c66affSColin Finck pInfo->DosAttribs & FILE_ATTRIBUTE_READONLY ? 'R' : '-',
77c2c66affSColin Finck pInfo->DosAttribs & FILE_ATTRIBUTE_SYSTEM ? 'S' : '-',
78c2c66affSColin Finck pInfo->FileSize, pInfo->NameInCabinet);
79c2c66affSColin Finck WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, count, &dummy, NULL);
80c2c66affSColin Finck return FILEOP_SKIP;
81c2c66affSColin Finck }
82c2c66affSColin Finck else
83c2c66affSColin Finck {
84c2c66affSColin Finck lstrcpyW(pInfo->FullTargetName, (LPCWSTR)Context);
85c2c66affSColin Finck lstrcatW(pInfo->FullTargetName, pInfo->NameInCabinet);
86c2c66affSColin Finck /* SetupIterateCabinet() doesn't create full path to target by itself,
87c2c66affSColin Finck so we should do it manually */
88c2c66affSColin Finck create_target_directory(pInfo->FullTargetName);
89c2c66affSColin Finck return FILEOP_DOIT;
90c2c66affSColin Finck }
91c2c66affSColin Finck case SPFILENOTIFY_FILEEXTRACTED:
92c2c66affSColin Finck pFilePaths = (FILEPATHS_W*)Param1;
93c2c66affSColin Finck WINE_TRACE("Extracted %s\n", wine_dbgstr_w(pFilePaths->Target));
94c2c66affSColin Finck return NO_ERROR;
95c2c66affSColin Finck }
96c2c66affSColin Finck return NO_ERROR;
97c2c66affSColin Finck }
98c2c66affSColin Finck
extract(LPCWSTR cabfile,LPWSTR destdir)99c2c66affSColin Finck static void extract(LPCWSTR cabfile, LPWSTR destdir)
100c2c66affSColin Finck {
101c2c66affSColin Finck if (!SetupIterateCabinetW(cabfile, 0, ExtCabCallback, destdir))
102c2c66affSColin Finck WINE_ERR("Could not extract cab file %s\n", wine_dbgstr_w(cabfile));
103c2c66affSColin Finck }
104c2c66affSColin Finck
copy_file(LPCWSTR source,LPCWSTR destination)105c2c66affSColin Finck static void copy_file(LPCWSTR source, LPCWSTR destination)
106c2c66affSColin Finck {
107c2c66affSColin Finck WCHAR destfile[MAX_PATH];
108c2c66affSColin Finck
109c2c66affSColin Finck /* append source filename if destination is a directory */
110c2c66affSColin Finck if (PathIsDirectoryW(destination))
111c2c66affSColin Finck {
112c2c66affSColin Finck PathCombineW(destfile, destination, PathFindFileNameW(source));
113c2c66affSColin Finck destination = destfile;
114c2c66affSColin Finck }
115c2c66affSColin Finck
116c2c66affSColin Finck if (PathFileExistsW(destination) && !force_mode)
117c2c66affSColin Finck {
118c2c66affSColin Finck static const WCHAR overwriteMsg[] = {'O','v','e','r','w','r','i','t','e',' ','"','%','s','"','?',0};
119c2c66affSColin Finck static const WCHAR titleMsg[] = {'E','x','t','r','a','c','t',0};
120c2c66affSColin Finck WCHAR msg[MAX_PATH+100];
121*f8052bd8SAmine Khaldi snprintfW(msg, ARRAY_SIZE(msg), overwriteMsg, destination);
122c2c66affSColin Finck if (MessageBoxW(NULL, msg, titleMsg, MB_YESNO | MB_ICONWARNING) != IDYES)
123c2c66affSColin Finck return;
124c2c66affSColin Finck }
125c2c66affSColin Finck
126c2c66affSColin Finck WINE_TRACE("copying %s to %s\n", wine_dbgstr_w(source), wine_dbgstr_w(destination));
127c2c66affSColin Finck CopyFileW(source, destination, FALSE);
128c2c66affSColin Finck }
129c2c66affSColin Finck
get_extrac_args(LPWSTR cmdline,int * pargc)130c2c66affSColin Finck static LPWSTR *get_extrac_args(LPWSTR cmdline, int *pargc)
131c2c66affSColin Finck {
132c2c66affSColin Finck enum {OUTSIDE_ARG, INSIDE_ARG, INSIDE_QUOTED_ARG} state;
133c2c66affSColin Finck LPWSTR str;
134c2c66affSColin Finck int argc;
135c2c66affSColin Finck LPWSTR *argv;
136c2c66affSColin Finck int max_argc = 16;
137c2c66affSColin Finck BOOL new_arg;
138c2c66affSColin Finck
139c2c66affSColin Finck WINE_TRACE("cmdline: %s\n", wine_dbgstr_w(cmdline));
140c2c66affSColin Finck str = HeapAlloc(GetProcessHeap(), 0, (strlenW(cmdline) + 1) * sizeof(WCHAR));
141c2c66affSColin Finck if(!str) return NULL;
142c2c66affSColin Finck strcpyW(str, cmdline);
143c2c66affSColin Finck argv = HeapAlloc(GetProcessHeap(), 0, (max_argc + 1) * sizeof(LPWSTR));
144c2c66affSColin Finck if(!argv)
145c2c66affSColin Finck {
146c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, str);
147c2c66affSColin Finck return NULL;
148c2c66affSColin Finck }
149c2c66affSColin Finck
150c2c66affSColin Finck /* Split command line to separate arg-strings and fill argv */
151c2c66affSColin Finck state = OUTSIDE_ARG;
152c2c66affSColin Finck argc = 0;
153c2c66affSColin Finck while(*str)
154c2c66affSColin Finck {
155c2c66affSColin Finck new_arg = FALSE;
156c2c66affSColin Finck /* Check character */
157c2c66affSColin Finck if(isspaceW(*str)) /* white space */
158c2c66affSColin Finck {
159c2c66affSColin Finck if(state == INSIDE_ARG)
160c2c66affSColin Finck {
161c2c66affSColin Finck state = OUTSIDE_ARG;
162c2c66affSColin Finck *str = 0;
163c2c66affSColin Finck }
164c2c66affSColin Finck }
165c2c66affSColin Finck else if(*str == '"') /* double quote */
166c2c66affSColin Finck switch(state)
167c2c66affSColin Finck {
168c2c66affSColin Finck case INSIDE_QUOTED_ARG:
169c2c66affSColin Finck state = OUTSIDE_ARG;
170c2c66affSColin Finck *str = 0;
171c2c66affSColin Finck break;
172c2c66affSColin Finck case INSIDE_ARG:
173c2c66affSColin Finck *str = 0;
174c2c66affSColin Finck /* Fall through */
175c2c66affSColin Finck case OUTSIDE_ARG:
176c2c66affSColin Finck if(!*++str) continue;
177c2c66affSColin Finck state = INSIDE_QUOTED_ARG;
178c2c66affSColin Finck new_arg = TRUE;
179c2c66affSColin Finck break;
180c2c66affSColin Finck }
181c2c66affSColin Finck else /* regular character */
182c2c66affSColin Finck if(state == OUTSIDE_ARG)
183c2c66affSColin Finck {
184c2c66affSColin Finck state = INSIDE_ARG;
185c2c66affSColin Finck new_arg = TRUE;
186c2c66affSColin Finck }
187c2c66affSColin Finck
188c2c66affSColin Finck /* Add new argv entry, if need */
189c2c66affSColin Finck if(new_arg)
190c2c66affSColin Finck {
191c2c66affSColin Finck if(argc >= max_argc - 1)
192c2c66affSColin Finck {
193c2c66affSColin Finck /* Realloc argv here because there always should be
194c2c66affSColin Finck at least one reserved cell for terminating NULL */
195c2c66affSColin Finck max_argc *= 2;
196c2c66affSColin Finck argv = HeapReAlloc(GetProcessHeap(), 0, argv,
197c2c66affSColin Finck (max_argc + 1) * sizeof(LPWSTR));
198c2c66affSColin Finck if(!argv)
199c2c66affSColin Finck {
200c2c66affSColin Finck HeapFree(GetProcessHeap(), 0, str);
201c2c66affSColin Finck return NULL;
202c2c66affSColin Finck }
203c2c66affSColin Finck }
204c2c66affSColin Finck argv[argc++] = str;
205c2c66affSColin Finck }
206c2c66affSColin Finck
207c2c66affSColin Finck str++;
208c2c66affSColin Finck }
209c2c66affSColin Finck
210c2c66affSColin Finck argv[argc] = NULL;
211c2c66affSColin Finck *pargc = argc;
212c2c66affSColin Finck
213c2c66affSColin Finck if(TRACE_ON(extrac32))
214c2c66affSColin Finck {
215c2c66affSColin Finck int i;
216c2c66affSColin Finck for(i = 0; i < argc; i++)
217c2c66affSColin Finck WINE_TRACE("arg %d: %s\n", i, wine_dbgstr_w(argv[i]));
218c2c66affSColin Finck }
219c2c66affSColin Finck return argv;
220c2c66affSColin Finck }
221c2c66affSColin Finck
wWinMain(HINSTANCE hInstance,HINSTANCE prev,LPWSTR cmdline,int show)222c2c66affSColin Finck int PASCAL wWinMain(HINSTANCE hInstance, HINSTANCE prev, LPWSTR cmdline, int show)
223c2c66affSColin Finck {
224c2c66affSColin Finck LPWSTR *argv;
225c2c66affSColin Finck int argc;
226c2c66affSColin Finck int i;
227c2c66affSColin Finck WCHAR check, cmd = 0;
228c2c66affSColin Finck WCHAR path[MAX_PATH];
229c2c66affSColin Finck LPCWSTR cabfile = NULL;
230c2c66affSColin Finck
231c2c66affSColin Finck path[0] = 0;
232c2c66affSColin Finck
233c2c66affSColin Finck /* Do not use CommandLineToArgvW() or __wgetmainargs() to parse
234c2c66affSColin Finck * command line for this program. It should treat each quote as argument
235c2c66affSColin Finck * delimiter. This doesn't match with behavior of mentioned functions.
236c2c66affSColin Finck * Do not use args provided by wmain() for the same reason.
237c2c66affSColin Finck */
238c2c66affSColin Finck argv = get_extrac_args(cmdline, &argc);
239c2c66affSColin Finck
240c2c66affSColin Finck if(!argv)
241c2c66affSColin Finck {
242c2c66affSColin Finck WINE_ERR("Command line parsing failed\n");
243c2c66affSColin Finck return 0;
244c2c66affSColin Finck }
245c2c66affSColin Finck
246c2c66affSColin Finck /* Parse arguments */
247c2c66affSColin Finck for(i = 0; i < argc; i++)
248c2c66affSColin Finck {
249c2c66affSColin Finck /* Get cabfile */
250c2c66affSColin Finck if (argv[i][0] != '/' && argv[i][0] != '-')
251c2c66affSColin Finck {
252c2c66affSColin Finck if (!cabfile)
253c2c66affSColin Finck {
254c2c66affSColin Finck cabfile = argv[i];
255c2c66affSColin Finck continue;
256c2c66affSColin Finck } else
257c2c66affSColin Finck break;
258c2c66affSColin Finck }
259c2c66affSColin Finck /* Get parameters for commands */
260c2c66affSColin Finck check = toupperW( argv[i][1] );
261c2c66affSColin Finck switch(check)
262c2c66affSColin Finck {
263c2c66affSColin Finck case 'A':
264c2c66affSColin Finck WINE_FIXME("/A not implemented\n");
265c2c66affSColin Finck break;
266c2c66affSColin Finck case 'Y':
267c2c66affSColin Finck force_mode = TRUE;
268c2c66affSColin Finck break;
269c2c66affSColin Finck case 'L':
270c2c66affSColin Finck if ((i + 1) >= argc) return 0;
271c2c66affSColin Finck if (!GetFullPathNameW(argv[++i], MAX_PATH, path, NULL))
272c2c66affSColin Finck return 0;
273c2c66affSColin Finck break;
274c2c66affSColin Finck case 'C':
275c2c66affSColin Finck case 'E':
276c2c66affSColin Finck case 'D':
277c2c66affSColin Finck if (cmd) return 0;
278c2c66affSColin Finck cmd = check;
279c2c66affSColin Finck break;
280c2c66affSColin Finck default:
281c2c66affSColin Finck return 0;
282c2c66affSColin Finck }
283c2c66affSColin Finck }
284c2c66affSColin Finck
285c2c66affSColin Finck if (!cabfile)
286c2c66affSColin Finck return 0;
287c2c66affSColin Finck
288c2c66affSColin Finck if (cmd == 'C')
289c2c66affSColin Finck {
290c2c66affSColin Finck if ((i + 1) != argc) return 0;
291c2c66affSColin Finck if (!GetFullPathNameW(argv[i], MAX_PATH, path, NULL))
292c2c66affSColin Finck return 0;
293c2c66affSColin Finck }
294c2c66affSColin Finck else if (!cmd)
295c2c66affSColin Finck /* Use extraction by default if names of required files presents */
296c2c66affSColin Finck cmd = i < argc ? 'E' : 'D';
297c2c66affSColin Finck
298c2c66affSColin Finck if (cmd == 'E' && !path[0])
299c2c66affSColin Finck GetCurrentDirectoryW(MAX_PATH, path);
300c2c66affSColin Finck
301c2c66affSColin Finck PathAddBackslashW(path);
302c2c66affSColin Finck
303c2c66affSColin Finck /* Execute the specified command */
304c2c66affSColin Finck switch(cmd)
305c2c66affSColin Finck {
306c2c66affSColin Finck case 'C':
307c2c66affSColin Finck /* Copy file */
308c2c66affSColin Finck copy_file(cabfile, path);
309c2c66affSColin Finck break;
310c2c66affSColin Finck case 'D':
311c2c66affSColin Finck /* Display CAB archive */
312c2c66affSColin Finck show_content = TRUE;
313c2c66affSColin Finck /* Fall through */
314c2c66affSColin Finck case 'E':
315c2c66affSColin Finck /* Extract CAB archive */
316c2c66affSColin Finck extract(cabfile, path);
317c2c66affSColin Finck break;
318c2c66affSColin Finck }
319c2c66affSColin Finck return 0;
320c2c66affSColin Finck }
321