Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

599 lines
18 KiB

  1. /*
  2. ** main.c - Main module for DOS command-line LZA file compression / expansion
  3. ** programs.
  4. **
  5. ** Author: DavidDi
  6. **
  7. ** This module is compiled twice - once for COMPRESS (COMPRESS defined) and
  8. ** once for EXPAND (COMPRESS not defined).
  9. */
  10. // Headers
  11. ///////////
  12. #include <malloc.h>
  13. #include <stdio.h>
  14. #include <string.h>
  15. #include "lz_common.h"
  16. #include "lz_buffers.h"
  17. #include "lz_header.h"
  18. #include "args.h"
  19. #include "main.h"
  20. #include "messages.h"
  21. //
  22. // diamond routines
  23. //
  24. #include "mydiam.h"
  25. // Globals
  26. ///////////
  27. CHAR ARG_PTR *pszInFileName, // input file name
  28. *pszOutFileName, // output file name
  29. *pszTargetName; // target path name
  30. TCHAR ErrorMsg[2048];
  31. BOOL bContainsMultipleFiles; // Is source file a multi-file CAB?
  32. INT nLocalFiles, nTotalFiles = 0; // number of files listed/expanded
  33. // Module Variables
  34. ////////////////////
  35. #ifndef COMPRESS
  36. static BOOL bCopyingFile; // Is current file being copied or expanded?
  37. #endif
  38. // Local Prototypes
  39. ////////////////////
  40. static VOID DisplayErrorMessage(INT fError);
  41. static VOID MakeDestFileName(CHAR ARG_PTR *argv[], CHAR ARG_PTR *pszDest);
  42. static BOOL GetCanonicalName(LPSTR lpszFileName, LPSTR lpszCanonicalBuf);
  43. static BOOL ActuallyTheSameFile(CHAR ARG_PTR *pszFile1,
  44. CHAR ARG_PTR *pszFile2);
  45. static BOOL ProcessNotification(CHAR ARG_PTR *pszSource,
  46. CHAR ARG_PTR *pszDest, WORD wNotification);
  47. /*
  48. ** static void DisplayErrorMessage(int fError);
  49. **
  50. ** Display error message for given error condition.
  51. **
  52. ** Arguments: LZERROR_ code
  53. **
  54. ** Returns: void
  55. **
  56. ** Globals: none
  57. */
  58. static VOID DisplayErrorMessage(INT fError)
  59. {
  60. switch(fError)
  61. {
  62. case LZERROR_BADINHANDLE:
  63. LoadString(NULL, SID_NO_OPEN_INPUT, ErrorMsg, 2048);
  64. // WARNING: Cannot call CharToOemW with src=dest
  65. CharToOem(ErrorMsg, ErrorMsg);
  66. printf(ErrorMsg, pszInFileName);
  67. break;
  68. case LZERROR_BADOUTHANDLE:
  69. LoadString(NULL, SID_NO_OPEN_OUTPUT, ErrorMsg, 2048);
  70. // WARNING: Cannot call CharToOemW with src=dest
  71. CharToOem(ErrorMsg, ErrorMsg);
  72. printf(ErrorMsg, pszOutFileName);
  73. break;
  74. case LZERROR_READ:
  75. LoadString(NULL, SID_FORMAT_ERROR, ErrorMsg, 2048);
  76. // WARNING: Cannot call CharToOemW with src=dest
  77. CharToOem(ErrorMsg, ErrorMsg);
  78. printf(ErrorMsg, pszInFileName);
  79. break;
  80. case LZERROR_WRITE:
  81. LoadString(NULL, SID_OUT_OF_SPACE, ErrorMsg, 2048);
  82. // WARNING: Cannot call CharToOemW with src=dest
  83. CharToOem(ErrorMsg, ErrorMsg);
  84. printf(ErrorMsg, pszOutFileName);
  85. break;
  86. case LZERROR_UNKNOWNALG:
  87. LoadString(NULL, SID_UNKNOWN_ALG, ErrorMsg, 2048);
  88. // WARNING: Cannot call CharToOemW with src=dest
  89. CharToOem(ErrorMsg, ErrorMsg);
  90. printf(ErrorMsg, pszInFileName);
  91. break;
  92. case BLANK_ERROR:
  93. break;
  94. default:
  95. LoadString(NULL, SID_GEN_FAILURE, ErrorMsg, 2048);
  96. // WARNING: Cannot call CharToOemW with src=dest
  97. CharToOem(ErrorMsg, ErrorMsg);
  98. printf(ErrorMsg, pszInFileName, pszOutFileName);
  99. break;
  100. }
  101. }
  102. /*
  103. ** static void MakeDestFileName(char ARG_PTR *argv[], char ARG_PTR *pszDest);
  104. **
  105. ** Create the appropriate destination file name.
  106. **
  107. ** Arguments: argv - like argument to main()
  108. ** pszDest - pointer to destination file name buffer to be filled
  109. ** in
  110. **
  111. ** Returns: void
  112. **
  113. ** Globals: none
  114. */
  115. static VOID MakeDestFileName(CHAR ARG_PTR *argv[], CHAR ARG_PTR *pszDest)
  116. {
  117. CHAR ARG_PTR *pszDestFile;
  118. if (nNumFileSpecs == 2 && bTargetIsDir == FALSE && bDoRename == FALSE)
  119. // Compress a single input file to a single output file. N.b., we must
  120. // be careful to eat up the output file name command-line argument so
  121. // it doesn't get processed like another input file!
  122. STRCPY(pszDest, argv[GetNextFileArg(argv)]);
  123. else if (bTargetIsDir == TRUE)
  124. {
  125. // Prepend output file name with destination directory path name.
  126. STRCPY(pszDest, pszTargetName);
  127. // Isolate source file name from source file specification.
  128. pszDestFile = ExtractFileName(pszInFileName);
  129. // Add destination file name to destination directory path
  130. // specification.
  131. MakePathName(pszDest, pszDestFile);
  132. }
  133. else
  134. // Destination file name same as source file name. N.b., this is an
  135. // error condition if (bDoRename == FALSE).
  136. STRCPY(pszDest, pszInFileName);
  137. }
  138. /*
  139. ** static BOOL GetCanonicalName(LPSTR lpszFileName, LPSTR lpszCanonicalBuf);
  140. **
  141. ** Gets the canonical name for a given file specification.
  142. **
  143. ** Arguments: pszFileName - file specification
  144. ** szCanonicalBuf - buffer to be filled with canonical name
  145. **
  146. ** Returns: TRUE if successful. FALSE if unsuccessful.
  147. **
  148. ** N.b., szCanonicalBuf must be at least 128 bytes long. The contents of
  149. ** szCanonicalBuf are only defined if the funstion returns TRUE.
  150. **
  151. */
  152. static BOOL GetCanonicalName(LPSTR lpszFileName, LPSTR lpszCanonicalBuf)
  153. {
  154. BOOL bRetVal = FALSE;
  155. LPSTR lpszLastComp;
  156. return((BOOL) GetFullPathName(lpszFileName, MAX_PATH, lpszCanonicalBuf, &lpszLastComp));
  157. }
  158. /*
  159. ** static BOOL ActuallyTheSameFile(char ARG_PTR *pszFile1,
  160. ** char ARG_PTR *pszFile2);
  161. **
  162. ** Checks to see if two file specifications point to the same physical file.
  163. **
  164. ** Arguments: pszFile1 - first file specification
  165. ** pszFile2 - second file specification
  166. **
  167. ** Returns: BOOL - TRUE if the file specifications point to the same
  168. ** physical file. FALSE if not.
  169. **
  170. ** Globals: none
  171. */
  172. static BOOL ActuallyTheSameFile(CHAR ARG_PTR *pszFile1,
  173. CHAR ARG_PTR *pszFile2)
  174. {
  175. CHAR szCanonicalName1[MAX_PATH],
  176. szCanonicalName2[MAX_PATH];
  177. if (GetCanonicalName(pszFile1, szCanonicalName1) &&
  178. GetCanonicalName(pszFile2, szCanonicalName2))
  179. {
  180. if (! lstrcmpiA(szCanonicalName1, szCanonicalName2))
  181. return(TRUE);
  182. }
  183. return(FALSE);
  184. }
  185. /*
  186. ** static BOOL ProcessNotification(char ARG_PTR *pszSource,
  187. ** char ARG_PTR *pszDest,
  188. ** WORD wNotification);
  189. **
  190. ** Callback function during file processing.
  191. **
  192. ** Arguments: pszSource - source file name
  193. ** pszDest - destination file name
  194. ** wNotification - process type query
  195. **
  196. ** Returns: BOOL - (wNotification == NOTIFY_START_*):
  197. ** TRUE if the source file should be "processed" into
  198. ** the destination file. FALSE if not.
  199. ** else
  200. ** TRUE.
  201. **
  202. ** Globals: none
  203. */
  204. static BOOL ProcessNotification(CHAR ARG_PTR *pszSource,
  205. CHAR ARG_PTR *pszDest, WORD wNotification)
  206. {
  207. switch(wNotification)
  208. {
  209. case NOTIFY_START_EXPAND:
  210. case NOTIFY_START_COPY:
  211. {
  212. // If we're listing files, display the name then tell caller to skip.
  213. if (bDoListFiles == TRUE)
  214. {
  215. PSTR p;
  216. //
  217. // Display just the base name from the target. The prefix of the
  218. // target path is garbage (the source path.)
  219. //
  220. if(p = StringRevChar(pszDest,'\\')) {
  221. p++;
  222. } else {
  223. p = pszDest;
  224. }
  225. LoadString(NULL, SID_LISTING, ErrorMsg, 2048);
  226. CharToOem(ErrorMsg, ErrorMsg);
  227. printf(ErrorMsg, pszSource, p);
  228. nLocalFiles++; // count files listed
  229. nTotalFiles++; // count files listed
  230. return(FALSE); // always skip file
  231. }
  232. // Fail if the source and destination files are identical.
  233. if (ActuallyTheSameFile(pszSource, pszDest))
  234. {
  235. LoadString(NULL, SID_COLLISION, ErrorMsg, 2048);
  236. // WARNING: Cannot call CharToOemW with src=dest
  237. CharToOem(ErrorMsg, ErrorMsg);
  238. printf(ErrorMsg, pszSource);
  239. return(FALSE);
  240. }
  241. nLocalFiles++; // count files expanded
  242. nTotalFiles++; // count files expanded
  243. // Display start message.
  244. if (wNotification == NOTIFY_START_EXPAND) {
  245. LoadString(NULL, SID_EXPANDING, ErrorMsg, 2048);
  246. // WARNING: Cannot call CharToOemW with src=dest
  247. CharToOem(ErrorMsg, ErrorMsg);
  248. printf(ErrorMsg, pszSource, pszDest);
  249. }
  250. else // NOTIFY_START_COPY
  251. {
  252. bCopyingFile = TRUE;
  253. LoadString(NULL, SID_COPYING, ErrorMsg, 2048);
  254. // WARNING: Cannot call CharToOemW with src=dest
  255. CharToOem(ErrorMsg, ErrorMsg);
  256. printf(ErrorMsg, pszSource, pszDest);
  257. }
  258. break;
  259. }
  260. default:
  261. break;
  262. }
  263. return(TRUE);
  264. }
  265. /*
  266. ** int main(int argc, char *argv[]);
  267. **
  268. ** Run command-line file compression program.
  269. **
  270. ** Arguments: figure it out
  271. **
  272. ** Returns: int - EXIT_SUCCESS if compression finished successfully,
  273. ** EXIT_FAILURE if not.
  274. **
  275. ** Globals: none
  276. */
  277. INT __cdecl main(INT argc, CHAR *argv[])
  278. {
  279. INT iSourceFileName,
  280. fError,
  281. nReturnCode = EXIT_SUCCESS;
  282. CHAR ARG_PTR pszDestFileName[MAX_PATH];
  283. LONG cblTotInSize = 0L,
  284. cblTotOutSize = 0L;
  285. PLZINFO pLZI;
  286. BOOL fIsDiamondFile;
  287. CHAR ARG_PTR *pszFilesSpec;
  288. BOOL fReportStats = TRUE; // cleared if any multi-file CABs or listing
  289. USHORT wLanguageId = LANGIDFROMLCID(GetThreadLocale());
  290. if ((LANG_JAPANESE == PRIMARYLANGID(wLanguageId)) ||
  291. (LANG_KOREAN == PRIMARYLANGID(wLanguageId)) ||
  292. (LANG_CHINESE == PRIMARYLANGID(wLanguageId)))
  293. {
  294. //
  295. // This used to be #ifdef DBCS. Now a runtime check.
  296. //
  297. DWORD dw = GetConsoleOutputCP();
  298. // WARNING: in product 1.1 we need to uncomment the SetConsole above
  299. // LoadString will return Ansi and printf will just pass it on
  300. // This will let cmd interpret the characters it gets.
  301. // SetConsoleOutputCP(GetACP());
  302. switch (dw) {
  303. case 932:
  304. case 936:
  305. case 949:
  306. case 950:
  307. SetThreadLocale(MAKELCID(
  308. MAKELANGID(
  309. PRIMARYLANGID(GetSystemDefaultLangID()),
  310. SUBLANG_ENGLISH_US),
  311. SORT_DEFAULT));
  312. break;
  313. default:
  314. SetThreadLocale(MAKELCID(
  315. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  316. SORT_DEFAULT));
  317. break;
  318. }
  319. }
  320. // Display sign-on banner.
  321. LoadString(NULL, SID_BANNER_TEXT, ErrorMsg, 2048);
  322. // WARNING: Cannot call CharToOemW with src=dest
  323. CharToOem(ErrorMsg, ErrorMsg);
  324. printf(ErrorMsg);
  325. // Parse command-line arguments.
  326. if (ParseArguments(argc, argv) != TRUE)
  327. return(EXIT_FAILURE);
  328. // Set up global target path name.
  329. pszTargetName = argv[iTarget];
  330. if (bDisplayHelp == TRUE)
  331. {
  332. // User asked for help.
  333. LoadString(NULL, SID_INSTRUCTIONS, ErrorMsg, 2048);
  334. // WARNING: Cannot call CharToOemW with src=dest
  335. CharToOem(ErrorMsg, ErrorMsg);
  336. printf(ErrorMsg);
  337. return(EXIT_SUCCESS);
  338. }
  339. // Check for command line problems.
  340. if (CheckArguments() == FALSE)
  341. return(EXIT_FAILURE);
  342. // Set up ring buffer and I/O buffers.
  343. pLZI = InitGlobalBuffersEx();
  344. if (!pLZI || !InitDiamond())
  345. {
  346. LoadString(NULL, SID_INSUFF_MEM, ErrorMsg, 2048);
  347. // WARNING: Cannot call CharToOemW with src=dest
  348. CharToOem(ErrorMsg, ErrorMsg);
  349. printf(ErrorMsg);
  350. return(EXIT_FAILURE);
  351. }
  352. // Process each source file.
  353. while ((iSourceFileName = GetNextFileArg(argv)) != FAIL)
  354. {
  355. nLocalFiles = 0;
  356. pLZI->cblOutSize = 0;
  357. // Set up global input file name.
  358. pszInFileName = CharLowerA(argv[iSourceFileName]);
  359. // Set up global output file name.
  360. MakeDestFileName(argv, pszDestFileName);
  361. pszOutFileName = CharLowerA(pszDestFileName);
  362. // Assume current file will be expanded. The ProcessNotification()
  363. // callback will change this module global to TRUE if the file is being
  364. // copied instead of expanded.
  365. bCopyingFile = FALSE;
  366. //
  367. // Determine whether the file was compressed with diamond.
  368. // If so, we need to expand it specially.
  369. //
  370. fIsDiamondFile = IsDiamondFile(pszInFileName, &bContainsMultipleFiles);
  371. if (fIsDiamondFile) {
  372. if (bContainsMultipleFiles) {
  373. if (nNumFileSpecs == 1 && (bDoListFiles == FALSE)) {
  374. //
  375. // The source file is a multi-file CAB, and is the only file
  376. // on the command line. We'll require an explicit filespec
  377. // which names the file(s) desired from within the CAB.
  378. // The Files specification may contain wildcards.
  379. //
  380. // If the user included multiple source files on the command
  381. // line, we'll assume they're expecting a lot of output, and
  382. // we'll default to all files. It would be pointless to put
  383. // up a message anyway, since the screen will be scrolling.
  384. //
  385. if (pszSelectiveFilesSpec == NULL) {
  386. LoadString(NULL, SID_FILESPEC_REQUIRED, ErrorMsg, 2048);
  387. // WARNING: Cannot call CharToOemW with src=dest
  388. CharToOem(ErrorMsg, ErrorMsg);
  389. printf(ErrorMsg);
  390. continue; // skip this (the only) source file
  391. }
  392. }
  393. if (!bTargetIsDir && (bDoListFiles == FALSE)) {
  394. //
  395. // The source file is a multi-file CAB, and the target is
  396. // a single file. Now this just isn't going to work. We'll
  397. // display this warning, and hope the user notices it. If
  398. // a multiple sources were specified, they're going to get
  399. // a mess anyway. We just won't contribute to it.
  400. //
  401. LoadString(NULL, SID_DEST_REQUIRED, ErrorMsg, 2048);
  402. // WARNING: Cannot call CharToOemW with src=dest
  403. CharToOem(ErrorMsg, ErrorMsg);
  404. printf(ErrorMsg, pszInFileName);
  405. continue; // skip this source file
  406. }
  407. pszFilesSpec = pszSelectiveFilesSpec;
  408. //
  409. // Don't try to interpret final stats if multi-file CABs are seen.
  410. // (Because of selective extract and other issues, you'll get silly
  411. // reports like "20000000 bytes expanded to 16320 bytes".)
  412. //
  413. fReportStats = FALSE;
  414. } else {
  415. //
  416. // Legacy: no selective expand from single-file CABs
  417. //
  418. pszFilesSpec = NULL;
  419. }
  420. //
  421. // Make sure renaming is ON if this is a multi-file CAB.
  422. //
  423. fError = ExpandDiamondFile(ProcessNotification,pszInFileName,
  424. pszOutFileName,(bDoRename || bContainsMultipleFiles),
  425. pszFilesSpec,pLZI);
  426. } else {
  427. fError = Expand(ProcessNotification, pszInFileName,
  428. pszOutFileName, bDoRename, pLZI);
  429. }
  430. if (fError != TRUE) {
  431. // Deal with returned error codes.
  432. DisplayErrorMessage(nReturnCode = fError);
  433. } else if (bContainsMultipleFiles) {
  434. if (nLocalFiles == 0) {
  435. LoadString(NULL, SID_NO_MATCHES, ErrorMsg, 2048);
  436. // WARNING: Cannot call CharToOemW with src=dest
  437. CharToOem(ErrorMsg, ErrorMsg);
  438. printf(ErrorMsg, pszInFileName, pszSelectiveFilesSpec);
  439. }
  440. } else {
  441. if (pLZI && pLZI->cblInSize && pLZI->cblOutSize) {
  442. // Keep track of cumulative statistics.
  443. cblTotInSize += pLZI->cblInSize;
  444. cblTotOutSize += pLZI->cblOutSize;
  445. if (bCopyingFile) {
  446. LoadString(NULL, SID_COPY_REPORT, ErrorMsg, 2048);
  447. // WARNING: Cannot call CharToOemW with src=dest
  448. CharToOem(ErrorMsg, ErrorMsg);
  449. printf(ErrorMsg, pszInFileName, pLZI->cblInSize);
  450. }
  451. else {
  452. LoadString(NULL, SID_FILE_REPORT, ErrorMsg, 2048);
  453. // WARNING: Cannot call CharToOemW with src=dest
  454. CharToOem(ErrorMsg, ErrorMsg);
  455. printf(ErrorMsg, pszInFileName, pLZI->cblInSize, pLZI->cblOutSize,
  456. (INT)(((100 * (LONGLONG) pLZI->cblOutSize) / pLZI->cblInSize) - 100));
  457. }
  458. }
  459. }
  460. // Separate individual file processing message blocks by a blank line.
  461. printf("\n");
  462. }
  463. // Free memory used by ring buffer and I/O buffers.
  464. FreeGlobalBuffers(pLZI);
  465. TermDiamond();
  466. if (!fReportStats || bDoListFiles) {
  467. if (nTotalFiles > 1) {
  468. LoadString(NULL, SID_TOTAL_COUNT, ErrorMsg, 2048);
  469. // WARNING: Cannot call CharToOemW with src=dest
  470. CharToOem(ErrorMsg, ErrorMsg);
  471. printf(ErrorMsg, nTotalFiles);
  472. }
  473. } else {
  474. // Display cumulative report for multiple files.
  475. if ((nTotalFiles > 1) && (cblTotInSize != 0)) {
  476. // Scale results to get accurate %
  477. LONG cblAdjInSize = cblTotInSize,
  478. cblAdjOutSize = cblTotOutSize;
  479. while (cblAdjInSize > 100000) {
  480. cblAdjInSize /= 2;
  481. cblAdjOutSize /= 2;
  482. }
  483. cblAdjOutSize += (cblAdjInSize / 200); // round off (+0.5%)
  484. if (cblAdjOutSize < 0) {
  485. cblAdjOutSize = 0;
  486. }
  487. LoadString(NULL, SID_TOTAL_REPORT, ErrorMsg, 2048);
  488. // WARNING: Cannot call CharToOemW with src=dest
  489. CharToOem(ErrorMsg, ErrorMsg);
  490. printf(ErrorMsg, nTotalFiles, cblTotInSize, cblTotOutSize,
  491. (INT)(100 * cblAdjOutSize / cblAdjInSize - 100));
  492. }
  493. }
  494. return(nReturnCode);
  495. }