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