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.

590 lines
16 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 <io.h>
  16. #include <fcntl.h>
  17. #include <share.h>
  18. #include <sys\types.h>
  19. #include <sys\stat.h>
  20. #include "lz_common.h"
  21. #include "lz_buffers.h"
  22. #include "lz_header.h"
  23. #include "args.h"
  24. #include "main.h"
  25. #include "messages.h"
  26. #include <diamondc.h>
  27. #include "mydiam.h"
  28. // Globals
  29. ///////////
  30. CHAR ARG_PTR *pszInFileName, // input file name
  31. *pszOutFileName, // output file name
  32. *pszTargetName; // target path name
  33. TCHAR ErrorMsg[1024];
  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, 1024);
  65. printf(ErrorMsg, pszInFileName);
  66. break;
  67. case LZERROR_BADOUTHANDLE:
  68. LoadString(NULL, SID_NO_OPEN_OUTPUT, ErrorMsg, 1024);
  69. printf(ErrorMsg, pszOutFileName);
  70. break;
  71. case LZERROR_READ:
  72. LoadString(NULL, SID_NO_READ_INPUT, ErrorMsg, 1024);
  73. printf(ErrorMsg, pszInFileName);
  74. break;
  75. case LZERROR_WRITE:
  76. LoadString(NULL, SID_OUT_OF_SPACE, ErrorMsg, 1024);
  77. printf(ErrorMsg, pszOutFileName);
  78. break;
  79. case BLANK_ERROR:
  80. break;
  81. default:
  82. LoadString(NULL, SID_GEN_FAILURE, ErrorMsg, 1024);
  83. printf(ErrorMsg, pszInFileName, pszOutFileName);
  84. break;
  85. }
  86. }
  87. /*
  88. ** static void MakeDestFileName(char ARG_PTR *argv[], char ARG_PTR *pszDest);
  89. **
  90. ** Create the appropriate destination file name.
  91. **
  92. ** Arguments: argv - like argument to main()
  93. ** pszDest - pointer to destination file name buffer to be filled
  94. ** in
  95. **
  96. ** Returns: void
  97. **
  98. ** Globals: none
  99. */
  100. static VOID MakeDestFileName(CHAR ARG_PTR *argv[], CHAR ARG_PTR *pszDest)
  101. {
  102. CHAR ARG_PTR *pszDestFile;
  103. if (nNumFileSpecs == 2 && bTargetIsDir == FALSE && bDoRename == FALSE)
  104. // Compress a single input file to a single output file. N.b., we must
  105. // be careful to eat up the output file name command-line argument so
  106. // it doesn't get processed like another input file!
  107. STRCPY(pszDest, argv[GetNextFileArg(argv)]);
  108. else if (bTargetIsDir == TRUE)
  109. {
  110. // Prepend output file name with destination directory path name.
  111. STRCPY(pszDest, pszTargetName);
  112. // Isolate source file name from source file specification.
  113. pszDestFile = ExtractFileName(pszInFileName);
  114. // Add destination file name to destination directory path
  115. // specification.
  116. MakePathName(pszDest, pszDestFile);
  117. }
  118. else
  119. // Destination file name same as source file name. N.b., this is an
  120. // error condition if (bDoRename == FALSE).
  121. STRCPY(pszDest, pszInFileName);
  122. }
  123. /*
  124. ** static BOOL GetCanonicalName(LPSTR lpszFileName, LPSTR lpszCanonicalBuf);
  125. **
  126. ** Gets the canonical name for a given file specification.
  127. **
  128. ** Arguments: pszFileName - file specification
  129. ** szCanonicalBuf - buffer to be filled with canonical name
  130. **
  131. ** Returns: TRUE if successful. FALSE if unsuccessful.
  132. **
  133. ** N.b., szCanonicalBuf must be at least 128 bytes long. The contents of
  134. ** szCanonicalBuf are only defined if the funstion returns TRUE.
  135. **
  136. */
  137. static BOOL GetCanonicalName(LPSTR lpszFileName, LPSTR lpszCanonicalBuf)
  138. {
  139. BOOL bRetVal = FALSE;
  140. LPSTR lpszLastComp;
  141. return((BOOL) GetFullPathName(lpszFileName, MAX_PATH, lpszCanonicalBuf, &lpszLastComp));
  142. }
  143. /*
  144. ** static BOOL ActuallyTheSameFile(char ARG_PTR *pszFile1,
  145. ** char ARG_PTR *pszFile2);
  146. **
  147. ** Checks to see if two file specifications point to the same physical file.
  148. **
  149. ** Arguments: pszFile1 - first file specification
  150. ** pszFile2 - second file specification
  151. **
  152. ** Returns: BOOL - TRUE if the file specifications point to the same
  153. ** physical file. FALSE if not.
  154. **
  155. ** Globals: none
  156. */
  157. static BOOL ActuallyTheSameFile(CHAR ARG_PTR *pszFile1,
  158. CHAR ARG_PTR *pszFile2)
  159. {
  160. CHAR szCanonicalName1[MAX_PATH],
  161. szCanonicalName2[MAX_PATH];
  162. if (GetCanonicalName(pszFile1, szCanonicalName1) &&
  163. GetCanonicalName(pszFile2, szCanonicalName2))
  164. {
  165. if (! lstrcmpiA(szCanonicalName1, szCanonicalName2))
  166. return(TRUE);
  167. }
  168. return(FALSE);
  169. }
  170. /*
  171. ** static BOOL ProcessNotification(char ARG_PTR *pszSource,
  172. ** char ARG_PTR *pszDest,
  173. ** WORD wNotification);
  174. **
  175. ** Callback function during file processing.
  176. **
  177. ** Arguments: pszSource - source file name
  178. ** pszDest - destination file name
  179. ** wNotification - process type query
  180. **
  181. ** Returns: BOOL - (wNotification == NOTIFY_START_*):
  182. ** TRUE if the source file should be "processed" into
  183. ** the destination file. FALSE if not.
  184. ** else
  185. ** TRUE.
  186. **
  187. ** Globals: none
  188. */
  189. static BOOL ProcessNotification(CHAR ARG_PTR *pszSource,
  190. CHAR ARG_PTR *pszDest, WORD wNotification)
  191. {
  192. switch(wNotification)
  193. {
  194. case NOTIFY_START_COMPRESS:
  195. {
  196. // Fail if the source and destination files are identical.
  197. if (ActuallyTheSameFile(pszSource, pszDest))
  198. {
  199. LoadString(NULL, SID_COLLISION, ErrorMsg, 1024);
  200. printf(ErrorMsg, pszSource);
  201. return(FALSE);
  202. }
  203. // Display start message.
  204. switch (byteAlgorithm)
  205. {
  206. case LZX_ALG:
  207. LoadString(
  208. NULL,
  209. SID_COMPRESSING_LZX,
  210. ErrorMsg,
  211. 1024
  212. );
  213. printf(ErrorMsg, pszSource, pszDest,
  214. CompressionMemoryFromTCOMP(DiamondCompressionType)
  215. );
  216. break;
  217. case QUANTUM_ALG:
  218. LoadString(
  219. NULL,
  220. SID_COMPRESSING_QUANTUM,
  221. ErrorMsg,
  222. 1024
  223. );
  224. printf(ErrorMsg, pszSource, pszDest,
  225. CompressionLevelFromTCOMP(DiamondCompressionType),
  226. CompressionMemoryFromTCOMP(DiamondCompressionType)
  227. );
  228. break;
  229. default:
  230. LoadString(
  231. NULL,
  232. (byteAlgorithm == MSZIP_ALG) ? SID_COMPRESSING_MSZIP : SID_COMPRESSING,
  233. ErrorMsg,
  234. 1024
  235. );
  236. printf(ErrorMsg, pszSource, pszDest);
  237. }
  238. }
  239. break;
  240. default:
  241. break;
  242. }
  243. return(TRUE);
  244. }
  245. //
  246. // static BOOL FileTimeIsNewer( const char* pszFile1, const char* pszFile2 );
  247. //
  248. // Return value is TRUE if time stamp on pszFile1 is newer than the
  249. // time stamp on pszFile2. If either of the two files do not exist,
  250. // the return value is also TRUE (for indicating that pszFile2 should
  251. // be update from pszFile1). Otherwise, the return value is FALSE.
  252. //
  253. static BOOL FileTimeIsNewer( const char* pszFile1, const char* pszFile2 ) {
  254. struct _stat StatBufSource,
  255. StatBufDest;
  256. if (( _stat( pszFile2, &StatBufDest )) ||
  257. ( _stat( pszFile1, &StatBufSource )) ||
  258. ( StatBufSource.st_mtime > StatBufDest.st_mtime ))
  259. return TRUE;
  260. return FALSE;
  261. }
  262. LPSTR
  263. ValidListEntry(
  264. LPSTR szArg
  265. )
  266. {
  267. // Check for special character at front of file
  268. if ( '@' == szArg[0] )
  269. return szArg + 1;
  270. else
  271. return NULL;
  272. }
  273. BOOL
  274. GetNextFileListFile(
  275. const LPSTR szFileList,
  276. char **pszSource,
  277. char **pszDest
  278. )
  279. {
  280. static char szList[MAX_PATH] = {0},
  281. szSource[MAX_PATH] = {0},
  282. szDest[MAX_PATH] = {0};
  283. static BOOL bParsingFile = FALSE;
  284. static FILE *hFile;
  285. static int dEntryNum = 1;
  286. int dRetVal;
  287. // Initialize out paramters to NULL
  288. *pszSource = *pszDest = NULL;
  289. // Open file if we are not currently parsing another one
  290. if ( !bParsingFile ) {
  291. // Do not reopen last file used as this is our signal to stop
  292. if ( !_stricmp( szFileList, szList ) ) {
  293. return TRUE;
  294. }
  295. // Attempt to open specified file
  296. hFile = fopen( szFileList, "rt" );
  297. if ( NULL == hFile ) {
  298. LoadString( NULL, SID_NO_OPEN_INPUT, ErrorMsg, 1024 );
  299. printf( ErrorMsg, szFileList );
  300. return FALSE;
  301. }
  302. // Store new file name in static buffer
  303. strcpy( szList, szFileList );
  304. bParsingFile = TRUE;
  305. }
  306. dRetVal = fscanf( hFile, "%s %s", szSource, szDest );
  307. if ( EOF == dRetVal ) {
  308. fclose( hFile );
  309. bParsingFile = FALSE;
  310. return TRUE;
  311. }
  312. else if ( 0 == dRetVal ) {
  313. LoadString( NULL, SID_INVALID_LIST_FILE, ErrorMsg, 1024 );
  314. printf( ErrorMsg, dEntryNum );
  315. return FALSE;
  316. }
  317. // Point to new source and destination entries
  318. *pszSource = szSource;
  319. *pszDest = szDest;
  320. // Track entry
  321. dEntryNum++;
  322. return TRUE;
  323. }
  324. /*
  325. ** int main(int argc, char *argv[]);
  326. **
  327. ** Run command-line file compression program.
  328. **
  329. ** Arguments: figure it out
  330. **
  331. ** Returns: int - EXIT_SUCCESS if compression finished successfully,
  332. ** EXIT_FAILURE if not.
  333. **
  334. ** Globals: none
  335. */
  336. INT __cdecl main(INT argc, CHAR *argv[])
  337. {
  338. INT iSourceFileName,
  339. fError,
  340. nTotalFiles = 0,
  341. nReturnCode = EXIT_SUCCESS;
  342. CHAR ARG_PTR pszDestFileName[MAX_PATH];
  343. CHAR chTargetFileName[ MAX_PATH ];
  344. LONG cblTotInSize = 0L,
  345. cblTotOutSize = 0L;
  346. PLZINFO pLZI;
  347. USHORT wLanguageId = LANGIDFROMLCID(GetThreadLocale());
  348. if ((LANG_JAPANESE == PRIMARYLANGID(wLanguageId)) ||
  349. (LANG_KOREAN == PRIMARYLANGID(wLanguageId)) ||
  350. (LANG_CHINESE == PRIMARYLANGID(wLanguageId)))
  351. {
  352. //
  353. // This used to be #ifdef DBCS. Now a runtime check.
  354. //
  355. DWORD dw = GetConsoleOutputCP();
  356. switch (dw) {
  357. case 932:
  358. case 936:
  359. case 949:
  360. case 950:
  361. SetThreadLocale(MAKELCID(
  362. MAKELANGID(
  363. PRIMARYLANGID(GetSystemDefaultLangID()),
  364. SUBLANG_ENGLISH_US),
  365. SORT_DEFAULT));
  366. break;
  367. default:
  368. SetThreadLocale(MAKELCID(
  369. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  370. SORT_DEFAULT ) );
  371. break;
  372. }
  373. }
  374. // Parse command-line arguments.
  375. if (ParseArguments(argc, argv) != TRUE)
  376. return(EXIT_FAILURE);
  377. // Display sign-on banner.
  378. if ( bNoLogo == FALSE ) {
  379. LoadString(NULL, SID_BANNER_TEXT, ErrorMsg, 1024);
  380. printf(ErrorMsg);
  381. }
  382. // Set up global target path name.
  383. pszTargetName = argv[iTarget];
  384. if (bDisplayHelp == TRUE)
  385. {
  386. // User asked for help.
  387. LoadString(NULL, SID_INSTRUCTIONS, ErrorMsg, 1024);
  388. printf(ErrorMsg);
  389. LoadString(NULL, SID_INSTRUCTIONS2, ErrorMsg, 1024);
  390. printf(ErrorMsg);
  391. LoadString(NULL, SID_INSTRUCTIONS3, ErrorMsg, 1024);
  392. printf(ErrorMsg);
  393. return(EXIT_SUCCESS);
  394. }
  395. // Check for command line problems.
  396. if (CheckArguments() == FALSE)
  397. return(EXIT_FAILURE);
  398. // Set up ring buffer and I/O buffers.
  399. pLZI = InitGlobalBuffersEx();
  400. if (!pLZI)
  401. {
  402. LoadString(NULL, SID_INSUFF_MEM, ErrorMsg, 1024);
  403. printf(ErrorMsg);
  404. return(EXIT_FAILURE);
  405. }
  406. // Process each source file.
  407. while ((iSourceFileName = GetNextFileArg(argv)) != FAIL)
  408. {
  409. char *pszFileList = NULL,
  410. *pszCurFile,
  411. *pszCurDestFile;
  412. // Determine if this is a directive file
  413. if ( pszFileList = ValidListEntry( argv[iSourceFileName] ) ) {
  414. if ( !GetNextFileListFile( pszFileList, &pszCurFile, &pszCurDestFile ) ) {
  415. return (EXIT_FAILURE);
  416. }
  417. // Handle empty directive lists
  418. if ( NULL == pszCurFile ) continue;
  419. }
  420. // Otherwise use current argument as file to compress
  421. else {
  422. pszCurFile = argv[iSourceFileName];
  423. }
  424. do {
  425. // Set up global input file name.
  426. pszInFileName = CharLowerA(pszCurFile);
  427. // Set up global output file name.
  428. if ( NULL == pszFileList ) {
  429. MakeDestFileName(argv, pszDestFileName);
  430. pszOutFileName = CharLowerA(pszDestFileName);
  431. }
  432. else {
  433. pszOutFileName = CharLowerA(pszCurDestFile);
  434. }
  435. strcpy( chTargetFileName, pszOutFileName );
  436. if ( bDoRename )
  437. MakeCompressedName( chTargetFileName );
  438. if (( ! bUpdateOnly ) ||
  439. ( FileTimeIsNewer( pszInFileName, chTargetFileName ))) {
  440. if(DiamondCompressionType) {
  441. fError = DiamondCompressFile(ProcessNotification,pszInFileName,
  442. pszOutFileName,bDoRename,pLZI);
  443. } else {
  444. fError = Compress(ProcessNotification, pszInFileName,
  445. pszOutFileName, byteAlgorithm, bDoRename, pLZI);
  446. }
  447. if(fError != TRUE)
  448. // Deal with returned error codes.
  449. DisplayErrorMessage(nReturnCode = fError);
  450. else
  451. {
  452. nTotalFiles++;
  453. if (pLZI && pLZI->cblInSize && pLZI->cblOutSize) {
  454. // Keep track of cumulative statistics.
  455. cblTotInSize += pLZI->cblInSize;
  456. cblTotOutSize += pLZI->cblOutSize;
  457. // Display report for each file.
  458. LoadString(NULL, SID_FILE_REPORT, ErrorMsg, 1024);
  459. printf(ErrorMsg, pszInFileName, pLZI->cblInSize, pLZI->cblOutSize,
  460. (INT)(100 - ((100 * (LONGLONG) pLZI->cblOutSize) / pLZI->cblInSize)));
  461. }
  462. else {
  463. LoadString(NULL, SID_EMPTY_FILE_REPORT, ErrorMsg, 1024);
  464. printf(ErrorMsg, pszInFileName, 0, 0);
  465. }
  466. }
  467. // Separate individual file processing message blocks by a blank line.
  468. printf("\n");
  469. }
  470. // If we are processing a directive file, get the next arguments
  471. if ( NULL != pszFileList ) {
  472. if ( !GetNextFileListFile( pszFileList, &pszCurFile, &pszCurDestFile ) ) {
  473. return (EXIT_FAILURE);
  474. }
  475. }
  476. } while ( NULL != pszFileList && NULL != pszCurFile );
  477. }
  478. // Free memory used by ring buffer and I/O buffers.
  479. FreeGlobalBuffers(pLZI);
  480. // Display cumulative report for multiple files.
  481. if (nTotalFiles > 1) {
  482. // Scale results to get accurate %
  483. LONG cblAdjInSize = cblTotInSize,
  484. cblAdjOutSize = cblTotOutSize;
  485. while (cblAdjInSize > 100000) {
  486. cblAdjInSize /= 2;
  487. cblAdjOutSize /= 2;
  488. }
  489. cblAdjOutSize += (cblAdjInSize / 200); // round off (+0.5%)
  490. if (cblAdjOutSize < 0) {
  491. cblAdjOutSize = 0;
  492. }
  493. LoadString(NULL, SID_TOTAL_REPORT, ErrorMsg, 1024);
  494. printf(ErrorMsg, nTotalFiles, cblTotInSize, cblTotOutSize,
  495. (INT)(100 - 100 * cblAdjOutSize / cblAdjInSize));
  496. }
  497. return(nReturnCode);
  498. }