Leaked source code of windows server 2003
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.

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