msvcr100: Fix next writer unblocking in reader_writer_lock::unlock.
[wine.git] / programs / extrac32 / extrac32.c
blob3741f16b3cd63835a5335b72dcb522453faa7983
1 /*
2 * Extract - Wine-compatible program for extract *.cab files.
4 * Copyright 2007 Etersoft (Lyutin Anatoly)
5 * Copyright 2009 Ilya Shpigor
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.
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.
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
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>
30 #include "wine/debug.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(extrac32);
34 static BOOL force_mode;
35 static BOOL show_content;
37 static void create_target_directory(LPWSTR Target)
39 WCHAR dir[MAX_PATH];
40 int res;
42 lstrcpyW(dir, Target);
43 *PathFindFileNameW(dir) = 0; /* Truncate file name */
44 if(!PathIsDirectoryW(dir))
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));
52 static UINT WINAPI ExtCabCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
54 FILE_IN_CABINET_INFO_W *pInfo;
55 FILEPATHS_W *pFilePaths;
57 switch(Notification)
59 case SPFILENOTIFY_FILEINCABINET:
60 pInfo = (FILE_IN_CABINET_INFO_W*)Param1;
61 if(show_content)
63 FILETIME ft;
64 SYSTEMTIME st;
65 CHAR date[12], time[12], buf[2 * MAX_PATH];
66 int count;
67 DWORD dummy;
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;
83 else
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;
92 case SPFILENOTIFY_FILEEXTRACTED:
93 pFilePaths = (FILEPATHS_W*)Param1;
94 WINE_TRACE("Extracted %s\n", wine_dbgstr_w(pFilePaths->Target));
95 return NO_ERROR;
97 return NO_ERROR;
100 static void extract(LPCWSTR cabfile, LPWSTR destdir)
102 if (!SetupIterateCabinetW(cabfile, 0, ExtCabCallback, destdir))
103 WINE_ERR("Could not extract cab file %s\n", wine_dbgstr_w(cabfile));
106 static void copy_file(LPCWSTR source, LPCWSTR destination)
108 WCHAR destfile[MAX_PATH];
110 /* append source filename if destination is a directory */
111 if (PathIsDirectoryW(destination))
113 PathCombineW(destfile, destination, PathFindFileNameW(source));
114 destination = destfile;
117 if (PathFileExistsW(destination) && !force_mode)
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;
125 WINE_TRACE("copying %s to %s\n", wine_dbgstr_w(source), wine_dbgstr_w(destination));
126 CopyFileW(source, destination, FALSE);
129 static LPWSTR *get_extrac_args(LPWSTR cmdline, int *pargc)
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;
138 WINE_TRACE("cmdline: %s\n", wine_dbgstr_w(cmdline));
139 str = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
140 if(!str) return NULL;
141 lstrcpyW(str, cmdline);
142 argv = HeapAlloc(GetProcessHeap(), 0, (max_argc + 1) * sizeof(LPWSTR));
143 if(!argv)
145 HeapFree(GetProcessHeap(), 0, str);
146 return NULL;
149 /* Split command line to separate arg-strings and fill argv */
150 state = OUTSIDE_ARG;
151 argc = 0;
152 while(*str)
154 new_arg = FALSE;
155 /* Check character */
156 if(iswspace(*str)) /* white space */
158 if(state == INSIDE_ARG)
160 state = OUTSIDE_ARG;
161 *str = 0;
164 else if(*str == '"') /* double quote */
165 switch(state)
167 case INSIDE_QUOTED_ARG:
168 state = OUTSIDE_ARG;
169 *str = 0;
170 break;
171 case INSIDE_ARG:
172 *str = 0;
173 /* Fall through */
174 case OUTSIDE_ARG:
175 if(!*++str) continue;
176 state = INSIDE_QUOTED_ARG;
177 new_arg = TRUE;
178 break;
180 else /* regular character */
181 if(state == OUTSIDE_ARG)
183 state = INSIDE_ARG;
184 new_arg = TRUE;
187 /* Add new argv entry, if need */
188 if(new_arg)
190 if(argc >= max_argc - 1)
192 /* Realloc argv here because there always should be
193 at least one reserved cell for terminating NULL */
194 max_argc *= 2;
195 argv = HeapReAlloc(GetProcessHeap(), 0, argv,
196 (max_argc + 1) * sizeof(LPWSTR));
197 if(!argv)
199 HeapFree(GetProcessHeap(), 0, str);
200 return NULL;
203 argv[argc++] = str;
206 str++;
209 argv[argc] = NULL;
210 *pargc = argc;
212 if(TRACE_ON(extrac32))
214 int i;
215 for(i = 0; i < argc; i++)
216 WINE_TRACE("arg %d: %s\n", i, wine_dbgstr_w(argv[i]));
218 return argv;
221 int PASCAL wWinMain(HINSTANCE hInstance, HINSTANCE prev, LPWSTR cmdline, int show)
223 LPWSTR *argv;
224 int argc;
225 int i;
226 WCHAR check, cmd = 0;
227 WCHAR path[MAX_PATH];
228 LPCWSTR cabfile = NULL;
230 InitCommonControls();
232 path[0] = 0;
234 /* Do not use CommandLineToArgvW() or __wgetmainargs() to parse
235 * command line for this program. It should treat each quote as argument
236 * delimiter. This doesn't match with behavior of mentioned functions.
237 * Do not use args provided by wmain() for the same reason.
239 argv = get_extrac_args(cmdline, &argc);
241 if(!argv)
243 WINE_ERR("Command line parsing failed\n");
244 return 0;
247 /* Parse arguments */
248 for(i = 0; i < argc; i++)
250 /* Get cabfile */
251 if (argv[i][0] != '/' && argv[i][0] != '-')
253 if (!cabfile)
255 cabfile = argv[i];
256 continue;
257 } else
258 break;
260 /* Get parameters for commands */
261 check = towupper( argv[i][1] );
262 switch(check)
264 case 'A':
265 WINE_FIXME("/A not implemented\n");
266 break;
267 case 'Y':
268 force_mode = TRUE;
269 break;
270 case 'L':
271 if ((i + 1) >= argc) return 0;
272 if (!GetFullPathNameW(argv[++i], MAX_PATH, path, NULL))
273 return 0;
274 break;
275 case 'C':
276 case 'E':
277 case 'D':
278 if (cmd) return 0;
279 cmd = check;
280 break;
281 default:
282 return 0;
286 if (!cabfile)
287 return 0;
289 if (cmd == 'C')
291 if ((i + 1) != argc) return 0;
292 if (!GetFullPathNameW(argv[i], MAX_PATH, path, NULL))
293 return 0;
295 else if (!cmd)
296 /* Use extraction by default if names of required files presents */
297 cmd = i < argc ? 'E' : 'D';
299 if (cmd == 'E' && !path[0])
300 GetCurrentDirectoryW(MAX_PATH, path);
302 PathAddBackslashW(path);
304 /* Execute the specified command */
305 switch(cmd)
307 case 'C':
308 /* Copy file */
309 copy_file(cabfile, path);
310 break;
311 case 'D':
312 /* Display CAB archive */
313 show_content = TRUE;
314 /* Fall through */
315 case 'E':
316 /* Extract CAB archive */
317 extract(cabfile, path);
318 break;
320 return 0;