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.

3232 lines
105 KiB

  1. /*** extract.c - Main program for EXTRACT.EXE
  2. *
  3. * Microsoft Confidential
  4. * Copyright (C) Microsoft Corporation 1994-1997
  5. * All Rights Reserved.
  6. *
  7. * Author:
  8. * Benjamin W. Slivka
  9. *
  10. * History:
  11. * 19-Feb-1994 bens Initial version (started with diamond.c)
  12. * 22-Feb-1994 bens Implement file extract
  13. * 03-Mar-1994 bens Split cabinet paths from cabinet file names
  14. * 08-Mar-1994 bens Add date/time/attribute display
  15. * 09-Mar-1994 bens Improve response to FDI errors
  16. * 16-Mar-1994 bens More FDI error codes
  17. * 21-Mar-1994 bens Log all open/close calls to see if we are
  18. * losing file handles in FDI.LIB.
  19. * 28-Mar-1994 bens Handle fdintCABINET_INFO, support /A switch
  20. * 30-Mar-1994 bens Move MS-DOS/Win32 knowledge to fileutil.*
  21. * 31-Mar-1994 bens Add SMALL_DOS to test small model FDI client
  22. * 01-Apr-1994 bens Add /D and /E switches, support full command
  23. * line behavior.
  24. * 07-Apr-1994 bens Add crypto support (at least for debugging)
  25. * 06-May-1994 bens Improve /D display for long filenames
  26. * 13-May-1994 bens Add prompting for next cabinet, DMF support
  27. * 27-May-1994 bens Include correct strings for localization
  28. * 03-Jun-1994 bens Report error on correct cabinet
  29. * 07-Jun-1994 bens Localization enabled
  30. * 21-Jun-1994 bens Localization enabled
  31. * 08-Jul-1994 bens Quantum Spill File, self-extracting cabinets!
  32. * 11-Jul-1994 bens Use 24-hour time format if am/pm strings are empty
  33. * 26-Jul-1994 bens Add /C switch; no switches give /? help
  34. * 05-Aug-1994 bens Chicago bug 13214 (don't show partial file info
  35. * unless name matches request). Chicago bug 13221
  36. * (truncate extracted file to specified size, in
  37. * case file already existed and was larger!).
  38. * Chicago bug 9646 (give details of Quantum
  39. * decompress failure -- out of RAM, spill file).
  40. * Implement overwrite prompt and /Y switch.
  41. * 14-Dec-1994 bens Include Floppy changeline fix from
  42. * ..\dmf\dmftsr\fixchg.c
  43. * 12-Mar-1995 bens Define NOT_US_PC flag to disable use of DMF hook
  44. * and FixChangeline code. Also, check COMSPEC to
  45. * detect boot drive, instead of hard-coding C:, so
  46. * that the Quantum spill file can default to the
  47. * boot drive if no TEMP path is found. In the far
  48. * east, the hard disk boot drive is A:, so that's
  49. * why we have to check!
  50. * 31-Mar-1995 jeffwe Fix Command line ambiguity when no /D or /E
  51. * option is specified
  52. * 2-Apr-1995 jeffwe Fix file time/date set to change the correct
  53. * file when rename option being used
  54. * 28-Feb-1997 msliger Added /Z option to zap paths from CABs. Fixed
  55. * 32-bit self-extract feature.
  56. * 18-Mar-1997 msliger Mask attribs to watch out for UTF et al.
  57. * 24-Mar-1997 msliger Fix self-extract on NT (don't use argv[0])
  58. * 13-May-1997 msliger Merged in deltas for GUI Extrac32.EXE
  59. * 26-Jun-1997 msliger Support XIMPLANT self-extract (CAB is an added
  60. * section in the PE file with a certain name; this
  61. * makes self-extractors Authenticode 2 compatible.
  62. * 01-Jul-1997 msliger Fix reporting wrong cab name for corrupt cabinets.
  63. * 22-Mar-1999 msliger Added support for CAB-destructive extraction.
  64. *
  65. *
  66. * Notes:
  67. * A self-extracting cabinet file can be created using DIAMOND.EXE and
  68. * EXTRACT.EXE very simply:
  69. * 1) Create a cabinet file using DIAMOND.EXE
  70. * 2) COPY /B EXTRACT.EXE+foo.cab foo.exe
  71. * When EXTRACT starts executing, it compares the file size indicated
  72. * in the EXE headers (MZ or PE, as appropriate) with the size of the
  73. * file indicated in argv[0]. If the argv[0] size is greater, and a
  74. * cabinet file appears there, then EXTRACT goes into self-extracting
  75. * mode!
  76. *
  77. * However, the EXE created this way is not Authenticode 2-compatible.
  78. *
  79. * For Authenticode compatibility, 32-bit versions can also:
  80. * 1) Create a cabinet file using DIAMOND.EXE
  81. * 2) XIMPLANT EXTRACT.EXE foo.cab foo.exe
  82. * This buries the cabinet inside the PE image in a way that doesn't
  83. * offend Authenticode 2.
  84. */
  85. //#include "resource.h"
  86. //#include "pch.h"
  87. #ifdef WIN32GUI
  88. #include "extrac32.rc"
  89. #else
  90. #include "extract.rc"
  91. #endif
  92. #include <stdlib.h>
  93. #include <stdio.h>
  94. #include <ctype.h>
  95. #include <string.h>
  96. #include <malloc.h>
  97. #include <fcntl.h>
  98. #include <sys\types.h>
  99. #include <sys\stat.h>
  100. #include <io.h>
  101. #include <errno.h>
  102. #include <direct.h>
  103. #include <conio.h>
  104. #ifdef BIT16
  105. #include <dos.h>
  106. #include "fixchg.h"
  107. #else // !BIT16
  108. //** Get minimal Win32 definitions
  109. #ifndef WIN32_LEAN_AND_MEAN
  110. #define WIN32_LEAN_AND_MEAN
  111. #endif
  112. #include <windows.h>
  113. #undef ERROR // Override "#define ERROR 0" in wingdi.h
  114. #endif // !BIT16
  115. #ifdef WIN32GUI
  116. #include <commctrl.h>
  117. #include "extrcids.h"
  118. #endif
  119. #include "types.h"
  120. #include "asrt.h"
  121. #include "error.h"
  122. #include "mem.h"
  123. #include "message.h"
  124. #include "filelist.h"
  125. #include "fileutil.h"
  126. #include "wildcard.h"
  127. #include "dmfon.h" // DMF support
  128. #include <extract.msg> // LOCALIZED for EXTRACT.EXE -- specify "cl /Ipath"
  129. #include "fdi.h"
  130. #include "oldnames.h"
  131. //** Constants
  132. #define cbMAX_LINE 256 // Maximum output line length
  133. #define cMAX_CAB_FILE_OPEN 2 // Maximum simultaneous opens on a single
  134. // cabinet file
  135. //** Error causes for failure of spill file
  136. typedef enum {
  137. seNONE, // No error
  138. seNOT_ENOUGH_MEMORY, // Not enough RAM
  139. seCANNOT_CREATE, // Cannot create spill file
  140. seNOT_ENOUGH_SPACE, // Not enough space for spill file
  141. } SPILLERR; /* se */
  142. //** Types
  143. typedef enum {
  144. actBAD, // Invalid action
  145. actHELP, // Show help
  146. actDEFAULT, // Perform default action based on command line arguments
  147. actDIRECTORY, // Force display of cabinet directory
  148. actEXTRACT, // Force file extraction
  149. actCOPY, // Do single file-to-file copy
  150. } ACTION; /* act */
  151. typedef struct {
  152. char achCabPath[cbFILE_NAME_MAX]; // Cabinet file path
  153. char achCabFilename[cbFILE_NAME_MAX]; // Cabinet file name.ext
  154. char achDiskName[cbFILE_NAME_MAX]; // User readable disk label
  155. USHORT setID;
  156. USHORT iCabinet;
  157. } CABINET; /* cab */
  158. typedef CABINET *PCABINET; /* pcab */
  159. #ifdef ASSERT
  160. #define sigSESSION MAKESIG('S','E','S','S') // SESSION signature
  161. #define AssertSess(psess) AssertStructure(psess,sigSESSION);
  162. #else // !ASSERT
  163. #define AssertSess(psess)
  164. #endif // !ASSERT
  165. typedef struct {
  166. #ifdef ASSERT
  167. SIGNATURE sig; // structure signature sigSESSION
  168. #endif
  169. ACTION act; // Action to perform
  170. HFILELIST hflist; // List of files specified on cmd line
  171. BOOL fAllCabinets; // TRUE => Process continutation cabs
  172. BOOL fOverwrite; // TRUE => Overwrite existing files
  173. BOOL fNoLineFeed; // TRUE if last printf did not have \n
  174. BOOL fSelfExtract; // TRUE if self-extracting
  175. long cbSelfExtract; // Size of EXE portion of self-ex cabinet
  176. long cbSelfExtractSize; // Size of CAB portion of self-ex cabinet
  177. int ahfSelf[cMAX_CAB_FILE_OPEN]; // Cabinet file handles
  178. int cErrors; // Count of errors encountered
  179. HFDI hfdi; // FDI context
  180. ERF erf; // FDI error structure
  181. long cFiles; // Total files processed
  182. long cbTotalBytes; // Total bytes extracted
  183. PERROR perr; // Pass through FDI
  184. SPILLERR se; // Spill file error
  185. long cbSpill; // Size of spill file requested
  186. char achSelf[cbFILE_NAME_MAX]; // Name of our EXE file
  187. char achMsg[cbMAX_LINE*2]; // Message formatting buffer
  188. char achLine[cbMAX_LINE]; // Line formatting buffer
  189. char achLocation[cbFILE_NAME_MAX]; // Output directory
  190. char achFile[cbFILE_NAME_MAX]; // Current filename being extracted
  191. char achDest[cbFILE_NAME_MAX]; // Forced destination file name
  192. char achCabPath[cbFILE_NAME_MAX]; // Path to look for cabinet file
  193. BOOL fContinuationCabinet; // TRUE => not 1st cabinet processed
  194. BOOL fShowReserveInfo; // TRUE => show RESERVEd cabinet info
  195. //** fNextCabCalled allows us to figure out which of the acab[] entries
  196. // to use if we are processing all file in a cabinet set (i.e., if
  197. // fAllCabinet is TRUE). If fdintNEXT_CABINET has never been called,
  198. // then acab[1] has the information for the next cabinet. But if
  199. // it has been called, then fdintCABINET_INFO will have been called
  200. // at least twice (once for the first cabinet, and once at least for
  201. // a continuation cabinet), and so acab[0] is the cabinet we need to
  202. // pass to a subsequent FDICopy() call.
  203. BOOL fNextCabCalled; // TRUE => GetNextCabinet called
  204. CABINET acab[2]; // Last two fdintCABINET_INFO data sets
  205. char achZap[cbFILE_NAME_MAX]; // prefix to strip from filename
  206. char achCabinetFile[cbFILE_NAME_MAX]; // current cabinet file
  207. int cArgv; // default self-ext argc
  208. char **pArgv; // default self-ext argv[]
  209. int fDestructive; // TRUE => minimize disk space needed
  210. USHORT iCurrentFolder; // iff fDestructive, only extract this one
  211. } SESSION; /* sess */
  212. typedef SESSION *PSESSION; /* psess */
  213. /*
  214. ** Spill file statics for Quantum
  215. */
  216. INT_PTR hfSpillFile; // File handle
  217. char achSpillFile[cbFILE_NAME_MAX]; // File path
  218. /*
  219. ** Global state for self-extract
  220. */
  221. PSESSION psessG;
  222. #ifdef WIN32GUI
  223. /*
  224. ** GUI support
  225. */
  226. static INT_PTR hCabFile1 = -1;
  227. static INT_PTR hCabFile2 = -1;
  228. static unsigned long ibCabFilePosition1;
  229. static unsigned long ibCabFilePosition2;
  230. static unsigned long cbCabFileMax;
  231. static unsigned long cbCabFileTotal;
  232. static unsigned long cbCabFileScale;
  233. static int iPercentLast;
  234. static void ProgressReport(unsigned long cbCabFileMax);
  235. LRESULT CALLBACK ProgressWndProc(HWND hdlg, UINT msg,
  236. WPARAM wparam, LPARAM lparam);
  237. static HINSTANCE g_hinst;
  238. static HWND g_hwndProgress = NULL;
  239. #endif
  240. //** Function Prototypes
  241. FNASSERTFAILURE(fnafReport);
  242. HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr);
  243. BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr);
  244. BOOL doCabinet(PSESSION psess, PERROR perr);
  245. BOOL doCopy(PSESSION psess, PERROR perr);
  246. BOOL ensureCabinet(PSESSION psess,
  247. char *pszPath,
  248. int cbPath,
  249. char *pszFile,
  250. char *pszLabel,
  251. USHORT setID,
  252. USHORT iCabinet,
  253. BOOL fLoop,
  254. BOOL fPromptOnly,
  255. PERROR perr);
  256. BOOL checkOverwrite(PSESSION psess,
  257. char *pszFile,
  258. PERROR perr,
  259. int *prc);
  260. BOOL checkSelfExtractingCab(PSESSION psess,
  261. int cArg,
  262. char *apszArg[],
  263. PERROR perr);
  264. char *getBootDrive(void);
  265. BOOL parseCommandLine(PSESSION psess,int cArg,char *apszArg[],PERROR perr);
  266. void printError(PSESSION psess, PERROR perr);
  267. void pszFromAttrFAT(char *psz, int cb, WORD attrFAT);
  268. void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time);
  269. int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin);
  270. //** FDI callbacks and related functions
  271. FNALLOC(fdiAlloc);
  272. FNFREE(fdiFree);
  273. FNFDINOTIFY(fdiNotifyDir);
  274. FNFDINOTIFY(fdiNotifyExt);
  275. FNFDINOTIFY(doGetNextCab);
  276. FNFDIDECRYPT(fdiDecryptDir);
  277. FNFDIDECRYPT(fdiDecryptExt);
  278. void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf);
  279. //** File I/O wrapper functions
  280. INT_PTR FAR DIAMONDAPI wrap_open(char FAR *, int, int);
  281. UINT FAR DIAMONDAPI wrap_read(INT_PTR, void FAR *, unsigned int);
  282. UINT FAR DIAMONDAPI wrap_write(INT_PTR, void FAR *, unsigned int);
  283. int FAR DIAMONDAPI wrap_close(INT_PTR);
  284. long FAR DIAMONDAPI wrap_lseek(INT_PTR, long, int);
  285. #ifdef SMALL_DOS
  286. #define STRCPY(dst,src) _fstrcpy((char far *)dst,(char far *)src)
  287. #else
  288. #define STRCPY(dst,src) strcpy(dst,src)
  289. #endif
  290. //FEATURE: 08-Jul-1994 bens Generate debug output
  291. //#define DEBUG_FDI 1
  292. #ifdef DEBUG_FDI
  293. #define dbg(a) a
  294. #else
  295. #define dbg(a)
  296. #endif
  297. #define HELP_MAX_SIZE 4096
  298. #define MAX_MESSAGE_SIZE 256
  299. #define EMPTY_SPACE L" "
  300. //** Functions
  301. /*** main - Extract main program
  302. *
  303. * See DIAMOND.DOC for spec and operation.
  304. *
  305. * NOTE: We're sloppy, and don't free resources allocated by
  306. * functions we call, on the assumption that program exit
  307. * will clean up memory and file handles for us.
  308. */
  309. int __cdecl wmain(DWORD cArg, LPWSTR apszArg[])
  310. {
  311. ERROR err;
  312. PSESSION psess;
  313. WCHAR szTemp[HELP_MAX_SIZE] = L"";
  314. DWORD dw = 0;
  315. LPSTR *szArg = NULL;
  316. LPSTR szTempArg;
  317. // #define NTVCPP_DEBUG_HACK
  318. #ifdef NTVCPP_DEBUG_HACK
  319. _chdir("\\elroy\\diamond\\layout\\testnew");
  320. #endif
  321. AssertRegisterFunc(fnafReport); // Register assertion reporter
  322. ErrClear(&err); // No error
  323. err.pszFile = NULL; // No file being processed, yet
  324. achSpillFile[0] = '\0'; // No name constructed, yet
  325. #ifdef BIT16
  326. #ifndef NOT_US_PC
  327. //** Make sure we can read DMF disks -- only for pre-Chicago systems
  328. EnableDMFSupport();
  329. //** Turn off floppy disk changeline support to make sure systems
  330. // with faulty change lines don't choke on disk 2 in the case
  331. // where disk 1 is non-DMF and disk 2 is DMF.
  332. FixChangelines();
  333. #endif
  334. #endif
  335. if( cArg<=1 )
  336. {
  337. fwprintf( stderr, pszINVALID_SYNTAX );
  338. fwprintf( stderr, pszHELP_MESSAGE );
  339. return( EXIT_FAILURE);
  340. }
  341. //** Initialize session
  342. psess = MemAlloc(sizeof(SESSION));
  343. if (!psess) {
  344. ErrSet(&err,pszEXTERR_NO_SESSION);
  345. printError(psess,&err);
  346. exit(1);
  347. }
  348. SetAssertSignature((psess),sigSESSION);
  349. psessG = psess; // Save for wrap_open/wrap_close
  350. psess->fOverwrite = FALSE; // Default to being save
  351. psess->fAllCabinets = FALSE; // Don't do continuation cabinets
  352. psess->fNextCabCalled = FALSE;
  353. psess->fContinuationCabinet = FALSE;
  354. psess->fShowReserveInfo = FALSE;
  355. psess->fSelfExtract = FALSE;
  356. psess->hflist = NULL;
  357. psess->hfdi = NULL;
  358. psess->fNoLineFeed = 0; // TRUE if last printf did not have \n
  359. psess->cFiles = 0; // No files, yet
  360. psess->cbTotalBytes = 0; // No bytes, yet
  361. psess->se = seNONE; // No spill file error
  362. psess->achZap[0] = '\0'; // No Zap pattern, yet
  363. psess->cArgv = 0; // No default self-ext cmd line
  364. psess->fDestructive = FALSE; // Not truncating cab during extract
  365. //** Print Extract banner
  366. if (psess->act == actHELP) { // Do help if any args, for now
  367. fwprintf(stdout, L"\n"); // Separate banner from help
  368. MultiByteToWideChar( CP_THREAD_ACP, 0, pszBANNER, strlen(pszBANNER),
  369. szTemp, HELP_MAX_SIZE );
  370. fwprintf(stderr, szTemp);
  371. ZeroMemory(szTemp, HELP_MAX_SIZE);
  372. }
  373. //** Parse command line, convert command line wide char strings to LPSTR
  374. szArg = (LPSTR *)malloc( cArg*sizeof(LPSTR*) );
  375. if( NULL == szArg )
  376. {
  377. fwprintf( stderr, L"ERROR: Memory Allocation failed.\n" );
  378. //** Free resources
  379. AssertSess(psess);
  380. ClearAssertSignature((psess));
  381. MemFree(psess);
  382. return EXIT_FAILURE;
  383. }
  384. for(dw=0; dw<cArg; dw++ )
  385. {
  386. szTempArg =(LPSTR) malloc( wcslen(apszArg[dw] ) );
  387. if( NULL == szTempArg )
  388. {
  389. fwprintf( stderr, L"ERROR: Memory Allocation failed.\n" );
  390. if( szArg != NULL )
  391. {
  392. free(szArg );
  393. szArg = NULL;
  394. }
  395. //** Free resources
  396. AssertSess(psess);
  397. ClearAssertSignature((psess));
  398. MemFree(psess);
  399. return EXIT_FAILURE;
  400. }
  401. ZeroMemory( szTempArg, wcslen(apszArg[dw]) );
  402. if( FALSE == WideCharToMultiByte( CP_THREAD_ACP, 0, apszArg[dw], wcslen(apszArg[dw]),
  403. szTempArg, wcslen(apszArg[dw]) , NULL, NULL ) )
  404. fwprintf( stderr, L"Error\n" );
  405. *(szTempArg+wcslen(apszArg[dw])) = '\0';
  406. szArg[dw] = szTempArg;
  407. }
  408. if (!parseCommandLine(psess,(int)cArg,szArg,&err)) {
  409. printError(psess,&err);
  410. if( szArg != NULL )
  411. {
  412. free(szArg );
  413. szArg = NULL;
  414. }
  415. if( szTempArg != NULL )
  416. {
  417. free(szTempArg );
  418. szTempArg = NULL;
  419. }
  420. //** Free resources
  421. AssertSess(psess);
  422. ClearAssertSignature((psess));
  423. MemFree(psess);
  424. return 1;
  425. }
  426. /*
  427. //free the memory for szArg
  428. for( dw=0; dw<cArg; dw++ )
  429. {
  430. szTempArg = szArg[dw];
  431. if( szTempArg != NULL )
  432. free(szTempArg );
  433. } */
  434. if( szTempArg != NULL )
  435. {
  436. free(szTempArg );
  437. szTempArg = NULL;
  438. }
  439. if( szArg != NULL )
  440. {
  441. free(szArg );
  442. szArg = NULL;
  443. }
  444. // szArg = NULL;
  445. //** Quick out if command line help is requested
  446. if (psess->act == actHELP) { // Do help if any args, for now
  447. fwprintf(stdout, L"\n"); // Separate banner from help
  448. MultiByteToWideChar( CP_THREAD_ACP, 0, pszCMD_LINE_HELP, strlen(pszCMD_LINE_HELP),
  449. szTemp, HELP_MAX_SIZE);
  450. fwprintf( stderr, szTemp );
  451. //** Free resources
  452. AssertSess(psess);
  453. ClearAssertSignature((psess));
  454. MemFree(psess);
  455. return 0;
  456. }
  457. ZeroMemory(szTemp, HELP_MAX_SIZE);
  458. //** Quick out for COPY command
  459. if (psess->act == actCOPY) {
  460. if (!doCopy(psess,&err)) {
  461. printError(psess,&err);
  462. //** Free resources
  463. AssertSess(psess);
  464. ClearAssertSignature((psess));
  465. MemFree(psess);
  466. return 1;
  467. }
  468. //** Success
  469. return 0;
  470. }
  471. //** Have some work to do -- go do it
  472. if (!doCabinet(psess,&err)) {
  473. printError(psess,&err);
  474. //** Make sure we delete spill file
  475. if (hfSpillFile != -1) {
  476. wrap_close(hfSpillFile); // Close and delete it
  477. }
  478. //** Free resources
  479. AssertSess(psess);
  480. ClearAssertSignature((psess));
  481. MemFree(psess);
  482. return 1;
  483. }
  484. //** See if we actually got any files
  485. if (psess->cFiles == 0) {
  486. MsgSet(psess->achMsg,pszEXT_NO_MATCHING_FILES);
  487. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg),
  488. szTemp, HELP_MAX_SIZE);
  489. fwprintf(stderr, L"%s\n",szTemp);
  490. ZeroMemory(szTemp, HELP_MAX_SIZE);
  491. }
  492. else if (psess->act == actDIRECTORY) {
  493. //** Print out file and byte count
  494. MsgSet(psess->achMsg,
  495. psess->cFiles == 1 ? pszEXT_SUMMARY1 : pszEXT_SUMMARY2,
  496. "%,13ld%,13ld",
  497. psess->cFiles, psess->cbTotalBytes);
  498. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg),
  499. szTemp, HELP_MAX_SIZE);
  500. fwprintf(stdout, L"%s\n",szTemp);
  501. ZeroMemory(szTemp, HELP_MAX_SIZE);
  502. }
  503. //** Free resources
  504. AssertSess(psess);
  505. ClearAssertSignature((psess));
  506. MemFree(psess);
  507. //** Success
  508. return 0;
  509. } /* main */
  510. /*** doCopy - Copy one file
  511. *
  512. * Entry:
  513. * psess - Description of operation to perform
  514. * perr - ERROR structure
  515. *
  516. * Exit-Success:
  517. * Returns TRUE; file copied
  518. *
  519. * Exit-Failure:
  520. * Returns FALSE; error
  521. *
  522. * NOTE:
  523. * Supported SRC/DST syntax:
  524. * Src Dst Example
  525. * ---- ---- --------------------------
  526. * file dir "foo.exe ."; "foo.exe c:\dir"
  527. * file file "foo.exe c:bar.exe"
  528. */
  529. BOOL doCopy(PSESSION psess, PERROR perr)
  530. {
  531. char achDst[cbFILE_NAME_MAX]; // Buffer for src file name
  532. HFILESPEC hfspec;
  533. char *pszSrc;
  534. char *pszSrcJustFile;
  535. char *pszDst;
  536. int rc;
  537. struct _stat stat;
  538. WCHAR szTemp[MAX_MESSAGE_SIZE];
  539. //** Get the source file
  540. hfspec = FLFirstFile(psess->hflist);
  541. Assert(hfspec != NULL);
  542. pszSrc = FLGetSource(hfspec);
  543. Assert(pszSrc!=NULL);
  544. Assert(*pszSrc);
  545. //** Get the destination file
  546. hfspec = FLNextFile(hfspec); // We have something
  547. Assert(hfspec != NULL);
  548. pszDst = FLGetSource(hfspec);
  549. Assert(pszDst!=NULL);
  550. Assert(*pszDst);
  551. //** Determine if destination is a directory
  552. if (-1 != _stat(pszDst,&stat)) { // File/Dir exists
  553. //** Destination exists
  554. if (stat.st_mode & _S_IFDIR) { // It is a directory
  555. //** It is a directory; get just file name and extension of source
  556. if (!(pszSrcJustFile = getJustFileNameAndExt(pszSrc,perr))) {
  557. return FALSE;
  558. }
  559. //** Construct destination name
  560. if (!catDirAndFile(
  561. achDst, // Buffer for full path
  562. sizeof(achDst), // Size of buffer
  563. pszDst, // Destination directory
  564. pszSrcJustFile, // File name
  565. NULL, // Don't have alternate name
  566. perr)) {
  567. return FALSE; // Failure
  568. }
  569. //** Use constructed name
  570. pszDst = achDst;
  571. }
  572. }
  573. //** Make sure it's OK to overwrite destination file
  574. if (!checkOverwrite(psess,pszDst,perr,&rc)) {
  575. //** Ignore skip/abort return code in rc
  576. return TRUE; // Skip file copy, everything is OK
  577. }
  578. //** Tell user we're copying the file
  579. MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s",pszSrc,pszDst);
  580. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg) ,
  581. szTemp, MAX_MESSAGE_SIZE );
  582. *(szTemp+strlen(psess->achMsg)) = '\0';
  583. fwprintf(stdout, L"%s\n",szTemp);
  584. //** Do the file copy
  585. return CopyOneFile(pszDst, // destination
  586. pszSrc, // source
  587. TRUE, // do the copy
  588. 32768U, // copy buffer size
  589. NULL, // don't override date/time/attr
  590. NULL, // no callback context
  591. perr);
  592. } /* doCopy() */
  593. /*** doCabinet - Show contents of one or more cabinet files
  594. *
  595. * Entry:
  596. * psess - Description of operation to perform
  597. * perr - ERROR structure
  598. *
  599. * Exit-Success:
  600. * Returns TRUE; directory displayed
  601. *
  602. * Exit-Failure:
  603. * Returns FALSE; perr filled in with details.
  604. */
  605. BOOL doCabinet(PSESSION psess, PERROR perr)
  606. {
  607. char achFile[cbFILE_NAME_MAX]; // Buffer for cabinet file
  608. FDICABINETINFO fdici;
  609. BOOL fCompatibilityCabinet; // TRUE => Exactly 1 file in 1 cabinet
  610. INT_PTR hfCab = -1; // File handle for peeking at cabinet
  611. HFILESPEC hfspec;
  612. int iCab;
  613. PFNFDINOTIFY pfnfdin;
  614. PFNFDIDECRYPT pfnfdid;
  615. char FAR *pbMemReserve; // Make sure we have some working mem
  616. char *pszCabinet; // Cabinet filespec
  617. char *pszCabFile; // Cabinet filename.ext
  618. char *pszDestOrPattern; // Destination or first pattern
  619. WCHAR szTemp[MAX_MESSAGE_SIZE];
  620. //** Get the cabinet name
  621. hfspec = FLFirstFile(psess->hflist);
  622. Assert(hfspec != NULL); // Must have at least one file
  623. pszCabinet = FLGetSource(hfspec);
  624. Assert(pszCabinet!=NULL);
  625. Assert(*pszCabinet);
  626. //** Get the destination file name or first pattern, if present
  627. if (NULL != (hfspec = FLNextFile(hfspec))) { // We have something
  628. pszDestOrPattern = FLGetSource(hfspec);
  629. Assert(pszDestOrPattern!=NULL);
  630. //** NOTE: hfspec must remain pointing to this 2nd file all the
  631. // way down below where we may need to change its value!
  632. }
  633. else {
  634. pszDestOrPattern = NULL; // No second argument on command line
  635. }
  636. //** Remember that we have not yet created a spill file
  637. hfSpillFile = -1; // No spill file, yet
  638. //** Prevent FDI from consuming all available memory
  639. // Why 2048? That's enough for 4 paths, which is more than we
  640. // will ever need.
  641. pbMemReserve = fdiAlloc(2048);
  642. if (!pbMemReserve) {
  643. ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet);
  644. return FALSE;
  645. }
  646. //** Create FDI context so we can get info from the cabinet file
  647. if (!(psess->hfdi = FDICreate(fdiAlloc,
  648. fdiFree,
  649. wrap_open,
  650. wrap_read,
  651. wrap_write,
  652. wrap_close,
  653. wrap_lseek,
  654. cpuUNKNOWN, // Let FDI do the CPU detection
  655. &(psess->erf)
  656. ))) {
  657. //** FDICreate failed, generate error message
  658. mapFDIError(perr,psess,pszCabinet,&(psess->erf));
  659. fdiFree(pbMemReserve); // Free reserved memory
  660. return FALSE;
  661. }
  662. fdiFree(pbMemReserve); // Free it so we can use it
  663. //** Make sure file is a cabinet, and get cabinet info
  664. if (-1 == (hfCab = wrap_open(pszCabinet,_O_BINARY | _O_RDONLY,0))) {
  665. ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",pszCabinet);
  666. goto cleanup;
  667. }
  668. if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) {
  669. if (!ErrIsError(perr)) { // Have to set error message
  670. ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",pszCabinet);
  671. }
  672. goto cleanup;
  673. }
  674. wrap_close(hfCab);
  675. hfCab = -1;
  676. //** No Default Destination
  677. psess->achDest[0] = '\0';
  678. //** If no mode specified, figure out what mode we should be in:
  679. //
  680. // The extract command has ambiguous syntax so we apply the following
  681. // rules to resolve the ambiguity.
  682. //
  683. // Most cabinet file authors use DIAMOND.EXE to create a set of
  684. // cabinet files with many files in it. A typical cabinet set would
  685. // look like:
  686. // (1) Cab#1
  687. // foo.1
  688. // foo.2
  689. // foo.3 - partial
  690. // Cab#2
  691. // foo.3 - continued
  692. // ...
  693. //
  694. // However, there are some "old-style" customers of DIAMOND.EXE that
  695. // like to compress each file independently, producing a "set" of
  696. // cabinet files that each contain exactly one file, i.e.:
  697. // (2) excel.ex_
  698. // excel.in_
  699. // setup.in_
  700. //
  701. // The "_" character in the extension is a hint that the file is
  702. // compressed. However, this isn't useful to this program
  703. //
  704. // Now, the question is, what does the customer want to have happen
  705. // when she types "EXTRACT foo.cab bar"? For the multi-file cabinet
  706. // case (1) above, this means "seach foo.cab and extract all files
  707. // that are named bar". BUT, for the case (2), we have a compatibility
  708. // constraint -- she thinks this means "extract the compressed
  709. // file foo.cab and call the resulting uncompressed file bar".
  710. //
  711. // Another question is what does the customer want to have happen
  712. // when she types "EXTRACT foo.cab"? For the multi-file cabinet
  713. // case (1) above this means list the contents of the cabinet.
  714. // But for case (2), we have a compatibility constraint -- customers
  715. // think this means "extract the compressed file foo.cab".
  716. //
  717. //
  718. // A cabinet is of type (1) if it contains more than one file,
  719. // or has either a previous or next cabinet. Otherwise, it is of
  720. // type (2), i.e., the cabinet has exactly one file, and has no
  721. // previous or next cabinet.
  722. if (psess->act == actDEFAULT) { // No action specified on command line
  723. //** Determine if cabinet is of type (2) described above.
  724. fCompatibilityCabinet = (fdici.cFiles == 1) &&
  725. (! (fdici.hasprev || fdici.hasnext));
  726. //** Now figure out what customer really wants
  727. if (pszDestOrPattern) { // extract foo.cab bar
  728. psess->act = actEXTRACT;
  729. if (fCompatibilityCabinet) {
  730. // Special Case Rename (see above (2))
  731. strcpy(psess->achDest, pszDestOrPattern);
  732. if (!FLSetSource(hfspec,pszALL_FILES,perr)) {
  733. goto cleanup;
  734. }
  735. }
  736. } else { // extract foo.cab
  737. if (fCompatibilityCabinet) {
  738. // Special Case Extract (see above (2))
  739. psess->act = actEXTRACT;
  740. } else {
  741. psess->act = actDIRECTORY;
  742. }
  743. }
  744. }
  745. //** Supply a default pattern if no pattern is present
  746. if (!pszDestOrPattern) {
  747. if (addFileSpec(psess,pszALL_FILES,perr) == NULL) {
  748. ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",pszALL_FILES);
  749. goto cleanup;
  750. }
  751. }
  752. //** Now, select the appropriate FDI notification function
  753. Assert((psess->act == actEXTRACT) || (psess->act == actDIRECTORY));
  754. pfnfdin = (psess->act == actEXTRACT) ? fdiNotifyExt : fdiNotifyDir;
  755. if (fdici.fReserve) { // Reserved area(s) present
  756. pfnfdid = (psess->act == actEXTRACT) ? fdiDecryptExt : fdiDecryptDir;
  757. }
  758. else {
  759. pfnfdid = NULL; // No reserved areas
  760. }
  761. //** Split cabinet spec into path and filename.ext
  762. pszCabFile = getJustFileNameAndExt(pszCabinet,perr);
  763. if (pszCabFile == NULL) {
  764. goto cleanup; // perr is already filled in
  765. }
  766. strcpy(achFile,pszCabFile);
  767. //** Need to trim off file name and keep just cabinet path
  768. strcpy(psess->achCabPath,pszCabinet); // Store in our buffer
  769. psess->achCabPath[pszCabFile - pszCabinet] = '\0'; // Trim off file name
  770. if (psess->fDestructive) {
  771. // don't do destructive extract on chained cabinets
  772. if ((psess->act != actEXTRACT) ||
  773. fdici.hasprev ||
  774. fdici.hasnext) {
  775. psess->fDestructive = FALSE;
  776. }
  777. else {
  778. psess->iCurrentFolder = fdici.cFolders - 1;
  779. }
  780. }
  781. psess->perr = perr; // Pass perr through FDI
  782. //** Do cabinets until there are no more, or an error occurs
  783. while ( (strlen(achFile) > 0) && !ErrIsError(perr) ) {
  784. //** Show which cabinet we are processing
  785. MsgSet(psess->achMsg,pszEXT_CABINET_HEADER,"%s",achFile);
  786. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen( psess->achMsg ), szTemp, MAX_MESSAGE_SIZE );
  787. *(szTemp+strlen(psess->achMsg)) = '\0';
  788. fwprintf(stdout, L"\n%s\n\n",szTemp);
  789. strcpy(psess->achCabinetFile,achFile);
  790. //** Do the cabinet
  791. if (!FDICopy(psess->hfdi, // FDI context
  792. achFile, // Cabinet file name.ext
  793. psess->achCabPath, // Path to cabinet
  794. 0, // Flags (???)
  795. pfnfdin, // Notifcation callback
  796. pfnfdid, // Decrypt callback
  797. psess // Our context
  798. )) {
  799. //** NOTE: psess->achCabPath *may* get changed during an
  800. // fdintNEXT_CABINET callback if we had to prompt the
  801. // use for a different path!
  802. //** FDICopy failed, construct error message
  803. if (!ErrIsError(perr)) { // Need to set error message
  804. //** Construct error message
  805. mapFDIError(perr,psess,psess->achCabinetFile,&(psess->erf));
  806. //** Delete file if created, with the assumption that
  807. // we were not able to completely write the file
  808. // (for example, if the destination disk ran out of space!)
  809. if (psess->erf.erfOper == FDIERROR_TARGET_FILE) {
  810. //** Ignore errors, if any
  811. _unlink(psess->achFile);
  812. }
  813. }
  814. }
  815. else {
  816. //** OK so far, see if any more cabinets to process
  817. if (psess->fDestructive) {
  818. if (psess->iCurrentFolder != 0) {
  819. FDITruncateCabinet(psess->hfdi,
  820. pszCabinet,
  821. psess->iCurrentFolder);
  822. psess->iCurrentFolder--; // move down to next folder
  823. }
  824. else {
  825. _unlink(pszCabinet); // delete the CAB!
  826. achFile[0] = '\0'; // Done
  827. }
  828. }
  829. else if (psess->fAllCabinets) {
  830. //** Skip "starts in ..." messages for subsequent cabinets
  831. psess->fContinuationCabinet = TRUE;
  832. //** Copy next cabinet file (ach[] is empty if no more!)
  833. iCab = psess->fNextCabCalled ? 0 : 1; // Select correct cabinet
  834. strcpy(achFile,psess->acab[iCab].achCabFilename);
  835. strcpy(psess->achCabinetFile,achFile);
  836. psess->fNextCabCalled = FALSE; // Reset flag
  837. //** If there is another cabinet to process, make sure it
  838. // is available; psess->achCabPath may be edited if we
  839. // can't find the cabinet until the user supplies another
  840. // path; perr will be set if an error occurs.
  841. if (achFile[0] != '\0') { // Another cabinet
  842. ensureCabinet(psess,
  843. psess->achCabPath,
  844. sizeof(psess->achCabPath),
  845. achFile,
  846. psess->acab[iCab].achDiskName,
  847. psess->acab[iCab].setID,
  848. (USHORT)(psess->acab[iCab].iCabinet+1),
  849. TRUE, // Loop until right cab or abort
  850. FALSE, // Check cabinet
  851. perr);
  852. }
  853. }
  854. else {
  855. achFile[0] = '\0'; // Done
  856. }
  857. }
  858. }
  859. cleanup:
  860. if (hfCab != -1) {
  861. wrap_close(hfCab);
  862. }
  863. if (!FDIDestroy(psess->hfdi)) {
  864. //** Only set error if we don't already have one
  865. if (!ErrIsError(perr)) {
  866. ErrSet(perr,pszEXTERR_FDIDESTROY_FAILED);
  867. }
  868. }
  869. psess->perr = NULL;
  870. //** Return success/failure indication
  871. return !ErrIsError(perr);
  872. } /* doCabinet() */
  873. /*** fdiNotifyDir - Callback from FDICopy for Directory display
  874. *
  875. * Entry:
  876. * fdint - type of notification
  877. * pfdin - data for notification
  878. *
  879. * Exit-Success:
  880. * Return value varies (see FDI.H:PFNFDINOTIFY type)
  881. *
  882. * Exit-Failure:
  883. * Return value varies (see FDI.H:PFNFDINOTIFY type)
  884. */
  885. FNFDINOTIFY(fdiNotifyDir)
  886. {
  887. char achAttr[10];
  888. PERROR perr;
  889. #ifdef SMALL_DOS
  890. PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv;
  891. char szLocal[cbFILE_NAME_MAX];
  892. #else
  893. PSESSION psess=(PSESSION)pfdin->pv;
  894. #endif
  895. WCHAR szTemp[MAX_MESSAGE_SIZE];
  896. AssertSess(psess);
  897. perr = psess->perr;
  898. switch (fdint) {
  899. case fdintCABINET_INFO:
  900. return updateCabinetInfo(psess,pfdin);
  901. case fdintCOPY_FILE:
  902. //** See if filspec matches specified patterns
  903. #ifdef SMALL_DOS
  904. _fstrcpy(szLocal,pfdin->psz1);
  905. #else
  906. #define szLocal pfdin->psz1
  907. #endif
  908. if (!checkWildMatches(psess,szLocal,perr)) {
  909. //** Either no match, or failure -- figure out which
  910. if (ErrIsError(perr)) {
  911. return -1; // Error, abort
  912. }
  913. else {
  914. return 0; // No error, skip this file
  915. }
  916. }
  917. //** Show directory
  918. pszFromMSDOSTime(psess->achMsg,
  919. sizeof(psess->achMsg),
  920. pfdin->date,
  921. pfdin->time);
  922. pszFromAttrFAT(achAttr, sizeof(achAttr), pfdin->attribs);
  923. MsgSet(psess->achLine,
  924. pszEXT_FILE_DETAILS,
  925. #ifdef SMALL_DOS
  926. "%s%s%,13ld%-Fs",
  927. #else
  928. "%s%s%,13ld%-s",
  929. #endif
  930. psess->achMsg,achAttr,pfdin->cb,pfdin->psz1);
  931. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achLine, strlen(psess->achLine),
  932. szTemp, MAX_MESSAGE_SIZE );
  933. *(szTemp+strlen(psess->achLine)) = '\0';
  934. fwprintf( stdout,L"%s\n",szTemp );
  935. psess->cFiles++;
  936. psess->cbTotalBytes += pfdin->cb;
  937. return 0; // Skip file, do not copy
  938. case fdintPARTIAL_FILE:
  939. //** Construct output filespec
  940. #ifdef SMALL_DOS
  941. _fstrcpy(szLocal,pfdin->psz1);
  942. #else
  943. #define szLocal pfdin->psz1
  944. #endif
  945. //** See if filspec matches specified patterns
  946. if (!checkWildMatches(psess,szLocal,perr)) {
  947. //** Either no match, or failure -- figure out which
  948. if (ErrIsError(perr)) {
  949. return -1; // Error, abort
  950. }
  951. else {
  952. return 0; // No error, skip this file
  953. }
  954. }
  955. //** Only show partial file messages for first cabinet
  956. if (!psess->fContinuationCabinet) { // First cabinet
  957. MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE,
  958. #ifdef SMALL_DOS
  959. "%Fs%Fs%Fs",
  960. #else
  961. "%s%s%s",
  962. #endif
  963. pfdin->psz1,pfdin->psz2,pfdin->psz3);
  964. //do the localization print the message
  965. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achLine, strlen(psess->achLine),
  966. szTemp, MAX_MESSAGE_SIZE );
  967. *(szTemp+strlen(psess->achLine)) = '\0';
  968. fwprintf( stdout,L"%s\n",szTemp );
  969. fwprintf( stdout, L"%s\n",szTemp);
  970. }
  971. return 0; // Continue
  972. case fdintNEXT_CABINET:
  973. return doGetNextCab(fdint,pfdin);
  974. case fdintENUMERATE:
  975. return 0;
  976. default:
  977. fwprintf(stdout, L"UNKNOWN NOTIFICATION: %d\n",fdint);
  978. return 0; /* ??? */
  979. }
  980. } /* fdiNotifyDir() */
  981. /*** fdiNotifyExt - Callback from FDICopy for file extraction
  982. *
  983. * <<< Extract files! >>>
  984. *
  985. * Entry:
  986. * fdint - type of notification
  987. * pfdin - data for notification
  988. *
  989. * Exit-Success:
  990. * Return value varies (see FDI.H:PFNFDINOTIFY type)
  991. *
  992. * Exit-Failure:
  993. * Return value varies (see FDI.H:PFNFDINOTIFY type)
  994. */
  995. FNFDINOTIFY(fdiNotifyExt)
  996. {
  997. INT_PTR fh;
  998. FILETIMEATTR fta;
  999. PERROR perr;
  1000. char *pszDestinationFile;
  1001. int rc;
  1002. #ifdef SMALL_DOS
  1003. PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv;
  1004. char szLocal[cbFILE_NAME_MAX];
  1005. #else
  1006. PSESSION psess=(PSESSION)pfdin->pv;
  1007. #endif
  1008. WCHAR szTemp[MAX_MESSAGE_SIZE];
  1009. AssertSess(psess);
  1010. perr = psess->perr;
  1011. //** Reset the spill file error code;
  1012. // We know that FDI is OK right now if it is asking us if we want
  1013. // to extract this file, so reset the spill file error code. If
  1014. // we did not, then it may have seNOT_ENOUGH_MEMORY (for example)
  1015. // as a result of Quantum trying to eat up all available memory,
  1016. // and a real decompression failure would be reported as an out
  1017. // of memory problem.
  1018. psess->se = seNONE;
  1019. switch (fdint) {
  1020. case fdintCABINET_INFO:
  1021. return updateCabinetInfo(psess,pfdin);
  1022. case fdintCOPY_FILE:
  1023. if (psess->fDestructive && (pfdin->iFolder != psess->iCurrentFolder)) {
  1024. return(0); // only do files in the current folder
  1025. }
  1026. //** Construct output filespec
  1027. #ifdef SMALL_DOS
  1028. _fstrcpy(szLocal,pfdin->psz1);
  1029. #else
  1030. #define szLocal pfdin->psz1
  1031. #endif
  1032. //** See if filspec matches specified patterns
  1033. if (!checkWildMatches(psess,szLocal,perr)) {
  1034. //** Either no match, or failure -- figure out which
  1035. if (ErrIsError(perr)) {
  1036. return -1; // Error, abort
  1037. }
  1038. else {
  1039. return 0; // No error, skip this file
  1040. }
  1041. }
  1042. //** Figure out what destination file name should be
  1043. if (psess->achDest[0] != '\0') { // Override name from cabinet
  1044. pszDestinationFile = psess->achDest;
  1045. }
  1046. else {
  1047. pszDestinationFile = szLocal;
  1048. }
  1049. if (psess->achZap[0] != '\0') // if prefix-zapping
  1050. {
  1051. if (!strncmp(pszDestinationFile,psess->achZap,strlen(psess->achZap)))
  1052. {
  1053. pszDestinationFile += strlen(psess->achZap);
  1054. }
  1055. }
  1056. //** Construct full destination file name
  1057. if (!catDirAndFile(psess->achFile, // Buffer for output filespec
  1058. sizeof(psess->achFile), // Size of output buffer
  1059. psess->achLocation, // Output directory
  1060. pszDestinationFile, // Output file name
  1061. NULL, // Don't have alternate name
  1062. perr)) {
  1063. return -1; // Abort with error;
  1064. }
  1065. //** Make sure output directory exists
  1066. if (!ensureDirectory(psess->achFile,TRUE,perr)) {
  1067. return -1; // perr already filled in
  1068. }
  1069. //** Do overwrite processing
  1070. if (!checkOverwrite(psess,psess->achFile,perr,&rc)) {
  1071. return rc; // Either Skip or Abort
  1072. }
  1073. //** Create file
  1074. fh = wrap_open(psess->achFile,
  1075. _O_BINARY | _O_RDWR | _O_CREAT | _O_TRUNC, // No translation, R/W
  1076. _S_IREAD | _S_IWRITE); // Attributes when file is closed
  1077. if (fh == -1) {
  1078. ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile);
  1079. return -1; // Failure
  1080. }
  1081. #if 0
  1082. // jforbes: if'd this out, added _O_TRUNC above
  1083. //** Truncate file (in case it already existed and was larger)
  1084. if (0 != _chsize(fh, 0)) {
  1085. //** Not the best error, but avoids more localization!
  1086. ErrSet(psess->perr,pszEXTERR_CANNOT_CREATE_FILE,"%s",psess->achFile);
  1087. wrap_close(fh);
  1088. }
  1089. #endif
  1090. //** Show status
  1091. if (pszDestinationFile == szLocal) { // File name is not changed
  1092. MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE,"%s",psess->achFile);
  1093. }
  1094. else { // Destination file is different
  1095. MsgSet(psess->achMsg,pszEXT_EXTRACTING_FILE2,"%s%s",
  1096. szLocal,psess->achFile);
  1097. }
  1098. //add the multibyte conversion
  1099. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg),
  1100. szTemp, strlen(psess->achMsg) );
  1101. *(szTemp+strlen(psess->achMsg)) = '\0';
  1102. fwprintf( stdout,L"%s\n",szTemp );
  1103. psess->fNoLineFeed = TRUE;
  1104. psess->cFiles++;
  1105. psess->cbTotalBytes += pfdin->cb;
  1106. return fh; // Return open file handle
  1107. case fdintCLOSE_FILE_INFO:
  1108. //** Close the file
  1109. wrap_close(pfdin->hf);
  1110. //** Construct output filespec
  1111. #ifdef SMALL_DOS
  1112. _fstrcpy(szLocal,pfdin->psz1);
  1113. #else
  1114. #define szLocal pfdin->psz1
  1115. #endif
  1116. //** Figure out what destination file name should be
  1117. if (psess->achDest[0] != '\0') { // Override name from cabinet
  1118. pszDestinationFile = psess->achDest;
  1119. }
  1120. else {
  1121. pszDestinationFile = szLocal;
  1122. }
  1123. if (psess->achZap[0] != '\0') // if prefix-zapping
  1124. {
  1125. if (!strncmp(pszDestinationFile,psess->achZap,strlen(psess->achZap)))
  1126. {
  1127. pszDestinationFile += strlen(psess->achZap);
  1128. }
  1129. }
  1130. //** Construct full destination file name
  1131. if (!catDirAndFile(psess->achFile, // Buffer for output filespec
  1132. sizeof(psess->achFile), // Size of output buffer
  1133. psess->achLocation, // Output directory
  1134. pszDestinationFile, // Output file name
  1135. NULL, // Don't have alternate name
  1136. perr)) {
  1137. return FALSE; // Abort with error
  1138. }
  1139. //** Set file date, time, and attributes
  1140. fta.date = pfdin->date;
  1141. fta.time = pfdin->time;
  1142. fta.attr = pfdin->attribs &
  1143. (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH);
  1144. if (!SetFileTimeAndAttr(psess->achFile, &fta, perr)) {
  1145. return FALSE; // Abort with error
  1146. }
  1147. return TRUE; // Success
  1148. case fdintPARTIAL_FILE:
  1149. //** Construct output filespec
  1150. #ifdef SMALL_DOS
  1151. _fstrcpy(szLocal,pfdin->psz1);
  1152. #else
  1153. #define szLocal pfdin->psz1
  1154. #endif
  1155. //** See if filspec matches specified patterns
  1156. if (!checkWildMatches(psess,szLocal,perr)) {
  1157. //** Either no match, or failure -- figure out which
  1158. if (ErrIsError(perr)) {
  1159. return -1; // Error, abort
  1160. }
  1161. else {
  1162. return 0; // No error, skip this file
  1163. }
  1164. }
  1165. //** Only show partial file messages for first cabinet
  1166. if (!psess->fContinuationCabinet) { // First cabinet
  1167. MsgSet(psess->achMsg,pszEXT_PARTIAL_FILE,
  1168. #ifdef SMALL_DOS
  1169. "%Fs%Fs%Fs",
  1170. #else
  1171. "%s%s%s",
  1172. #endif
  1173. pfdin->psz1,pfdin->psz2,pfdin->psz3);
  1174. printf("%s\n",psess->achMsg);
  1175. }
  1176. return 0; // Continue
  1177. case fdintNEXT_CABINET:
  1178. return doGetNextCab(fdint,pfdin);
  1179. case fdintENUMERATE:
  1180. return 0;
  1181. default:
  1182. printf("UNKNOWN NOTIFICATION: %d\n",fdint);
  1183. return 0; /* ??? */
  1184. }
  1185. } /* fdiNotifyExt() */
  1186. /*** checkOverwrite - Check for file existence and do overwrite processing
  1187. *
  1188. * Entry:
  1189. * psess - Session
  1190. * pszFile - File to check
  1191. * perr - Error structure
  1192. * prc - Gets return code
  1193. *
  1194. * Exit-Success:
  1195. * Returns TRUE; file can be overwritten
  1196. *
  1197. * Exit-Failure:
  1198. * Returns FALSE; perr filled in if error,
  1199. * *prc == 0 -> Skip file
  1200. * *prc == -1 -> Abort
  1201. */
  1202. BOOL checkOverwrite(PSESSION psess,
  1203. char *pszFile,
  1204. PERROR perr,
  1205. int *prc)
  1206. {
  1207. char ch;
  1208. BOOL fGotReply;
  1209. BOOL fOverwrite;
  1210. BOOL fOverwriteAll;
  1211. struct _stat stat;
  1212. //** Check to see if file already exists
  1213. if (-1 == _stat(pszFile,&stat)) { // File does not exist
  1214. return TRUE; // Write it
  1215. }
  1216. //** Prompt if we're supposed to
  1217. if (!psess->fOverwrite) {
  1218. //** Display prompt -- no CR/LF
  1219. MsgSet(psess->achMsg,pszEXT_OVERWRITE_PROMPT,"%s",pszFile);
  1220. printf("%s",psess->achMsg);
  1221. //** Get valid single character response and ENTER key;
  1222. // any illegal keys are just ignored
  1223. fGotReply = FALSE;
  1224. while (!fGotReply || (ch != '\r')) {
  1225. ch = (char)_getch(); // Get a keystroke
  1226. switch (toupper(ch)) {
  1227. case chOVERWRITE_YES:
  1228. fGotReply = TRUE;
  1229. fOverwrite = TRUE;
  1230. fOverwriteAll = FALSE;
  1231. printf("%c\b",ch); // Echo character and backspace over it
  1232. break;
  1233. case chOVERWRITE_NO:
  1234. fGotReply = TRUE;
  1235. fOverwrite = FALSE;
  1236. fOverwriteAll = FALSE;
  1237. printf("%c\b",ch); // Echo character and backspace over it
  1238. break;
  1239. case chOVERWRITE_ALL:
  1240. fGotReply = TRUE;
  1241. fOverwrite = TRUE;
  1242. fOverwriteAll = TRUE;
  1243. printf("%c\b",ch); // Echo character and backspace over it
  1244. break;
  1245. default:
  1246. break; // Ignore character
  1247. }
  1248. }
  1249. //** Do the line feed
  1250. printf("\n");
  1251. //** Respect user's wish
  1252. if (!fOverwrite) { // Don't overwrite file
  1253. *prc = 0; // Indicate skip
  1254. return FALSE;
  1255. }
  1256. else { // Overwrite once or all
  1257. psess->fOverwrite = fOverwriteAll; // Set accordingly
  1258. }
  1259. }
  1260. //** Make sure file is writeable, if it isn't already
  1261. if (!(stat.st_mode & _S_IWRITE)) { // File is not writeable
  1262. _chmod(pszFile, _S_IREAD | _S_IWRITE);
  1263. //** Ignore error code, because the open will fail and catch it
  1264. }
  1265. //** Done
  1266. return TRUE; // Overwrite that file
  1267. } /* checkOverwrite() */
  1268. /*** updateCabinetInfo - update history of cabinets seen
  1269. *
  1270. * Entry:
  1271. * psess - Session
  1272. * pfdin - FDI info structurue
  1273. *
  1274. * Exit:
  1275. * Returns 0;
  1276. */
  1277. int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin)
  1278. {
  1279. AssertSess(psess);
  1280. //** Save older cabinet info
  1281. psess->acab[0] = psess->acab[1];
  1282. //** Save new cabinet info
  1283. STRCPY(psess->acab[1].achCabPath ,pfdin->psz3);
  1284. STRCPY(psess->acab[1].achCabFilename ,pfdin->psz1);
  1285. STRCPY(psess->acab[1].achDiskName ,pfdin->psz2);
  1286. psess->acab[1].setID = pfdin->setID;
  1287. psess->acab[1].iCabinet = pfdin->iCabinet;
  1288. return 0;
  1289. }
  1290. /*** ensureCabinet - Make sure desired cabinet is available
  1291. *
  1292. * Make sure requested cabinet is available.
  1293. *
  1294. * Entry:
  1295. * psess - Session
  1296. * pszPath - Path buffer (modified if necessary on output)
  1297. * cbPath - Size of path buffer
  1298. * pszFile - Cabinet file name
  1299. * pszLabel - Label for disk with cabinet file
  1300. * setID - setID for cabinet
  1301. * iCabinet - iCabinet for cabinet
  1302. * fLoop - TRUE => Loop until right cabinet or user aborts
  1303. * FALSE => Only try once
  1304. * fPromptOnly - TRUE => Caller knows cabinet is bad, just prompt
  1305. * FALSE => Check cabinet, prompt if necessary
  1306. * perr - Error structure
  1307. *
  1308. * Exit-Success:
  1309. * Returns TRUE; desired cabinet is present
  1310. *
  1311. * Exit-Failure:
  1312. * Returns FALSE; perr filled in.
  1313. * Returns -1; user aborted
  1314. */
  1315. BOOL ensureCabinet(PSESSION psess,
  1316. char *pszPath,
  1317. int cbPath,
  1318. char *pszFile,
  1319. char *pszLabel,
  1320. USHORT setID,
  1321. USHORT iCabinet,
  1322. BOOL fLoop,
  1323. BOOL fPromptOnly,
  1324. PERROR perr)
  1325. {
  1326. char ach[cbFILE_NAME_MAX];
  1327. int cch;
  1328. int cLoop=0;
  1329. char chDrive;
  1330. BOOL fCabinetExists;
  1331. FDICABINETINFO fdici;
  1332. INT_PTR hfCab;
  1333. char ch;
  1334. int i=0;
  1335. AssertSess(psess);
  1336. do {
  1337. cLoop++; // Count loops
  1338. ErrClear(perr); // Make sure no error is set
  1339. //** Construct fully qualified cabinet file path
  1340. if (!catDirAndFile(ach, // Buffer for output filespec
  1341. sizeof(ach), // Size of output buffer
  1342. pszPath, // Path
  1343. pszFile, // Filename
  1344. NULL, // Don't have alternate name
  1345. perr)) {
  1346. return FALSE; // Abort with error
  1347. }
  1348. //** Check cabinet only if asked
  1349. if (!fPromptOnly) {
  1350. //** Make sure cabinet is the one we want
  1351. if (-1 == (hfCab = wrap_open(ach,_O_BINARY | _O_RDONLY,0))) {
  1352. ErrSet(perr,pszEXTERR_CANNOT_OPEN_FILE,"%s",ach);
  1353. }
  1354. else if (!FDIIsCabinet(psess->hfdi,hfCab,&fdici)) {
  1355. if (!ErrIsError(perr)) { // Have to set error message
  1356. ErrSet(perr,pszEXTERR_NOT_A_CABINET,"%s",ach);
  1357. }
  1358. }
  1359. else if ((fdici.setID != setID) ||
  1360. (fdici.iCabinet != iCabinet)) {
  1361. ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",ach);
  1362. }
  1363. //** Close the cabinet file (if we got it opened)
  1364. if (hfCab != -1) {
  1365. wrap_close(hfCab);
  1366. fCabinetExists = TRUE;
  1367. }
  1368. else {
  1369. fCabinetExists = FALSE;
  1370. }
  1371. //** Did we get the cabinet we wanted?
  1372. if (!ErrIsError(perr)) {
  1373. return TRUE; // Yup, return success
  1374. }
  1375. //** Don't show message if first time and cabinet not there,
  1376. // since this is the common case cabinets on separate floppy
  1377. // disks, and we don't want to whine before we ask them to
  1378. // insert the right floppy.
  1379. //
  1380. if ((cLoop > 1) || fCabinetExists) {
  1381. MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach);
  1382. printf("\n%s\n",psess->achMsg);
  1383. }
  1384. }
  1385. //** Tell user what we want
  1386. if (IsPathRemovable(ach,&chDrive)) {
  1387. MsgSet(psess->achMsg,pszEXT_FLOPPY_PROMPT,"%s%s%c",
  1388. pszFile,pszLabel,chDrive);
  1389. }
  1390. else {
  1391. MsgSet(psess->achMsg,pszEXT_NOFLOPPY_PROMPT,"%s%s%c",
  1392. pszFile,pszLabel);
  1393. }
  1394. printf("%s\n",psess->achMsg);
  1395. //** Get response
  1396. do
  1397. {
  1398. ch = getchar();
  1399. ach[i++]=ch;
  1400. if( i >= cbFILE_NAME_MAX )
  1401. break;
  1402. }while( ch!='\n' );
  1403. ach[i-1]=0;
  1404. /*
  1405. if (!gets(ach)) { // Error or EOF
  1406. ErrSet(perr,pszEXTERR_ABORT);
  1407. return -1; // User abort
  1408. }
  1409. */
  1410. if (strlen(ach) > 0) {
  1411. strcpy(pszPath,ach); // Update path
  1412. cch = strlen(pszPath);
  1413. //** Make sure path has path separator, since FDI requires it
  1414. cch += appendPathSeparator(&(pszPath[cch-1]));
  1415. //** Update path for next FDICopy() call!
  1416. if (cch >= sizeof(psess->achCabPath)) {
  1417. Assert(0);
  1418. return -1; // Path too big
  1419. }
  1420. strcpy(psess->achCabPath,pszPath);
  1421. }
  1422. }
  1423. while (fLoop);
  1424. //** Did not guarantee desired cabinet
  1425. return FALSE;
  1426. } /* ensureCabinet() */
  1427. /*** doGetNextCab - Get next cabinet
  1428. *
  1429. * Make sure requested cabinet is available.
  1430. *
  1431. * Entry:
  1432. * fdint - type of notification
  1433. * pfdin - data for notification
  1434. *
  1435. * Exit-Success:
  1436. * Returns anything but -1;
  1437. *
  1438. * Exit-Failure:
  1439. * Returns -1 => abort FDICopy() call.
  1440. */
  1441. FNFDINOTIFY(doGetNextCab)
  1442. {
  1443. char ach[cbFILE_NAME_MAX];
  1444. static int cErrors=0; // Count of errors for single attempt
  1445. PERROR perr;
  1446. int rc;
  1447. #ifdef SMALL_DOS
  1448. PSESSION psess=(PSESSION)(void *)(short)(long)pfdin->pv;
  1449. static char szCabPath[cbFILE_NAME_MAX];
  1450. static char szCabFile[cbFILE_NAME_MAX];
  1451. static char szCabLabel[cbFILE_NAME_MAX];
  1452. #else
  1453. PSESSION psess=(PSESSION)pfdin->pv;
  1454. #endif
  1455. AssertSess(psess);
  1456. perr = psess->perr;
  1457. #ifdef SMALL_DOS
  1458. _fstrcpy(psess->achCabinetFile,pfdin->psz1);
  1459. #else
  1460. strcpy(psess->achCabinetFile,pfdin->psz1);
  1461. #endif
  1462. //** Skip "starts in ..." messages for subsequent cabinets
  1463. psess->fContinuationCabinet = TRUE;
  1464. //** Keep track of GetNextCabinet calls so we can determine
  1465. // what cabinet to expect next.
  1466. psess->fNextCabCalled = TRUE;
  1467. //** If there is no problem to report, just let FDI do the checks
  1468. if (pfdin->fdie == FDIERROR_NONE) {
  1469. cErrors = 0;
  1470. return 0;
  1471. }
  1472. //** If FDI didn't get the correct cabinet last time it called us,
  1473. // it calls us again with a specific error code. Tell the user
  1474. // something intelligible.
  1475. //
  1476. // pfdin->psz1 = cabinet filename
  1477. // pfdin->psz2 = disk user-readable name
  1478. // pfdin->psz3 = current cabinet path
  1479. #ifdef SMALL_DOS
  1480. _fstrcpy(szCabFile ,pfdin->psz1);
  1481. _fstrcpy(szCabLabel,pfdin->psz2);
  1482. _fstrcpy(szCabPath ,pfdin->psz3);
  1483. #else
  1484. #define szCabFile pfdin->psz1
  1485. #define szCabLabel pfdin->psz2
  1486. #define szCabPath pfdin->psz3
  1487. #endif
  1488. cErrors++; // Count of errors on this cabinet
  1489. switch (pfdin->fdie) {
  1490. case FDIERROR_USER_ABORT:
  1491. Assert(0); //** Should never be called with this error code
  1492. break;
  1493. default:
  1494. //** Construct full path name of cabinet
  1495. if (!catDirAndFile(ach, // Buffer for output filespec
  1496. sizeof(ach), // Size of output buffer
  1497. szCabPath, // Path
  1498. szCabLabel, // Filename s/b szCabFile?
  1499. NULL, // Don't have alternate name
  1500. perr)) {
  1501. return -1; // Abort with error
  1502. }
  1503. //** Construct error string
  1504. mapFDIError(perr,psess,ach,&(psess->erf));
  1505. //** Reset error
  1506. psess->erf.erfOper = FDIERROR_NONE;
  1507. } /* switch */
  1508. //** Tell user what the problem is, except in the case where the
  1509. // file was not found *and* this was the first try at finding
  1510. // the cabinet.
  1511. if ((cErrors > 1) || (pfdin->fdie != FDIERROR_CABINET_NOT_FOUND)) {
  1512. MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach);
  1513. printf("\n%s\n",psess->achMsg);
  1514. }
  1515. //** Tell user to swap disks or type in new path
  1516. rc = ensureCabinet(psess,
  1517. szCabPath, // cabinet path
  1518. cbFILE_NAME_MAX,
  1519. szCabFile, // cabinet file name
  1520. szCabLabel, // User-readable label
  1521. pfdin->setID, // Required setID
  1522. pfdin->iCabinet, // Required iCabinet
  1523. FALSE, // Do not loop
  1524. TRUE, // Skip check, just prompt
  1525. perr);
  1526. #ifdef SMALL_DOS
  1527. //** Copy possibly modified cabinet path back to FDI structure
  1528. _fstrcpy(pfdin->psz3,szCabPath);
  1529. #endif
  1530. //** Return result
  1531. return rc;
  1532. } /* doGetNextCab() */
  1533. /*** fdiDecryptDir - Callback from FDICopy for decryption
  1534. *
  1535. * <<< Just indicate calls made >>>
  1536. *
  1537. * NOTE: See fdi.h for details.
  1538. *
  1539. * Entry:
  1540. * pfdid - data for decryption
  1541. *
  1542. * Exit-Success:
  1543. * Return TRUE;
  1544. *
  1545. * Exit-Failure:
  1546. * Return -1;
  1547. */
  1548. FNFDIDECRYPT(fdiDecryptDir)
  1549. {
  1550. PERROR perr;
  1551. #ifdef SMALL_DOS
  1552. PSESSION psess=(PSESSION)(void *)(short)(long)pfdid->pvUser;
  1553. #else
  1554. PSESSION psess=(PSESSION)pfdid->pvUser;
  1555. #endif
  1556. AssertSess(psess);
  1557. perr = psess->perr;
  1558. //** Bail out if we're not supposed to show info
  1559. if (!psess->fShowReserveInfo) {
  1560. return TRUE;
  1561. }
  1562. switch (pfdid->fdidt) {
  1563. case fdidtNEW_CABINET:
  1564. MsgSet(psess->achMsg,pszEXT_DECRYPT_HEADER,
  1565. "%08lx%u%x%d",
  1566. pfdid->cabinet.pHeaderReserve,
  1567. pfdid->cabinet.cbHeaderReserve,
  1568. pfdid->cabinet.setID,
  1569. pfdid->cabinet.iCabinet);
  1570. printf("%s\n",psess->achMsg);
  1571. break;
  1572. case fdidtNEW_FOLDER:
  1573. MsgSet(psess->achMsg,pszEXT_DECRYPT_FOLDER,
  1574. "%08lx%u%d",
  1575. pfdid->folder.pFolderReserve,
  1576. pfdid->folder.cbFolderReserve,
  1577. pfdid->folder.iFolder);
  1578. printf("%s\n",psess->achMsg);
  1579. break;
  1580. case fdidtDECRYPT:
  1581. MsgSet(psess->achMsg,pszEXT_DECRYPT_DATA,
  1582. "%08lx%u%08lx%u%d%u",
  1583. pfdid->decrypt.pDataReserve,
  1584. pfdid->decrypt.cbDataReserve,
  1585. pfdid->decrypt.pbData,
  1586. pfdid->decrypt.cbData,
  1587. pfdid->decrypt.fSplit,
  1588. pfdid->decrypt.cbPartial);
  1589. printf("%s\n",psess->achMsg);
  1590. break;
  1591. default:
  1592. printf("UNKNOWN DECRYPT COMMAND: %d\n",pfdid->fdidt);
  1593. return -1; // Abort
  1594. };
  1595. return TRUE;
  1596. } /* fdiDecryptDir() */
  1597. /*** fdiDecryptExt - Callback from FDICopy for real decryption
  1598. *
  1599. * NOTE: See fdi.h for details.
  1600. *
  1601. * Entry:
  1602. * pfdid - data for decryption
  1603. *
  1604. * Exit-Success:
  1605. * Return TRUE;
  1606. *
  1607. * Exit-Failure:
  1608. * Return -1;
  1609. */
  1610. FNFDIDECRYPT(fdiDecryptExt)
  1611. {
  1612. return fdiDecryptDir(pfdid);
  1613. } /* fdiDecryptExt() */
  1614. /*** checkWildMatchs - Check filespec against list of filespec patterns
  1615. *
  1616. * Entry:
  1617. * psess - SESSION -- has list of filespec patterns
  1618. * pszFile - Filespec to test (may have path characters)
  1619. * perr - ERROR structure
  1620. *
  1621. * Exit-Success:
  1622. * Returns TRUE, pszFile matched a pattern
  1623. *
  1624. * Exit-Failure:
  1625. * Returns FALSE, pszFile did not match a pattern -or- an error occurred.
  1626. * Use ErrIsError(perr) to determine if an error occured.
  1627. *
  1628. * 11/12/99 msliger Added PatternMatch(). For backwards compatibility,
  1629. * if the given wildspec contained no path separators, use only the
  1630. * base filename/extension. If path separators exist in the
  1631. * wildspec, use the full pathname from the cab.
  1632. * FEATURE: PatternMatch is not MBCS-enabled. But then, even with the
  1633. * CharIncr() refinements, the existing IsWildMatch didn't work either
  1634. * (trail bytes were not compared.)
  1635. */
  1636. BOOL checkWildMatches(PSESSION psess, char *pszFile, PERROR perr)
  1637. {
  1638. HFILESPEC hfspec; // Use to walk list of file patterns
  1639. char *pszNameExt; // Name.Ext piece
  1640. char *pszWild; // Filespec pattern
  1641. int fAllowImpliedDot; // TRUE iff no dot in base name
  1642. char *psz; // either pszNameExt or pszFile
  1643. //** Get name.ext piece
  1644. pszNameExt = getJustFileNameAndExt(pszFile,perr);
  1645. if (!pszNameExt) {
  1646. return FALSE; // perr already filled in
  1647. }
  1648. if (strchr(pszNameExt, '.') == NULL)
  1649. {
  1650. fAllowImpliedDot = TRUE; // allows *.* to match "hosts"
  1651. }
  1652. else
  1653. {
  1654. fAllowImpliedDot = FALSE; // base name has it's own dot
  1655. }
  1656. //** Loop through list of filespec patterns
  1657. hfspec = FLFirstFile(psess->hflist);
  1658. Assert(hfspec != NULL); // Skip over cabinet filespec
  1659. hfspec = FLNextFile(hfspec);
  1660. Assert(hfspec != NULL); // First wildcard spec
  1661. while (hfspec != NULL) { // Check patterns
  1662. pszWild = FLGetSource(hfspec);
  1663. Assert(pszWild!=NULL);
  1664. Assert(*pszWild);
  1665. if (strchr(pszWild, '\\') == NULL) { // path in wildspec?
  1666. psz = pszNameExt; // no path, match base name
  1667. } else {
  1668. psz = pszFile; // path given, match full name
  1669. if (*pszWild == '\\') {
  1670. pszWild++; // implied leading '\' in CAB
  1671. }
  1672. }
  1673. if (PatternMatch(psz,pszWild,fAllowImpliedDot)) {
  1674. return TRUE; // Got a match!
  1675. }
  1676. hfspec = FLNextFile(hfspec); // Try next pattern
  1677. }
  1678. //** Failure -- none of the patterns matched
  1679. return FALSE;
  1680. } /* checkWildMatches() */
  1681. /*** fdiAlloc - memory allocator for FDI
  1682. *
  1683. * Entry:
  1684. * cb - size of block to allocate
  1685. *
  1686. * Exit-Success:
  1687. * returns non-NULL pointer to block of size at least cb.
  1688. *
  1689. * Exit-Failure:
  1690. * returns NULL
  1691. */
  1692. FNALLOC(fdiAlloc)
  1693. {
  1694. void HUGE *pv;
  1695. //** Do allocation
  1696. #ifdef BIT16
  1697. pv = _halloc(cb,1); // Use 16-bit function
  1698. #else // !BIT16
  1699. pv = malloc(cb); // Use 32-bit function
  1700. #endif // !BIT16
  1701. //** Remember if error occured, to improve quality of error messages
  1702. if (pv == NULL) {
  1703. psessG->se = seNOT_ENOUGH_MEMORY;
  1704. }
  1705. //** Return buffer (or failure indication)
  1706. return pv;
  1707. } /* fdiAlloc() */
  1708. /*** fdiFree - memory free function for FDI
  1709. *
  1710. * Entry:
  1711. * pv - memory allocated by fciAlloc to be freed
  1712. *
  1713. * Exit:
  1714. * Frees memory
  1715. */
  1716. FNFREE(fdiFree)
  1717. {
  1718. #ifdef BIT16
  1719. //** Use 16-bit function
  1720. _hfree(pv);
  1721. #else // !BIT16
  1722. //** Use 32-bit function
  1723. free(pv);
  1724. #endif // !BIT16
  1725. }
  1726. /*** STATELOC - States for /L (location) parsing
  1727. *
  1728. */
  1729. typedef enum {
  1730. slNONE, // No /L seen
  1731. slEXPECTING, // Just saw /L, need a location
  1732. slGOT, // We have parsed "/L location"
  1733. } STATELOC; /* sl */
  1734. /*** parseCommandLine - Parse the command line arguments
  1735. *
  1736. * Entry:
  1737. * psess - SESSION
  1738. * cArg - Count of arguments, including program name
  1739. * apszArg - Array of argument strings
  1740. * perr - ERROR structure
  1741. *
  1742. * Exit-Success:
  1743. * Returns TRUE, psess filled in.
  1744. *
  1745. * Exit-Failure:
  1746. * Returns actBAD, perr filled in with error.
  1747. */
  1748. BOOL parseCommandLine(PSESSION psess, int cArg, char *apszArg[], PERROR perr)
  1749. {
  1750. int cFile=0; // Count of non-directive file names seen
  1751. int ch; // Switch value
  1752. int i;
  1753. char *pch;
  1754. STATELOC sl=slNONE; // Location parsing state
  1755. AssertSess(psess);
  1756. psess->act = actDEFAULT; // We don't know what we are doing; yet
  1757. psess->achLocation[0] = '\0'; // Default to current directory
  1758. //** Empty file handle table
  1759. for (i=0; i<cMAX_CAB_FILE_OPEN; i++) {
  1760. psess->ahfSelf[i] = -1; // No open handle
  1761. }
  1762. //** See if we are a self-extracting cabinet
  1763. if (!checkSelfExtractingCab(psess,cArg,apszArg,perr)) {
  1764. return FALSE; // An error occurred, perr filled in
  1765. }
  1766. if (psess->fSelfExtract) {
  1767. if ((cArg < 2) && (psess->cArgv > 1)) {
  1768. cArg = psess->cArgv;
  1769. apszArg = psess->pArgv;
  1770. }
  1771. if (addFileSpec(psess,psess->achSelf,perr) == NULL) {
  1772. ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",psess->achSelf);
  1773. return FALSE;
  1774. }
  1775. cFile++; // Count files
  1776. }
  1777. //** Parse args, skipping program name
  1778. for (i=1; i<cArg; i++) {
  1779. if ((apszArg[i][0] == chSWITCH1) ||
  1780. (apszArg[i][0] == chSWITCH2) ) {
  1781. //** Have a switch to parse, make sure switch is OK here
  1782. if (sl == slEXPECTING) {
  1783. ErrSet(perr,pszEXTERR_MISSING_LOCATION);
  1784. return FALSE;
  1785. }
  1786. //** Process switches (support string to ease typing)
  1787. for (pch=&apszArg[i][1]; *pch; pch++) {
  1788. ch = toupper(*pch); // Switch character
  1789. switch (ch) {
  1790. case chSWITCH_HELP:
  1791. psess->act = actHELP; // Show help
  1792. return TRUE;
  1793. case chSWITCH_ALL:
  1794. psess->fAllCabinets = TRUE;
  1795. break;
  1796. case chSWITCH_COPY:
  1797. if (psess->act != actDEFAULT) {
  1798. ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch);
  1799. return FALSE;
  1800. }
  1801. psess->act = actCOPY;
  1802. break;
  1803. case chSWITCH_DIRECTORY:
  1804. if (psess->act != actDEFAULT) {
  1805. ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch);
  1806. return FALSE;
  1807. }
  1808. psess->act = actDIRECTORY;
  1809. break;
  1810. case chSWITCH_EXTRACT:
  1811. if (psess->act != actDEFAULT) {
  1812. ErrSet(perr,pszEXTERR_CONFLICTING_SWITCH,"%c",*pch);
  1813. return FALSE;
  1814. }
  1815. psess->act = actEXTRACT;
  1816. break;
  1817. case chSWITCH_LOCATION:
  1818. //** Make sure we only got location once
  1819. if (sl == slGOT) {
  1820. ErrSet(perr,pszEXTERR_LOCATION_TWICE);
  1821. return FALSE;
  1822. }
  1823. sl = slEXPECTING;
  1824. break;
  1825. case chSWITCH_OVERWRITE:
  1826. psess->fOverwrite = TRUE;
  1827. break;
  1828. case chSWITCH_RESERVE:
  1829. psess->fShowReserveInfo = TRUE;
  1830. break;
  1831. case chSWITCH_ZAP:
  1832. pch++;
  1833. if (*pch == '\0')
  1834. {
  1835. if ((i+1) < cArg)
  1836. {
  1837. pch = apszArg[++i];
  1838. }
  1839. else
  1840. {
  1841. ErrSet(perr,pszEXTERR_MISSING_LOCATION);
  1842. return(FALSE);
  1843. }
  1844. }
  1845. strcpy(psess->achZap,pch);
  1846. pch = " "; // continue parse on next arg
  1847. break;
  1848. case chSWITCH_ONCE:
  1849. psess->fDestructive++; // will require "/###"
  1850. break;
  1851. default:
  1852. ErrSet(perr,pszEXTERR_BAD_SWITCH,"%s",apszArg[i]);
  1853. return FALSE;
  1854. break;
  1855. }
  1856. }
  1857. }
  1858. //** Not a command line switch
  1859. else if (sl == slEXPECTING) {
  1860. //** Get the location (output directory)
  1861. STRCPY(psess->achLocation,apszArg[i]); // Save location
  1862. sl = slGOT; // Done eating location
  1863. }
  1864. else {
  1865. //** We have a file name, add it to our list
  1866. if (addFileSpec(psess,apszArg[i],perr) == NULL) {
  1867. ErrSet(perr,pszEXTERR_COULD_NOT_ADD_FILE,"%s",apszArg[i]);
  1868. return FALSE;
  1869. }
  1870. cFile++; // Count files
  1871. }
  1872. }
  1873. if (psess->fDestructive < 3) { // require 3 #'s to activate
  1874. psess->fDestructive = FALSE;
  1875. }
  1876. if (psess->fSelfExtract) {
  1877. psess->fDestructive = FALSE; // can't do it in self-extractor
  1878. }
  1879. //** If no arguments and not self-extracting, show help
  1880. if ((cArg == 1) && !psess->fSelfExtract) {
  1881. psess->act = actHELP; // Show help
  1882. return TRUE;
  1883. }
  1884. //** Make sure no trailing /L without location
  1885. if (sl == slEXPECTING) {
  1886. ErrSet(perr,pszEXTERR_MISSING_LOCATION);
  1887. return FALSE;
  1888. }
  1889. //** Make sure we got right number of arguments for COPY case
  1890. if ((psess->act == actCOPY) && (cFile != 2)) {
  1891. //** General purpose error, to minimize localization effort
  1892. ErrSet(perr,pszEXTERR_BAD_PARAMETERS);
  1893. return FALSE;
  1894. }
  1895. //** Make sure we got at least one filespec
  1896. if (cFile == 0) {
  1897. ErrSet(perr,pszEXTERR_MISSING_CABINET);
  1898. return FALSE;
  1899. }
  1900. //** Special processing for self-extract
  1901. if (psess->fSelfExtract) {
  1902. psess->fAllCabinets = TRUE; // Always do all cabinets
  1903. //** Force EXTRACT if no /E or /D specified
  1904. if (psess->act == actDEFAULT) {
  1905. psess->act = actEXTRACT;
  1906. }
  1907. }
  1908. //** Success
  1909. return TRUE;
  1910. }
  1911. /*** checkSelfExtractingCab - See if we are a self-extracting cabinet
  1912. *
  1913. * Entry:
  1914. * psess - SESSION
  1915. * cArg - Count of arguments, including program name
  1916. * apszArg - Array of argument strings
  1917. * perr - ERROR structure
  1918. *
  1919. * Exit-Success:
  1920. * Returns TRUE, psess filled in.
  1921. * psess->fSelfExtract set to TRUE if self-extracting
  1922. * psess->cbSelfExtract set to EXE size if self-extracting (this is
  1923. * the offset to the start of the cabinet file header).
  1924. * psess->cbSelfExtractSize set to the CAB size if self-extractor
  1925. * psess->achSelf set to the EXE file's name
  1926. *
  1927. * Exit-Failure:
  1928. * Returns actBAD, perr filled in with error.
  1929. *
  1930. * Notes:
  1931. * The strategy is to get the EXE file size indicated in the appropriate
  1932. * EXE header, and if the actual size of our EXE file is larger than that,
  1933. * we assume that there is a cabinet file tacked on to the end, and we
  1934. * extract the files from that!
  1935. *
  1936. * Refer to the XIMPLANT source for details on the workings of the XIMPLANT
  1937. * implementation.
  1938. */
  1939. BOOL checkSelfExtractingCab(PSESSION psess,
  1940. int cArg,
  1941. char *apszArg[],
  1942. PERROR perr)
  1943. {
  1944. long cbFile; // EXE file size
  1945. long cbFromHeader; // Size indicated by EXE header
  1946. INT_PTR hf;
  1947. DWORD signature; // should be "MSCF"
  1948. DWORD alignment; // file structure alignment, ie, 512
  1949. #ifdef BIT16
  1950. long info;
  1951. #else // !BIT16
  1952. char achFile[cbFILE_NAME_MAX];
  1953. IMAGE_DOS_HEADER idh; // MS-DOS header
  1954. IMAGE_NT_HEADERS inh; // Complete PE header
  1955. IMAGE_SECTION_HEADER ish; // section header
  1956. WORD iSection;
  1957. struct
  1958. {
  1959. unsigned long signature;
  1960. unsigned long reserved;
  1961. unsigned long cbArgv;
  1962. unsigned long cbCabinet;
  1963. } magic;
  1964. DWORD offDebug; // file offset of debug info
  1965. DWORD cbDebug; // size of debug info
  1966. IMAGE_DEBUG_DIRECTORY idd; // debug descriptor
  1967. #define IMPLANT_SECTION_NAME "Ext_Cab1" /* must be 8 chars */
  1968. #endif // !BIT16
  1969. // jforbes: removed SFX CAB checking for when generating
  1970. // debug executable for extract, since it thinks the debug
  1971. // exe is a cab file
  1972. #ifdef BIT16
  1973. #ifdef _DEBUG
  1974. psess->fSelfExtract = FALSE;
  1975. return TRUE;
  1976. #endif
  1977. #endif
  1978. //** Open our EXE file
  1979. #ifdef BIT16
  1980. hf = wrap_open(apszArg[0],_O_BINARY | _O_RDONLY,0);
  1981. #else
  1982. if (!GetModuleFileName(NULL,achFile,sizeof(achFile))) {
  1983. return TRUE;
  1984. }
  1985. hf = wrap_open(achFile,_O_BINARY | _O_RDONLY,0);
  1986. #endif
  1987. if (hf == -1) {
  1988. return TRUE; // Something is bogus, just skip selfex
  1989. }
  1990. //** Get the expected EXE file size
  1991. #ifdef BIT16
  1992. //** Do it the MS-DOS way
  1993. if (-1 == wrap_lseek(hf,2,SEEK_SET)) {
  1994. goto Exit;
  1995. }
  1996. if (sizeof(info) != wrap_read(hf,&info,sizeof(info))) {
  1997. goto Exit;
  1998. }
  1999. //** OK, we've got the page count and count of bytes in the last page;
  2000. // convert to a file size.
  2001. cbFromHeader = ((info>>16)-1)*512 + (info&0xFFFF);
  2002. alignment = 16;
  2003. #else // !BIT16
  2004. //** Do it the Win32 way
  2005. //** Get MS-DOS header
  2006. if (sizeof(idh) != wrap_read(hf,&idh,sizeof(idh))) {
  2007. goto Exit;
  2008. }
  2009. //** Seek to and read NT header
  2010. if (-1 == wrap_lseek(hf,idh.e_lfanew,SEEK_SET)) {
  2011. goto Exit;
  2012. }
  2013. if (sizeof(inh) != wrap_read(hf,&inh,sizeof(inh))) {
  2014. goto Exit;
  2015. }
  2016. //** Seek to first section header
  2017. if (-1 == wrap_lseek(hf,idh.e_lfanew + sizeof(DWORD) +
  2018. sizeof(IMAGE_FILE_HEADER) + inh.FileHeader.SizeOfOptionalHeader,
  2019. SEEK_SET)) {
  2020. goto Exit;
  2021. }
  2022. cbFromHeader = 0;
  2023. iSection = inh.FileHeader.NumberOfSections;
  2024. offDebug = 0;
  2025. cbDebug = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].Size;
  2026. alignment = inh.OptionalHeader.FileAlignment;
  2027. while (iSection--) {
  2028. //** Read this section header
  2029. if (sizeof(ish) != wrap_read(hf,&ish,sizeof(ish))) {
  2030. goto Exit;
  2031. }
  2032. if (memcmp(ish.Name,IMPLANT_SECTION_NAME,sizeof(ish.Name)) == 0)
  2033. {
  2034. /* found an implanted section, use the magic */
  2035. if ((wrap_lseek(hf,ish.PointerToRawData,SEEK_SET) == -1) ||
  2036. (wrap_read(hf,&magic,sizeof(magic)) != sizeof(magic))) {
  2037. goto Exit;
  2038. }
  2039. if (magic.signature == 0x4E584653) {
  2040. psess->fSelfExtract = TRUE;
  2041. psess->cbSelfExtract = ish.PointerToRawData + sizeof(magic) + magic.cbArgv;
  2042. psess->cbSelfExtractSize = magic.cbCabinet;
  2043. if (magic.cbArgv) {
  2044. psess->pArgv = MemAlloc(magic.cbArgv);
  2045. if ((psess->pArgv != NULL) &&
  2046. (wrap_read(hf,psess->pArgv,magic.cbArgv) == magic.cbArgv)) {
  2047. psess->cArgv = 1; /* for [0], which is NULL */
  2048. while (psess->pArgv[psess->cArgv] != NULL) {
  2049. psess->pArgv[psess->cArgv] += (LONG_PTR) psess->pArgv;
  2050. (psess->cArgv)++;
  2051. }
  2052. }
  2053. }
  2054. break;
  2055. }
  2056. }
  2057. if ((ish.PointerToRawData != 0) &&
  2058. (ish.SizeOfRawData != 0) &&
  2059. (cbFromHeader < (long)
  2060. (ish.PointerToRawData + ish.SizeOfRawData))) {
  2061. cbFromHeader = (long)
  2062. (ish.PointerToRawData + ish.SizeOfRawData);
  2063. }
  2064. if (cbDebug != 0)
  2065. {
  2066. if ((ish.VirtualAddress <=
  2067. inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) &&
  2068. ((ish.VirtualAddress + ish.SizeOfRawData) >
  2069. inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress)) {
  2070. offDebug = inh.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress
  2071. - ish.VirtualAddress + ish.PointerToRawData;
  2072. }
  2073. }
  2074. }
  2075. if (!psess->fSelfExtract && (offDebug != 0)) {
  2076. if (-1 == wrap_lseek(hf,offDebug,SEEK_SET)) {
  2077. goto Exit;
  2078. }
  2079. while (cbDebug >= sizeof(idd)) {
  2080. if (sizeof(idd) != wrap_read(hf,&idd,sizeof(idd))) {
  2081. goto Exit;
  2082. }
  2083. if ((idd.PointerToRawData != 0) &&
  2084. (idd.SizeOfData != 0) &&
  2085. (cbFromHeader < (long)
  2086. (idd.PointerToRawData + idd.SizeOfData))) {
  2087. cbFromHeader = (long)
  2088. (idd.PointerToRawData + idd.SizeOfData);
  2089. }
  2090. cbDebug -= sizeof(idd);
  2091. }
  2092. }
  2093. #endif // !BIT16
  2094. if (!psess->fSelfExtract)
  2095. {
  2096. //** Get actual file size
  2097. cbFile = wrap_lseek(hf,0,SEEK_END);
  2098. //** Modify state IF we are doing self-extract
  2099. if (cbFile > cbFromHeader) {
  2100. if ((cbFromHeader == wrap_lseek(hf,cbFromHeader,SEEK_SET)) &&
  2101. (sizeof(signature) == wrap_read(hf,&signature,sizeof(signature))) &&
  2102. (signature == 0x4643534DLU)) {
  2103. psess->fSelfExtract = TRUE;
  2104. psess->cbSelfExtract = cbFromHeader;
  2105. psess->cbSelfExtractSize = cbFile - cbFromHeader;
  2106. }
  2107. else if ((cbFromHeader % alignment) != 0) {
  2108. cbFromHeader += (alignment - 1);
  2109. cbFromHeader -= (cbFromHeader % alignment);
  2110. if ((cbFromHeader == wrap_lseek(hf,cbFromHeader,SEEK_SET)) &&
  2111. (sizeof(signature) == wrap_read(hf,&signature,sizeof(signature))) &&
  2112. (signature == 0x4643534DLU)) {
  2113. psess->fSelfExtract = TRUE;
  2114. psess->cbSelfExtract = cbFromHeader;
  2115. psess->cbSelfExtractSize = cbFile - cbFromHeader;
  2116. }
  2117. }
  2118. }
  2119. }
  2120. if (psess->fSelfExtract)
  2121. {
  2122. //** Save our file name and use it as the cabinet file name
  2123. #ifdef BIT16
  2124. strcpy(psess->achSelf,apszArg[0]); // Save our name
  2125. #else
  2126. strcpy(psess->achSelf,achFile); // Save our name
  2127. #endif
  2128. }
  2129. //** Success
  2130. Exit:
  2131. wrap_close(hf);
  2132. return TRUE;
  2133. } /* checkSelfExtractingCab() */
  2134. /*** addFileSpec - Add filename to session list
  2135. *
  2136. * Entry:
  2137. * psess - Session to update
  2138. * pszArg - File name to add
  2139. * perr - ERROR structure
  2140. *
  2141. * Exit-Success:
  2142. * Returns HFILESPEC, psess updated.
  2143. *
  2144. * Exit-Failure:
  2145. * Returns NULL, perr filled in with error.
  2146. */
  2147. HFILESPEC addFileSpec(PSESSION psess, char *pszArg, PERROR perr)
  2148. {
  2149. HFILESPEC hfspec;
  2150. AssertSess(psess);
  2151. //** Make sure a list exists
  2152. if (psess->hflist == NULL) {
  2153. if (!(psess->hflist = FLCreateList(perr))) {
  2154. return FALSE;
  2155. }
  2156. }
  2157. //** Add file to list
  2158. if (!(hfspec = FLAddFile(psess->hflist, pszArg, NULL, perr))) {
  2159. return NULL;
  2160. }
  2161. //** Success
  2162. return hfspec;
  2163. } /* addFileSpec() */
  2164. #ifdef ASSERT
  2165. /*** fnafReport - Report assertion failure
  2166. *
  2167. * NOTE: See asrt.h for entry/exit conditions.
  2168. */
  2169. FNASSERTFAILURE(fnafReport)
  2170. {
  2171. printf("\n%s:(%d) Assertion Failed: %s\n",pszFile,iLine,pszMsg);
  2172. exit(1);
  2173. }
  2174. #endif // ASSERT
  2175. /*** printError - Display error on stdout
  2176. *
  2177. * Entry
  2178. * perr - ERROR structure to print
  2179. *
  2180. * Exit-Success
  2181. * Writes error message to stdout.
  2182. */
  2183. void printError(PSESSION psess, PERROR perr)
  2184. {
  2185. WCHAR szTemp[MAX_MESSAGE_SIZE];
  2186. //** Make sure error starts on a new line
  2187. if (psess)
  2188. {
  2189. if (psess->fNoLineFeed) {
  2190. fwprintf(stdout, L"\n");
  2191. psess->fNoLineFeed = FALSE;
  2192. }
  2193. }
  2194. //** General error
  2195. Assert(perr->pszFile == NULL);
  2196. MsgSet(psess->achMsg,pszEXTERR_ERROR,"%s",perr->ach);
  2197. MultiByteToWideChar( CP_THREAD_ACP, 0, psess->achMsg, strlen(psess->achMsg),
  2198. szTemp, MAX_MESSAGE_SIZE);
  2199. *(szTemp+strlen(psess->achMsg)) = '\0';
  2200. fwprintf( stderr, szTemp );
  2201. } /* printError() */
  2202. /*** pszFromMSDOSTime - Convert MS-DOS file date/time to string
  2203. *
  2204. * Entry:
  2205. * psz - Buffer to receive formatted date/time
  2206. * cb - Length of psz buffer
  2207. * date - MS-DOS FAT file system date format (see below)
  2208. * time - MS-DOS FAT file system time format (see below)
  2209. *
  2210. * Exit:
  2211. * *psz filled in
  2212. *
  2213. * NOTE: This is the interpretation of the MS-DOS date/time values:
  2214. *
  2215. * Time Bits cBits Meaning
  2216. * --------- ----- ----------------------------------------
  2217. * 0 - 4 5 Number of two-second increments (0 - 29)
  2218. * 5 - 10 6 Minutes (0 - 59)
  2219. * 11 - 15 5 Hours (0 - 23)
  2220. *
  2221. * Date Bits cBits Meaning
  2222. * --------- ----- ----------------------------------------
  2223. * 0 - 4 5 Day (1 - 31)
  2224. * 5 - 8 4 Month (1 - 12)
  2225. * 9 - 15 7 Year since 1980 (for example, 1994 is stored as 14)
  2226. */
  2227. void pszFromMSDOSTime(char *psz, int cb, WORD date, WORD time)
  2228. {
  2229. int sec;
  2230. int min;
  2231. int hour;
  2232. int day;
  2233. int month;
  2234. int year;
  2235. char *pszAMPM; // AM/PM string
  2236. sec = (time & 0x1f) << 1; // 0, 2, ..., 58
  2237. min = (time >> 5) & 0x3f; // 0, 1, ..., 59
  2238. hour = (time >> 11) & 0x1f; // 0, 1, ..., 23
  2239. //** Determine 12-hour vs. 24-hour clock
  2240. if (strlen(pszEXT_TIME_PM) == 0) {
  2241. //** 24 hour clock
  2242. Assert(strlen(pszEXT_TIME_AM) == 0);
  2243. pszAMPM = pszEXT_TIME_PM;
  2244. }
  2245. else {
  2246. //** Get am/pm extension, and map 0 to 12
  2247. if (hour >= 12) {
  2248. pszAMPM = pszEXT_TIME_PM;
  2249. hour -= 12;
  2250. }
  2251. else {
  2252. pszAMPM = pszEXT_TIME_AM;
  2253. }
  2254. if (hour == 0) {
  2255. hour = 12;
  2256. }
  2257. }
  2258. day = (date & 0x1f);
  2259. month = (date >> 5) & 0x0f;
  2260. year = ((date >> 9) & 0x7f) + 1980;
  2261. MsgSet(psz,pszEXT_DATE_TIME, "%02d%02d%02d%2d%02d%02d%s",
  2262. month, day, year, hour, min, sec, pszAMPM);
  2263. } /* pszFromMSDOSTime() */
  2264. /*** pszFromAttrFAT - Convert FAT file attributes to string
  2265. *
  2266. * Entry:
  2267. * attrFAT - file attributes
  2268. *
  2269. * Exit:
  2270. * *psz filled in "----".."A-R-".."AHRS"
  2271. */
  2272. void pszFromAttrFAT(char *psz, int cb, WORD attrFAT)
  2273. {
  2274. STRCPY(psz,"----");
  2275. if (attrFAT & _A_ARCH)
  2276. psz[0] = 'A';
  2277. if (attrFAT & _A_HIDDEN)
  2278. psz[1] = 'H';
  2279. if (attrFAT & _A_RDONLY)
  2280. psz[2] = 'R';
  2281. if (attrFAT & _A_SYSTEM)
  2282. psz[3] = 'S';
  2283. return;
  2284. } /* pszFromAttrFAT() */
  2285. /*** mapFDIError - Create error message from FDI error codes
  2286. *
  2287. * Entry:
  2288. * perr - ERROR structure to recieve message
  2289. * psess - Our context
  2290. * pszCabinet - Cabinet file being processed
  2291. * perf - FDI error structure
  2292. *
  2293. * Exit:
  2294. * perr filled in with formatted message
  2295. */
  2296. void mapFDIError(PERROR perr,PSESSION psess, char *pszCabinet, PERF perf)
  2297. {
  2298. switch (perf->erfOper) {
  2299. case FDIERROR_NONE:
  2300. Assert(0);
  2301. break;
  2302. case FDIERROR_CABINET_NOT_FOUND:
  2303. ErrSet(perr,pszFDIERR_CAB_NOT_FOUND,"%s",pszCabinet);
  2304. break;
  2305. case FDIERROR_NOT_A_CABINET:
  2306. ErrSet(perr,pszFDIERR_NOT_A_CABINET,"%s",pszCabinet);
  2307. break;
  2308. case FDIERROR_UNKNOWN_CABINET_VERSION:
  2309. ErrSet(perr,pszFDIERR_BAD_CAB_VER,"%s%04x",pszCabinet,perf->erfType);
  2310. break;
  2311. case FDIERROR_CORRUPT_CABINET:
  2312. ErrSet(perr,pszFDIERR_CORRUPT_CAB,"%s",pszCabinet);
  2313. break;
  2314. case FDIERROR_ALLOC_FAIL:
  2315. ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet);
  2316. break;
  2317. case FDIERROR_BAD_COMPR_TYPE:
  2318. ErrSet(perr,pszFDIERR_BAD_COMPR_TYPE,"%s",pszCabinet);
  2319. break;
  2320. case FDIERROR_MDI_FAIL:
  2321. //** Improve detail of failure message
  2322. switch (psess->se) {
  2323. case seNONE:
  2324. //** Some other decompression error (corrupted data?)
  2325. ErrSet(perr,pszFDIERR_MDI_FAIL,"%s",pszCabinet);
  2326. break;
  2327. case seNOT_ENOUGH_MEMORY:
  2328. //** Not enough RAM for decompressor itself
  2329. ErrSet(perr,pszFDIERR_ALLOC_FAIL,"%s",pszCabinet);
  2330. break;
  2331. case seCANNOT_CREATE:
  2332. //** Could not create a Quantum temporary spill file
  2333. ErrSet(perr,pszFDIERR_SPILL_CREATE,"%s%s",pszCabinet,achSpillFile);
  2334. break;
  2335. case seNOT_ENOUGH_SPACE:
  2336. //** TMP directory did not have enough space for Quantum
  2337. // spill file.
  2338. ErrSet(perr,pszFDIERR_SPILL_SIZE,"%s%s%ld",pszCabinet,
  2339. achSpillFile,
  2340. psess->cbSpill);
  2341. break;
  2342. default:
  2343. Assert(0);
  2344. }
  2345. break;
  2346. case FDIERROR_TARGET_FILE:
  2347. ErrSet(perr,pszFDIERR_TARGET_FILE,"%s%s",psess->achFile,pszCabinet);
  2348. break;
  2349. case FDIERROR_RESERVE_MISMATCH:
  2350. ErrSet(perr,pszFDIERR_RESERVE_MISMATCH,"%s",pszCabinet);
  2351. break;
  2352. case FDIERROR_WRONG_CABINET:
  2353. ErrSet(perr,pszFDIERR_WRONG_CABINET,"%s",pszCabinet);
  2354. break;
  2355. case FDIERROR_USER_ABORT:
  2356. ErrSet(perr,pszFDIERR_USER_ABORT,"%s",pszCabinet);
  2357. break;
  2358. default:
  2359. ErrSet(perr,pszFDIERR_UNKNOWN_ERROR,"%d%s",perf->erfOper,pszCabinet);
  2360. break;
  2361. }
  2362. } /* mapFDIError() */
  2363. /*** wrap_close - close an open file
  2364. *
  2365. */
  2366. int FAR DIAMONDAPI wrap_close(INT_PTR fh)
  2367. {
  2368. int i;
  2369. int rc;
  2370. #ifdef SMALL_DOS
  2371. rc = _dos_close((HFILE)fh);
  2372. if (rc != 0) { // Map random MS-DOS error code to -1 failure
  2373. rc = -1;
  2374. }
  2375. #else
  2376. rc = _close((HFILE)fh);
  2377. #endif
  2378. //** See if we have to destroy the spill file
  2379. if (fh == hfSpillFile) {
  2380. _unlink(achSpillFile); // Delete spill file
  2381. hfSpillFile = -1; // Remember spill file is gone
  2382. }
  2383. //** Take handle off list if we are self-extracting
  2384. if (psessG->fSelfExtract) {
  2385. //** See if this is a handle to our EXE/cabinet file;
  2386. for (i=0;
  2387. (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != (HFILE)fh);
  2388. i++) { ; }
  2389. if (i < cMAX_CAB_FILE_OPEN) { // Found a match
  2390. psessG->ahfSelf[i] = -1; // Take it off our list
  2391. dbg( printf("\nDBG: Close self as handle %d (slot %d)\n",(HFILE)fh,i) );
  2392. }
  2393. }
  2394. #ifdef WIN32GUI
  2395. if (fh == hCabFile1) {
  2396. hCabFile1 = hCabFile2;
  2397. ibCabFilePosition1 = ibCabFilePosition2;
  2398. hCabFile2 = -1;
  2399. if (hCabFile1 == -1) {
  2400. cbCabFileTotal = 0;
  2401. cbCabFileMax = 0;
  2402. iPercentLast = -1;
  2403. }
  2404. }
  2405. else if (fh == hCabFile2) {
  2406. hCabFile2 = -1;
  2407. }
  2408. #endif
  2409. //** Done
  2410. dbg( printf("DBG: %d=CLOSE on handle %d\n",rc,(HFILE)fh) );
  2411. return rc;
  2412. } /* wrap_close */
  2413. /*** wrap_lseek - seek on a file
  2414. *
  2415. */
  2416. long FAR DIAMONDAPI wrap_lseek(INT_PTR fh, long pos, int func)
  2417. {
  2418. long cbAdjust=0; // Assume file is 0-based
  2419. int i;
  2420. long rc;
  2421. long cbLimit=0; // assume no length clipping
  2422. //** See if we are self-extracting
  2423. if (psessG->fSelfExtract) {
  2424. //** See if this is a handle to our EXE/cabinet file;
  2425. for (i=0;
  2426. (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != (HFILE)fh);
  2427. i++) { ; }
  2428. if (i < cMAX_CAB_FILE_OPEN) { // Found a match
  2429. cbAdjust = psessG->cbSelfExtract; // So return value gets adjusted
  2430. cbLimit = psessG->cbSelfExtractSize; // known CAB image size
  2431. if (func == SEEK_SET) { // Need to adjust absolute position
  2432. pos += cbAdjust; // Shift up to account for EXE
  2433. dbg(printf("\nDBG: Seek self to %ld as handle %d (slot %d)\n",pos,(HFILE)fh,i));
  2434. }
  2435. }
  2436. }
  2437. #ifdef SMALL_DOS
  2438. rc = _dos_seek((HFILE)fh,pos,func);
  2439. #else
  2440. rc = _lseek((HFILE)fh,pos,func);
  2441. #endif
  2442. //** If seek didn't fail, adjust return value for self-extract case
  2443. if (rc != -1) {
  2444. rc -= cbAdjust;
  2445. if ((cbLimit != 0) && (rc > cbLimit)) {
  2446. rc = cbLimit;
  2447. }
  2448. }
  2449. #ifdef WIN32GUI
  2450. if (fh == hCabFile1) {
  2451. ibCabFilePosition1 = rc;
  2452. }
  2453. else if (fh == hCabFile2) {
  2454. ibCabFilePosition2 = rc;
  2455. }
  2456. #endif
  2457. dbg( printf("DBG: %ld=LSEEK on handle %d, pos=%ld, func=%d\n",rc,(HFILE)fh,pos,func) );
  2458. return rc;
  2459. } /* wrap_lseek() */
  2460. /*** wrap_open - open a file
  2461. *
  2462. */
  2463. INT_PTR FAR DIAMONDAPI wrap_open(char FAR *sz, int mode, int share)
  2464. {
  2465. int i;
  2466. int rc;
  2467. char FAR *psz;
  2468. PFDISPILLFILE pfdisf; // FDI spill file info
  2469. #ifdef SMALL_DOS
  2470. int ignore;
  2471. char szLocal[cbFILE_NAME_MAX];
  2472. #endif
  2473. //** See if FDI is asking for a spill file (for Quantum)
  2474. if (*sz == '*') { // Yes, we need to create a spill file
  2475. Assert(hfSpillFile == -1); // Only support one at a time
  2476. achSpillFile[0] = '\0'; // No name constructed, yet
  2477. pfdisf = (PFDISPILLFILE)sz; // Get pointer to spill file size
  2478. //** Try boot drive if no TEMP variable defined
  2479. // NOTE: We assume the boot drive is more likely to be writeable
  2480. // than the current directory, since the customer may be
  2481. // running EXTRACT.EXE off a write-protected floppy (which
  2482. // also wouldn't have enough space), or a read-only network
  2483. // drive.
  2484. psz = _tempnam(getBootDrive(),"esf"); // Get a temporary file name
  2485. if (psz == NULL) {
  2486. psessG->se = seCANNOT_CREATE;
  2487. return -1; // Could not create
  2488. }
  2489. strcpy(achSpillFile,psz); // Remember name for wrap_close
  2490. free(psz); // Free temporary name buffer
  2491. mode = _O_CREAT | _O_BINARY | _O_RDWR; // Force open mode
  2492. psz = achSpillFile; // Use spill file name
  2493. }
  2494. else {
  2495. psz = (char FAR *)sz; // Use passed-in name
  2496. }
  2497. //** Open/create file
  2498. #ifdef SMALL_DOS
  2499. _fstrcpy(szLocal,psz);
  2500. if (mode & _O_CREAT) {
  2501. ignore = _dos_creat(szLocal,_A_NORMAL,&rc);
  2502. }
  2503. else {
  2504. //** Keep only relevant bits for _dos_open!
  2505. mode &= _O_RDONLY | _O_WRONLY | _O_RDWR;
  2506. ignore = _dos_open(szLocal,mode,&rc);
  2507. }
  2508. if (ignore != 0) {
  2509. rc = -1;
  2510. }
  2511. #else
  2512. rc = _open(psz,mode,share);
  2513. #endif
  2514. //** If this is the spill file, make sure the file was created,
  2515. // make sure it is the requested size, and save the handle.
  2516. // If we cannot do this, we set a flag to remember what the
  2517. // problem was, so that we can report the error intelligently.
  2518. if (*sz == '*') { // Need to size spill file
  2519. if (-1 == rc) { // Could not create it
  2520. psessG->se = seCANNOT_CREATE;
  2521. return (INT_PTR)rc;
  2522. }
  2523. //** Remember file handle, so that wrap_close can do the delete
  2524. hfSpillFile = rc;
  2525. //** Don't need to seek/write if zero length requested
  2526. if (pfdisf->cbFile > 0) {
  2527. //** Seek to size minus 1
  2528. if (-1L == wrap_lseek(rc,pfdisf->cbFile-1,SEEK_SET)) {
  2529. psessG->se = seNOT_ENOUGH_SPACE;
  2530. psessG->cbSpill = pfdisf->cbFile;
  2531. wrap_close(rc); // Close and destroy spill file
  2532. return -1;
  2533. }
  2534. //** Write one byte
  2535. if (1 != wrap_write(rc,"b",1)) {
  2536. psessG->se = seNOT_ENOUGH_SPACE;
  2537. psessG->cbSpill = pfdisf->cbFile;
  2538. wrap_close(rc);
  2539. return -1;
  2540. }
  2541. }
  2542. //** Spill file created successfully
  2543. psessG->se = seNONE; // No error
  2544. }
  2545. #ifndef BIT16
  2546. #define _fstricmp(a,b) stricmp(a,b)
  2547. #endif
  2548. else if (psessG->fSelfExtract && !_fstricmp(sz,psessG->achSelf)) {
  2549. //** Self-extracting and this is our EXE/cabinet file;
  2550. // Find a slot to store the file handle.
  2551. for (i=0;
  2552. (i<cMAX_CAB_FILE_OPEN) && (psessG->ahfSelf[i] != -1);
  2553. i++) { ; }
  2554. if (i >= cMAX_CAB_FILE_OPEN) {
  2555. Assert(0);
  2556. wrap_close(rc);
  2557. return -1;
  2558. }
  2559. dbg( printf("\nDBG: Opened self (%s) as handle %d (slot %d)\n",sz,rc,i) );
  2560. //** Save the new handle
  2561. psessG->ahfSelf[i] = rc;
  2562. //** Position the file handle to the start of the cabinet file header!
  2563. // NOTE: Since we just added the handle to the list, wrap_lseek()
  2564. // will know to do the EXE size adjustment!
  2565. wrap_lseek(rc,0,SEEK_SET);
  2566. }
  2567. #ifdef WIN32GUI
  2568. if ((mode == (_O_RDONLY|_O_BINARY)) && (rc != -1)) {
  2569. //* If this is the CAB file, track it's handle and get the file's size
  2570. // REARCHITECT: this is done this way in the interest of development time.
  2571. // I'm using _O_RDONLY to identify that it's the CAB file being
  2572. // opened, and I'm depending upon there being no more than two such
  2573. // handles.
  2574. if (hCabFile1 == -1) {
  2575. hCabFile1 = rc;
  2576. if (psessG->fSelfExtract)
  2577. {
  2578. cbCabFileTotal = psessG->cbSelfExtractSize;
  2579. }
  2580. else
  2581. {
  2582. cbCabFileTotal = _filelength((HFILE)hCabFile1);
  2583. }
  2584. cbCabFileMax = 0;
  2585. ibCabFilePosition1 = 0;
  2586. cbCabFileScale = 1;
  2587. while (cbCabFileTotal > 10000000)
  2588. {
  2589. cbCabFileTotal /= 10;
  2590. cbCabFileScale *= 10;
  2591. }
  2592. iPercentLast = -1;
  2593. if (g_hwndProgress == NULL)
  2594. {
  2595. g_hwndProgress = CreateDialog(g_hinst,
  2596. MAKEINTRESOURCE(DLG_PROGRESS), NULL, ProgressWndProc);
  2597. }
  2598. }
  2599. else if (hCabFile2 == -1) {
  2600. hCabFile2 = rc;
  2601. ibCabFilePosition2 = 0;
  2602. }
  2603. }
  2604. #endif
  2605. //** Done
  2606. dbg( printf("DBG: %d=OPEN file %s, mode=%d, share=%d\n",rc,sz,mode,share) );
  2607. return (INT_PTR)rc;
  2608. } /* wrap_open() */
  2609. /*** wrap_read - read a file
  2610. *
  2611. */
  2612. UINT FAR DIAMONDAPI wrap_read(INT_PTR fh, void FAR *pb, unsigned int cb)
  2613. {
  2614. int rc;
  2615. #ifdef SMALL_DOS
  2616. UINT ignore;
  2617. ignore = _dos_read((HFILE)fh,pb,cb,&rc);
  2618. if (ignore != 0) {
  2619. rc = -1;
  2620. }
  2621. #else
  2622. rc = _read((HFILE)fh,pb,cb);
  2623. #endif
  2624. #ifdef WIN32GUI
  2625. if (fh == hCabFile1) {
  2626. ibCabFilePosition1 += rc;
  2627. if (ibCabFilePosition1 > cbCabFileMax) {
  2628. cbCabFileMax = ibCabFilePosition1;
  2629. ProgressReport(cbCabFileMax);
  2630. }
  2631. }
  2632. else if (fh == hCabFile2) {
  2633. ibCabFilePosition2 += rc;
  2634. if (ibCabFilePosition2 > cbCabFileMax) {
  2635. cbCabFileMax = ibCabFilePosition2;
  2636. ProgressReport(cbCabFileMax);
  2637. }
  2638. }
  2639. #endif
  2640. dbg( printf("DBG: %d=READ on handle %d, pb=%08lx, cb=%u\n",rc,(HFILE)fh,pb,cb) );
  2641. return rc;
  2642. } /* wrap_read() */
  2643. /*** wrap_write - write a file
  2644. *
  2645. * Iff cb == 0, truncate the file at the current position.
  2646. * (Needed for FDITruncateCabinet.)
  2647. */
  2648. UINT FAR DIAMONDAPI wrap_write(INT_PTR fh, void FAR *pb, unsigned int cb)
  2649. {
  2650. int rc;
  2651. #ifdef SMALL_DOS
  2652. UINT ignore;
  2653. ignore = _dos_write((HFILE)fh,pb,cb,&rc);
  2654. if (ignore != 0) {
  2655. rc = -1;
  2656. }
  2657. #else
  2658. if (cb == 0) {
  2659. rc = _chsize((HFILE)fh,_lseek((HFILE)fh,0,SEEK_CUR));
  2660. }
  2661. else {
  2662. rc = _write((HFILE)fh,pb,cb);
  2663. }
  2664. #endif
  2665. dbg( printf("DBG: %d=WRITE on handle %d, pb=%08lx, cb=%u\n",rc,(HFILE)fh,pb,cb) );
  2666. return rc;
  2667. } /* wrap_write() */
  2668. /*** getBootDrive - Returns boot drive path (e.g., "C:\")
  2669. *
  2670. * Entry:
  2671. * none
  2672. *
  2673. * Exit:
  2674. * Returns pointer to static buffer with bootdrive ("C:\")
  2675. */
  2676. char *getBootDrive(void)
  2677. {
  2678. char ch;
  2679. char *psz;
  2680. static char szBootDrive[]="C:\\";
  2681. //** Default to Drive C
  2682. *szBootDrive = 'C';
  2683. //** Get COMSPEC -- we're assuming it's drive letter is the boot drive!
  2684. psz = getenv("COMSPEC");
  2685. if ( psz && // COMSPEC exists
  2686. *psz && // It is not empty
  2687. (*(psz+1) == ':')) { // Has the right format -- "?:..."
  2688. //** We could try to validate that this is really the boot drive,
  2689. // but we'll just trust that COMSPEC is correct. A test for the
  2690. // drive being in the range A..C would work for the US, but in
  2691. // Japan the boot drive can be anything between A..G, and maybe
  2692. // even higher. So, we'll just make sure it's a drive letter.
  2693. ch = (char)tolower(*psz);
  2694. if (('a' <= ch) && (ch <= 'z')) {
  2695. *szBootDrive = ch; // Use COMSPEC drive letter
  2696. }
  2697. }
  2698. //** Return path of root of boot drive
  2699. return szBootDrive;
  2700. } /* getBootDrive() */
  2701. #ifdef BIT16
  2702. //** Get Changeline fix code
  2703. //APPCOMPAT: 14-Dec-1994 bens Include *.c file to avoid makefile change!
  2704. #include "fixchg.c"
  2705. #endif
  2706. #ifdef WIN32GUI
  2707. int WINAPI WinMain(
  2708. HINSTANCE hInstance,
  2709. HINSTANCE hPrevInstance,
  2710. LPSTR lpCmdLine,
  2711. int nShowCmd)
  2712. {
  2713. int result;
  2714. char *pchCommand;
  2715. char *argv[50];
  2716. int argc;
  2717. enum { WHITESPACE, UNQUOTED, QUOTED } eState = WHITESPACE;
  2718. g_hinst = hInstance;
  2719. pchCommand = strdup(lpCmdLine); /* work on a copy */
  2720. argv[0] = ""; /* no EXE name supplied */
  2721. argc = 1; /* that was one */
  2722. if (pchCommand)
  2723. {
  2724. while (*pchCommand) /* walk the string */
  2725. {
  2726. switch (eState)
  2727. {
  2728. case WHITESPACE:
  2729. if (*pchCommand <= ' ')
  2730. {
  2731. /* ignore it */
  2732. }
  2733. else if (*pchCommand == '\"')
  2734. {
  2735. argv[argc++] = pchCommand + 1; /* skip quote */
  2736. eState = QUOTED;
  2737. }
  2738. else
  2739. {
  2740. argv[argc++] = pchCommand;
  2741. eState = UNQUOTED;
  2742. }
  2743. break;
  2744. case UNQUOTED:
  2745. if (*pchCommand <= ' ')
  2746. {
  2747. *pchCommand = '\0'; /* nul-terminate */
  2748. eState = WHITESPACE;
  2749. }
  2750. else
  2751. {
  2752. /* keep moving up */
  2753. }
  2754. break;
  2755. case QUOTED:
  2756. if (*pchCommand == '\"')
  2757. {
  2758. *pchCommand = '\0'; /* turn quote to a nul */
  2759. eState = WHITESPACE;
  2760. }
  2761. else
  2762. {
  2763. /* keep moving up */
  2764. }
  2765. break;
  2766. }
  2767. pchCommand++;
  2768. }
  2769. }
  2770. argv[argc] = NULL; /* NULL-terminate the list */
  2771. InitCommonControls();
  2772. result = main(argc, argv); /* run Extract */
  2773. if (g_hwndProgress != NULL)
  2774. {
  2775. DestroyWindow(g_hwndProgress);
  2776. g_hwndProgress = NULL;
  2777. }
  2778. return(result); /* return the result */
  2779. }
  2780. static void ProgressReport(unsigned long cbCabFileMax)
  2781. {
  2782. MSG msg;
  2783. int iPercent;
  2784. if ((cbCabFileTotal > 0) && (g_hwndProgress != NULL))
  2785. {
  2786. cbCabFileMax /= cbCabFileScale;
  2787. if (cbCabFileMax > cbCabFileTotal)
  2788. {
  2789. cbCabFileMax = cbCabFileTotal;
  2790. }
  2791. cbCabFileMax *= 100;
  2792. iPercent = (int) (cbCabFileMax / cbCabFileTotal);
  2793. if (iPercent != iPercentLast)
  2794. {
  2795. SendDlgItemMessage(g_hwndProgress, IDC_PROGRESS, PBM_SETPOS,
  2796. iPercent, 0);
  2797. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  2798. {
  2799. DispatchMessage(&msg);
  2800. }
  2801. iPercentLast = iPercent;
  2802. }
  2803. }
  2804. }
  2805. LRESULT CALLBACK ProgressWndProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
  2806. {
  2807. switch (msg)
  2808. {
  2809. case WM_INITDIALOG:
  2810. SendDlgItemMessage(hdlg, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, 99));
  2811. EnableMenuItem(GetSystemMenu(hdlg, FALSE), SC_CLOSE, MF_BYCOMMAND|MF_DISABLED|MF_GRAYED);
  2812. return TRUE;
  2813. }
  2814. return 0;
  2815. }
  2816. #endif /* WIN32GUI */