extrac32.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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. #include <stdio.h>
  22. #include <windows.h>
  23. #include <commctrl.h>
  24. #include <shellapi.h>
  25. #include <setupapi.h>
  26. #include <shlwapi.h>
  27. #include <shlobj.h>
  28. #include "wine/debug.h"
  29. WINE_DEFAULT_DEBUG_CHANNEL(extrac32);
  30. static BOOL force_mode;
  31. static BOOL show_content;
  32. static void create_target_directory(LPWSTR Target)
  33. {
  34. WCHAR dir[MAX_PATH];
  35. int res;
  36. lstrcpyW(dir, Target);
  37. *PathFindFileNameW(dir) = 0; /* Truncate file name */
  38. if(!PathIsDirectoryW(dir))
  39. {
  40. res = SHCreateDirectoryExW(NULL, dir, NULL);
  41. if(res != ERROR_SUCCESS && res != ERROR_ALREADY_EXISTS)
  42. WINE_ERR("Can't create directory: %s\n", wine_dbgstr_w(dir));
  43. }
  44. }
  45. static UINT WINAPI ExtCabCallback(PVOID Context, UINT Notification, UINT_PTR Param1, UINT_PTR Param2)
  46. {
  47. FILE_IN_CABINET_INFO_W *pInfo;
  48. FILEPATHS_W *pFilePaths;
  49. switch(Notification)
  50. {
  51. case SPFILENOTIFY_FILEINCABINET:
  52. pInfo = (FILE_IN_CABINET_INFO_W*)Param1;
  53. if(show_content)
  54. {
  55. FILETIME ft;
  56. SYSTEMTIME st;
  57. CHAR date[12], time[12], buf[2 * MAX_PATH];
  58. int count;
  59. DWORD dummy;
  60. /* DosDate and DosTime already represented at local time */
  61. DosDateTimeToFileTime(pInfo->DosDate, pInfo->DosTime, &ft);
  62. FileTimeToSystemTime(&ft, &st);
  63. GetDateFormatA(0, 0, &st, "MM'-'dd'-'yyyy", date, sizeof date);
  64. GetTimeFormatA(0, 0, &st, "HH':'mm':'ss", time, sizeof time);
  65. count = wsprintfA(buf, "%s %s %c%c%c%c %15u %S\n", date, time,
  66. pInfo->DosAttribs & FILE_ATTRIBUTE_ARCHIVE ? 'A' : '-',
  67. pInfo->DosAttribs & FILE_ATTRIBUTE_HIDDEN ? 'H' : '-',
  68. pInfo->DosAttribs & FILE_ATTRIBUTE_READONLY ? 'R' : '-',
  69. pInfo->DosAttribs & FILE_ATTRIBUTE_SYSTEM ? 'S' : '-',
  70. pInfo->FileSize, pInfo->NameInCabinet);
  71. WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, count, &dummy, NULL);
  72. return FILEOP_SKIP;
  73. }
  74. else
  75. {
  76. lstrcpyW(pInfo->FullTargetName, (LPCWSTR)Context);
  77. lstrcatW(pInfo->FullTargetName, pInfo->NameInCabinet);
  78. /* SetupIterateCabinet() doesn't create full path to target by itself,
  79. so we should do it manually */
  80. create_target_directory(pInfo->FullTargetName);
  81. return FILEOP_DOIT;
  82. }
  83. case SPFILENOTIFY_FILEEXTRACTED:
  84. pFilePaths = (FILEPATHS_W*)Param1;
  85. WINE_TRACE("Extracted %s\n", wine_dbgstr_w(pFilePaths->Target));
  86. return NO_ERROR;
  87. }
  88. return NO_ERROR;
  89. }
  90. static void extract(LPCWSTR cabfile, LPWSTR destdir)
  91. {
  92. if (!SetupIterateCabinetW(cabfile, 0, ExtCabCallback, destdir))
  93. WINE_ERR("Could not extract cab file %s\n", wine_dbgstr_w(cabfile));
  94. }
  95. static void copy_file(LPCWSTR source, LPCWSTR destination)
  96. {
  97. WCHAR destfile[MAX_PATH];
  98. /* append source filename if destination is a directory */
  99. if (PathIsDirectoryW(destination))
  100. {
  101. PathCombineW(destfile, destination, PathFindFileNameW(source));
  102. destination = destfile;
  103. }
  104. if (PathFileExistsW(destination) && !force_mode)
  105. {
  106. WCHAR msg[MAX_PATH+100];
  107. swprintf(msg, ARRAY_SIZE(msg), L"Overwrite \"%s\"?", destination);
  108. if (MessageBoxW(NULL, msg, L"Extract", MB_YESNO | MB_ICONWARNING) != IDYES)
  109. return;
  110. }
  111. WINE_TRACE("copying %s to %s\n", wine_dbgstr_w(source), wine_dbgstr_w(destination));
  112. CopyFileW(source, destination, FALSE);
  113. }
  114. static LPWSTR *get_extrac_args(LPWSTR cmdline, int *pargc)
  115. {
  116. enum {OUTSIDE_ARG, INSIDE_ARG, INSIDE_QUOTED_ARG} state;
  117. LPWSTR str;
  118. int argc;
  119. LPWSTR *argv;
  120. int max_argc = 16;
  121. BOOL new_arg;
  122. WINE_TRACE("cmdline: %s\n", wine_dbgstr_w(cmdline));
  123. str = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(cmdline) + 1) * sizeof(WCHAR));
  124. if(!str) return NULL;
  125. lstrcpyW(str, cmdline);
  126. argv = HeapAlloc(GetProcessHeap(), 0, (max_argc + 1) * sizeof(LPWSTR));
  127. if(!argv)
  128. {
  129. HeapFree(GetProcessHeap(), 0, str);
  130. return NULL;
  131. }
  132. /* Split command line to separate arg-strings and fill argv */
  133. state = OUTSIDE_ARG;
  134. argc = 0;
  135. while(*str)
  136. {
  137. new_arg = FALSE;
  138. /* Check character */
  139. if(iswspace(*str)) /* white space */
  140. {
  141. if(state == INSIDE_ARG)
  142. {
  143. state = OUTSIDE_ARG;
  144. *str = 0;
  145. }
  146. }
  147. else if(*str == '"') /* double quote */
  148. switch(state)
  149. {
  150. case INSIDE_QUOTED_ARG:
  151. state = OUTSIDE_ARG;
  152. *str = 0;
  153. break;
  154. case INSIDE_ARG:
  155. *str = 0;
  156. /* Fall through */
  157. case OUTSIDE_ARG:
  158. if(!*++str) continue;
  159. state = INSIDE_QUOTED_ARG;
  160. new_arg = TRUE;
  161. break;
  162. }
  163. else /* regular character */
  164. if(state == OUTSIDE_ARG)
  165. {
  166. state = INSIDE_ARG;
  167. new_arg = TRUE;
  168. }
  169. /* Add new argv entry, if need */
  170. if(new_arg)
  171. {
  172. if(argc >= max_argc - 1)
  173. {
  174. /* Realloc argv here because there always should be
  175. at least one reserved cell for terminating NULL */
  176. max_argc *= 2;
  177. argv = HeapReAlloc(GetProcessHeap(), 0, argv,
  178. (max_argc + 1) * sizeof(LPWSTR));
  179. if(!argv)
  180. {
  181. HeapFree(GetProcessHeap(), 0, str);
  182. return NULL;
  183. }
  184. }
  185. argv[argc++] = str;
  186. }
  187. str++;
  188. }
  189. argv[argc] = NULL;
  190. *pargc = argc;
  191. if(TRACE_ON(extrac32))
  192. {
  193. int i;
  194. for(i = 0; i < argc; i++)
  195. WINE_TRACE("arg %d: %s\n", i, wine_dbgstr_w(argv[i]));
  196. }
  197. return argv;
  198. }
  199. int PASCAL wWinMain(HINSTANCE hInstance, HINSTANCE prev, LPWSTR cmdline, int show)
  200. {
  201. LPWSTR *argv;
  202. int argc;
  203. int i;
  204. WCHAR check, cmd = 0;
  205. WCHAR path[MAX_PATH];
  206. LPCWSTR cabfile = NULL;
  207. InitCommonControls();
  208. path[0] = 0;
  209. /* Do not use CommandLineToArgvW() or __wgetmainargs() to parse
  210. * command line for this program. It should treat each quote as argument
  211. * delimiter. This doesn't match with behavior of mentioned functions.
  212. * Do not use args provided by wmain() for the same reason.
  213. */
  214. argv = get_extrac_args(cmdline, &argc);
  215. if(!argv)
  216. {
  217. WINE_ERR("Command line parsing failed\n");
  218. return 0;
  219. }
  220. /* Parse arguments */
  221. for(i = 0; i < argc; i++)
  222. {
  223. /* Get cabfile */
  224. if (argv[i][0] != '/' && argv[i][0] != '-')
  225. {
  226. if (!cabfile)
  227. {
  228. cabfile = argv[i];
  229. continue;
  230. } else
  231. break;
  232. }
  233. /* Get parameters for commands */
  234. check = towupper( argv[i][1] );
  235. switch(check)
  236. {
  237. case 'A':
  238. WINE_FIXME("/A not implemented\n");
  239. break;
  240. case 'Y':
  241. force_mode = TRUE;
  242. break;
  243. case 'L':
  244. if ((i + 1) >= argc) return 0;
  245. if (!GetFullPathNameW(argv[++i], MAX_PATH, path, NULL))
  246. return 0;
  247. break;
  248. case 'C':
  249. case 'E':
  250. case 'D':
  251. if (cmd) return 0;
  252. cmd = check;
  253. break;
  254. default:
  255. return 0;
  256. }
  257. }
  258. if (!cabfile)
  259. return 0;
  260. if (cmd == 'C')
  261. {
  262. if ((i + 1) != argc) return 0;
  263. if (!GetFullPathNameW(argv[i], MAX_PATH, path, NULL))
  264. return 0;
  265. }
  266. else if (!cmd)
  267. /* Use extraction by default if names of required files presents */
  268. cmd = i < argc ? 'E' : 'D';
  269. if (cmd == 'E' && !path[0])
  270. GetCurrentDirectoryW(MAX_PATH, path);
  271. PathAddBackslashW(path);
  272. /* Execute the specified command */
  273. switch(cmd)
  274. {
  275. case 'C':
  276. /* Copy file */
  277. copy_file(cabfile, path);
  278. break;
  279. case 'D':
  280. /* Display CAB archive */
  281. show_content = TRUE;
  282. /* Fall through */
  283. case 'E':
  284. /* Extract CAB archive */
  285. extract(cabfile, path);
  286. break;
  287. }
  288. return 0;
  289. }